[PoC] Reducing planning time when tables have many partitions

Started by Yuya Watarialmost 4 years ago143 messages
#1Yuya Watari
watari.yuya@gmail.com
5 attachment(s)

Hello,

I found a problem that planning takes too much time when the tables
have many child partitions. According to my observation, the planning
time increases in the order of O(n^2). Here, n is the number of child
partitions. I attached the patch to solve this problem. Please be
noted that this patch is a PoC.

1. Problem Statement

The problem arises in the next simple query. This query is modeled
after a university's grade system, joining tables about students,
scores, and their GPAs to output academic records for each student.

=====
SELECT students.name, gpas.gpa AS gpa, sum(scores.score) AS total_score
FROM students, scores, gpas
WHERE students.id = scores.student_id AND students.id = gpas.student_id
GROUP BY students.id, gpas.student_id;
=====

Here, since there are so many students enrolled in the university, we
will partition each table. If so, the planning time of the above query
increases very rapidly as the number of partitions increases.

I conducted an experiment by varying the number of partitions of three
tables (students, scores, and gpas) from 2 to 1024. The attached
figure illustrates the result. The blue line annotated with "master"
stands for the result on the master branch. Obviously, its
computational complexity is large.

I attached SQL files to this e-mail as "sample-queries.zip". You can
reproduce my experiment by the next steps:
=====
$ unzip sample-queries.zip
$ cd sample-queries
# Create tables and insert sample data ('n' denotes the number of partitions)
$ psql -f create-table-n.sql
# Measure planning time
$ psql -f query-n.sql
=====

2. Where is Slow?

In order to identify bottlenecks, I ran a performance profiler(perf).
The "perf-master.png" is a call graph of planning of query-1024.sql.

From this figure, it can be seen that "bms_equal" and "bms_is_subset"
take up most of the running time. Most of these functions are called
when enumerating EquivalenceMembers in EquivalenceClass. The
enumerations exist in src/backend/optimizer/path/equivclass.c and have
the following form.

=====
EquivalenceClass *ec = /* given */;

EquivalenceMember *em;
ListCell *lc;
foreach(lc, ec->ec_members)
{
em = (EquivalenceMember *) lfirst(lc);

/* predicate is bms_equal or bms_is_subset, etc */
if (!predicate(em))
continue;

/* The predicate satisfies */
do something...;
}
=====

This foreach loop is a linear search, whose cost will become very high
when there are many EquivalenceMembers in ec_members. This is the case
when the number of partitions is large. Eliminating this heavy linear
search is a key to improving planning performance.

3. How to Solve?

In my patch, I made three different optimizations depending on the
predicate pattern.

3.1 When the predicate is "!em->em_is_child"

In equivclass.c, there are several processes performed when
em_is_child is false. If a table has many partitions, the number of
EquivalenceMembers which are not children is limited. Therefore, it is
useful to keep only the non-child members as a list in advance.

My patch adds the "ec_not_child_members" field to EquivalenceClass.
This field is a List containing non-child members. Taking advantage of
this, the previous loop can be rewritten as follows:

=====
foreach(lc, ec->ec_not_child_members)
{
em = (EquivalenceMember *) lfirst(lc);
Assert(!em->em_is_child);
do something...;
}
=====

3.2 When the predicate is "bms_equal(em->em_relids, relids)"

"bms_equal" is another example of the predicate. In this case,
processes will be done when the "em_relids" matches certain Relids.

This type of loop can be quickly handled by utilizing a hash table.
First, group EquivalenceMembers with the same Relids into a list.
Then, create an associative array whose key is Relids and whose value
is the list. In my patch, I added the "ec_members_htab" field to
EquivalenceClass, which plays a role of an associative array.

Based on this idea, the previous loop is transformed as follows. Here,
the FindEcMembersMatchingRelids function looks up the hash table and
returns the corresponding value, which is a list.
=====
foreach(lc, FindEcMembersMatchingRelids(ec, relids))
{
em = (EquivalenceMember *) lfirst(lc);
Assert(bms_equal(em->em_relids, relids));
do something...;
}
=====

3.3 When the predicate is "bms_is_subset(em->em_relids, relids)"

There are several processings performed on EquivalenceMembers whose
em_relids is a subset of the given "relids". In this case, the
predicate is "bms_is_subset". Optimizing this search is not as easy as
with bms_equal, but the technique above by hash tables can be applied.

There are 2^m subsets if the number of elements of the "relids" is m.
The key here is that m is not so large in most cases. For example, m
is up to 3 in the sample query, meaning that the number of subsets is
at most 2^3=8. Therefore, we can enumerate all subsets within a
realistic time. Looking up the hash table with each subset as a key
will drastically reduce unnecessary searches. My patch's optimization
is based on this notion.

This technique can be illustrated as the next pseudo-code. The code
iterates over all subsets and looks up the corresponding
EquivalenceMembers from the hash table. The actual code is more
complicated for performance reasons.

===
EquivalenceClass *ec = /* given */;
Relids relids = /* given */;

int num_members_in_relids = bms_num_members(relids);
for (int bit = 0; bit < (1 << num_members_in_relids); bit++)
{
EquivalenceMember *em;
ListCell *lc;
Relids subset = construct subset from 'bit';

foreach(lc, FindEcMembersMatchingRelids(ec, subset))
{
em = (EquivalenceMember *) lfirst(lc);
Assert(bms_is_subset(em->em_relids, relids));
do something...;
}
}
===

4. Experimental Result

The red line in the attached figure is the planning time with my
patch. The chart indicates that planning performance has been greatly
improved. The exact values are shown below.

Planning time of "query-n.sql" (n = number of partitions):
n | Master (s) | Patched (s) | Speed up
------------------------------------------
2 | 0.003 | 0.003 | 0.9%
4 | 0.004 | 0.004 | 1.0%
8 | 0.006 | 0.006 | 4.6%
16 | 0.011 | 0.010 | 5.3%
32 | 0.017 | 0.016 | 4.7%
64 | 0.032 | 0.030 | 8.0%
128 | 0.073 | 0.060 | 17.7%
256 | 0.216 | 0.142 | 34.2%
384 | 0.504 | 0.272 | 46.1%
512 | 0.933 | 0.462 | 50.4%
640 | 1.529 | 0.678 | 55.7%
768 | 2.316 | 1.006 | 56.6%
896 | 3.280 | 1.363 | 58.5%
1024 | 4.599 | 1.770 | 61.5%

With 1024 partitions, the planning time was reduced by 61.5%. Besides,
with 128 partitions, which is a realistic use case, the performance
increased by 17.7%.

5. Things to Be Discussed

5.1 Regressions

While my approach is effective for tables with a large number of
partitions, it may cause performance degradation otherwise. For small
cases, it is necessary to switch to a conventional algorithm. However,
its threshold is not self-evident.

5.2 Enumeration order

My patch may change the order in which members are enumerated. This
affects generated plans.

5.3 Code Quality

Source code quality should be improved.

=====

Again, I posted this patch as a PoC. I would appreciate it if you
would discuss the effectiveness of these optimizations with me.

Best regards,
Yuya Watari

Attachments:

v1-reducing-planning-time-when-tables-have-many-partitions.patchapplication/octet-stream; name=v1-reducing-planning-time-when-tables-have-many-partitions.patchDownload
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6bdad462c7..178464615b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2479,6 +2479,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_members_htab);
+	WRITE_NODE_FIELD(ec_not_child_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8c6770de97..a1c6e48145 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,6 +31,144 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
+/*
+ * Constant, state structs and enum for looping macros below.
+ */
+#define MAX_NUM_SUBSETS_OF_RELIDS (1 << 16)
+
+typedef struct
+{
+	const List *list;
+	int			i;
+} EmForeachListState;
+
+typedef enum
+{
+	EM_FOREACH_NEXT_BIT,		/* Proceed to next subset */
+	EM_FOREACH_NEXT_LIST_ENTRY,	/* Proceed to next entry */
+	EM_FOREACH_LINEAR_SEARCH,	/* Standard linear search */
+	EM_FOREACH_FINISHED			/* Loop was done */
+} EmForeachBitStateStatus;
+
+typedef struct
+{
+	EmForeachBitStateStatus 	status;
+	EquivalenceClass		   *ec;
+	Relids						relids;
+	int32						bit;
+	int							num_members_in_relids;
+	int							all_members_in_relids[MAX_NUM_SUBSETS_OF_RELIDS];
+	Relids						subset;
+	List					   *list;
+	int							i;
+} EmForeachBitState;
+
+/*
+ * The following three em_foreach_* macros help enumerate
+ * EquivalenceClass::ec_members.
+ *
+ * See the comments below for further information.
+ */
+
+/*
+ * em_foreach_not_children
+ *
+ * Optimized version of
+ * =====
+ * ListCell   *lc;
+ * foreach(lc, ec->ec_members)
+ * {
+ *     EquivalenceMember *em = lfirst(lc);
+ *
+ *     if (em->em_is_child)
+ *         continue;
+ *
+ *     content of loop...
+ * }
+ * =====
+ *
+ * Usage:
+ * =====
+ * EquivalenceClass *ec = ...;
+ *
+ * EquivalenceMember *em;
+ * em_foreach_not_children(em, ec)
+ * {
+ *     content of loop...
+ * }
+ * =====
+ */
+#define em_foreach_not_children(em, ec)	\
+	for (EmForeachListState em##__state = { (ec)->ec_not_child_members, -1 };	\
+		 em_foreach_list_move_next(&em##__state, &em); )
+
+/*
+ * em_foreach_relids_equals
+ *
+ * Optimized version of
+ * =====
+ * ListCell   *lc;
+ * foreach(lc, ec->ec_members)
+ * {
+ *     EquivalenceMember *em = lfirst(lc);
+ *
+ *     if (!bms_equal(em->em_relids, relids))
+ *         continue;
+ *
+ *     content of loop...
+ * }
+ * =====
+ *
+ * Usage:
+ * =====
+ * EquivalenceClass *ec = ...;
+ * Relids relids = ...;
+ *
+ * EquivalenceMember *em;
+ * em_foreach_relids_equals(em, ec)
+ * {
+ *     content of loop...
+ * }
+ * =====
+ */
+#define em_foreach_relids_equals(em, ec, relids)	\
+	for (EmForeachListState em##__state =	\
+		 { FindEcMembersMatchingRelids(ec, relids), -1 };	\
+		 em_foreach_list_move_next(&em##__state, &em); )
+
+/*
+ * em_foreach_relids_subset
+ *
+ * Optimized version of
+ * =====
+ * ListCell   *lc;
+ * foreach(lc, ec->ec_members)
+ * {
+ *     EquivalenceMember *em = lfirst(lc);
+ *
+ *     if (!bms_is_subset(em->em_relids, relids))
+ *         continue;
+ *
+ *     content of loop...
+ * }
+ * =====
+ *
+ * Usage:
+ * =====
+ * EquivalenceClass *ec = ...;
+ * Relids relids = ...;
+ *
+ * EmForeachBitState state;
+ * EquivalenceMember *em;
+ * initialize_EmForeachBitState(&state, ec, relids);
+ * em_foreach_relids_subset(em, ec)
+ * {
+ *     content of loop...
+ * }
+ * =====
+ */
+#define em_foreach_relids_subset(state, em)	\
+	while (em_foreach_bit_move_next(&state, &em))
 
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids, Relids nullable_relids,
@@ -69,6 +207,21 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
 											Relids relids2);
+static inline bool em_foreach_list_move_next(
+	EmForeachListState *state, EquivalenceMember **em);
+static inline void initialize_EmForeachBitState(
+	EmForeachBitState *state, EquivalenceClass *ec, Relids relids);
+static inline bool em_foreach_bit_move_next(
+	EmForeachBitState *state, EquivalenceMember **em);
+static void InitializeEcMembersHtab(EquivalenceClass *ec);
+static void AppendEcMember(EquivalenceClass *ec,
+						   EquivalenceMember *emem);
+static void ConcatEcMember(EquivalenceClass *ec1,
+						   EquivalenceClass *ec2);
+static void DeleteNthEcMember(EquivalenceClass *ec,
+							  int index);
+static List *FindEcMembersMatchingRelids(EquivalenceClass *ec,
+										 Relids relids);
 
 
 /*
@@ -364,7 +517,7 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ConcatEcMember(ec1, ec2);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -379,6 +532,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_members_htab = NULL;
+		ec2->ec_not_child_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -439,6 +594,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_members_htab = NULL;
+		ec->ec_not_child_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -573,7 +730,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
+	AppendEcMember(ec, em);
 
 	return em;
 }
@@ -645,7 +802,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMember *cur_em = NULL;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,17 +817,31 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * Ignore child members unless they match the request.
+		 */
+
+		em_foreach_not_children(cur_em, cur_ec)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
-			 * Ignore child members unless they match the request.
+			 * If below an outer join, don't match constants: they're not as
+			 * constant as they look.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
+			if (cur_ec->ec_below_outer_join &&
+				cur_em->em_is_const)
 				continue;
 
+			if (opcintype == cur_em->em_datatype &&
+				equal(expr, cur_em->em_expr))
+				return cur_ec;	/* Match! */
+		}
+
+		em_foreach_relids_equals(cur_em, cur_ec, rel)
+		{
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
+
 			/*
 			 * If below an outer join, don't match constants: they're not as
 			 * constant as they look.
@@ -700,6 +871,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_members_htab = NULL;
+	newec->ec_not_child_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -787,17 +960,23 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMember *em;
+	EmForeachBitState state;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * Ignore child members unless they belong to the requested rel.
+	 */
+
+	em_foreach_not_children(em, ec)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
+		Assert(!em->em_is_child || bms_is_subset(em->em_relids, relids));
+
 		/*
 		 * We shouldn't be trying to sort by an equivalence class that
 		 * contains a constant, so no need to consider such cases any further.
@@ -806,10 +985,28 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			continue;
 
 		/*
-		 * Ignore child members unless they belong to the requested rel.
+		 * Match if same expression (after stripping relabel).
 		 */
-		if (em->em_is_child &&
-			!bms_is_subset(em->em_relids, relids))
+		emexpr = em->em_expr;
+		while (emexpr && IsA(emexpr, RelabelType))
+			emexpr = ((RelabelType *) emexpr)->arg;
+
+		if (equal(emexpr, expr))
+			return em;
+	}
+
+	initialize_EmForeachBitState(&state, ec, relids);
+	em_foreach_relids_subset(state, em)
+	{
+		Expr	   *emexpr;
+
+		Assert(!em->em_is_child || bms_is_subset(em->em_relids, relids));
+
+		/*
+		 * We shouldn't be trying to sort by an equivalence class that
+		 * contains a constant, so no need to consider such cases any further.
+		 */
+		if (em->em_is_const)
 			continue;
 
 		/*
@@ -1578,6 +1775,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
 	ListCell   *lc1;
+	EquivalenceMember *cur_em;
+	EmForeachBitState state;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1588,17 +1787,16 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
 
-		/*
-		 * We don't need to check explicitly for child EC members.  This test
-		 * against join_relids will cause them to be ignored except when
-		 * considering a child inner rel, which is what we want.
-		 */
-		if (!bms_is_subset(cur_em->em_relids, join_relids))
-			continue;			/* not computable yet, or wrong child */
+	/*
+	 * We don't need to check explicitly for child EC members.  This test
+	 * against join_relids will cause them to be ignored except when
+	 * considering a child inner rel, which is what we want.
+	 */
+	initialize_EmForeachBitState(&state, ec, join_relids);
+	em_foreach_relids_subset(state, cur_em)
+	{
+		Assert(bms_is_subset(cur_em->em_relids, join_relids));
 
 		if (bms_is_subset(cur_em->em_relids, outer_relids))
 			outer_members = lappend(outer_members, cur_em);
@@ -2354,7 +2552,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			DeleteNthEcMember(cur_ec, coal_idx);
 			return true;
 		}
 
@@ -2875,7 +3073,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMember *other_em;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2898,11 +3096,10 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		em_foreach_relids_equals(cur_em, cur_ec, rel->relids)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
-			if (bms_equal(cur_em->em_relids, rel->relids) &&
-				callback(root, rel, cur_ec, cur_em, callback_arg))
+			Assert(bms_equal(cur_em->em_relids, rel->relids));
+			if (callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
@@ -2914,14 +3111,12 @@ 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)
+		em_foreach_not_children(other_em, cur_ec)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3253,3 +3448,407 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * em_foreach_list_move_next
+ *		Update EmForeachListState so that it points to the next
+ *		EquivalenceMember.
+ *		Return true if the next element was found with assigning it to *em.
+ */
+static inline bool em_foreach_list_move_next(
+	EmForeachListState *state, EquivalenceMember **em)
+{
+	int length = list_length(state->list);
+
+	while (++(state->i) < length)
+	{
+		EquivalenceMemberListEntry *listEntry
+			= (EquivalenceMemberListEntry *) list_nth(state->list, state->i);
+
+		if (!listEntry->deleted)
+		{
+			*em = listEntry->emem;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * Helper functions for the em_foreach_relids_subset macro.
+ *
+ * em_foreach_relids_subset macro is based on enumeration of all possible
+ * subsets of the given relids. For each subset, we look up the hash table in
+ * EquivalenceClass and iterate over the corresponding EquivalenceMembers.
+ *
+ * This technique can be written like the next pasudo code.
+ * =====
+ * int num_members_in_relids = bms_num_members(relids);
+ * if (num_members_in_relids < MAX_NUM_SUBSETS_OF_RELIDS)
+ * {
+ *     // We will use the special optimization technique
+ *     for (int bit = 0; bit < (1 << num_members_in_relids); bit++)
+ *     {
+ *         EquivalenceMember *em;
+ *         ListCell          *lc;
+ *         Relids             subset = construct subset from 'bit';
+ *
+ *         foreach(lc, FindEcMembersMatchingRelids(ec, subset))
+ *         {
+ *             em = (EquivalenceMember *) lfirst(lc);
+ *             content of loop...
+ *         }
+ *     }
+ * }
+ * else
+ * {
+ *     // There are too many subsets, so we will use simple linear search
+ *     ListCell *lc;
+ *     foreach(lc, ec->ec_members)
+ *     {
+ *         EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+ *         if (bms_is_subset(em->em_relids, relids))
+ *         {
+ *             em = (EquivalenceMember *) lfirst(lc);
+ *             content of loop...
+ *         }
+ *     }
+ * }
+ * =====
+ *
+ * EmForeachBitState and related functions provide the state machine for this
+ * algorithm.
+ */
+
+/*
+ * initialize_EmForeachBitState
+ *		Initialize the given EmForeachBitState.
+ *		This function must be called before using the
+ *		em_foreach_relids_subset macro.
+ */
+static inline void initialize_EmForeachBitState(
+	EmForeachBitState *state, EquivalenceClass *ec, Relids relids)
+{
+	const int num_ec_members = list_length(ec->ec_members);
+	int num_members_in_relids = 0;
+	int i;
+
+	state->ec = ec;
+	state->relids = relids;
+
+	/*
+	 * Enumerate all members in 'relids'
+	 */
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		++num_members_in_relids;
+
+		if (num_members_in_relids >= MAX_NUM_SUBSETS_OF_RELIDS
+			|| ((1 << num_members_in_relids) >= num_ec_members))
+		{
+			/*
+			 * There are too many subsets, so we will use simple linear search
+			 * instead of the special optimization technique.
+			 */
+			state->status = EM_FOREACH_LINEAR_SEARCH;
+			state->i = -1;
+			return;
+		}
+
+		state->all_members_in_relids[num_members_in_relids - 1] = i;
+	}
+
+	/*
+	 * Prepare for the special optimization
+	 */
+	state->status = EM_FOREACH_NEXT_BIT;
+	state->bit = -1;
+	state->num_members_in_relids = num_members_in_relids;
+	state->subset = NULL;
+	return;
+}
+
+/*
+ * em_foreach_bit_move_next
+ *		Update EmForeachBitState so that it points to the next
+ *		EquivalenceMember.
+ *		Return true if the next element was found with assigning it to *em.
+ */
+static inline bool em_foreach_bit_move_next(
+	EmForeachBitState *state, EquivalenceMember **em)
+{
+	switch (state->status)
+	{
+		case EM_FOREACH_NEXT_BIT:
+		CASE_EM_FOREACH_NEXT_BIT:
+		{
+			/*
+			 * We need to proceed the next subset.
+			 */
+			int i;
+			const int num_members_in_relids = state->num_members_in_relids;
+
+			if (++(state->bit) >= (1 << num_members_in_relids))
+			{
+				/* Reached end */
+				state->status = EM_FOREACH_FINISHED;
+				return false;
+			}
+
+			/* Clear Bitmapset */
+			if (state->subset != NULL)
+			{
+				for (i = 0; i < state->subset->nwords; i++)
+				{
+					state->subset->words[i] = 0;
+				}
+			}
+
+			/* Set flags */
+			for (i = 0; i < num_members_in_relids; i++)
+			{
+				if (state->bit & (1 << i))
+				{
+					state->subset =
+						bms_add_member(state->subset,
+									   state->all_members_in_relids[i]);
+				}
+			}
+
+			/* Update state and begin iteration on this subset.*/
+			state->status = EM_FOREACH_NEXT_LIST_ENTRY;
+			state->list = FindEcMembersMatchingRelids(
+				state->ec, state->subset);
+			state->i = -1;
+			goto CASE_EM_FOREACH_NEXT_LIST_ENTRY;
+		}
+		case EM_FOREACH_NEXT_LIST_ENTRY:
+		CASE_EM_FOREACH_NEXT_LIST_ENTRY:
+		{
+			/*
+			 * Iterate on the current subset.
+			 */
+			const List *list = state->list;
+			const int length = list_length(list);
+			int i = state->i;
+
+			while (++i < length)
+			{
+				EquivalenceMemberListEntry *listEntry
+					= (EquivalenceMemberListEntry *) list_nth(list, i);
+
+				if (!listEntry->deleted)
+				{
+					/* Found */
+					*em = listEntry->emem;
+					state->i = i;
+					return true;
+				}
+			}
+
+			/*
+			 * There are no more members in the current subset.
+			 * We need to proceed next one.
+			 */
+			state->status = EM_FOREACH_NEXT_BIT;
+			goto CASE_EM_FOREACH_NEXT_BIT;
+		}
+		case EM_FOREACH_LINEAR_SEARCH:
+		{
+			/*
+			 * Simple linear search.
+			 */
+			const List *ec_members = state->ec->ec_members;
+			const Relids relids = state->relids;
+			const int length = list_length(ec_members);
+			int i = state->i;
+
+			while (++i < length)
+			{
+				EquivalenceMember *member =
+					(EquivalenceMember *) list_nth(ec_members, i);
+
+				if (bms_is_subset(member->em_relids, relids))
+				{
+					/* Found */
+					*em = member;
+					state->i = i;
+					return true;
+				}
+			}
+
+			/*
+			 * Reached end
+			 */
+			state->status = EM_FOREACH_FINISHED;
+			return false;
+		}
+		case EM_FOREACH_FINISHED:
+		default:
+			break;
+	}
+
+	return false;
+}
+
+/*
+ * InitializeEcMembersHtab
+ *		Initialize a hash table in the given EquivalenceClass.
+ */
+static void InitializeEcMembersHtab(EquivalenceClass *ec)
+{
+	HASHCTL hash_ctl;
+
+	if (ec->ec_members_htab != NULL)
+	{
+		return;
+	}
+
+	hash_ctl.keysize = sizeof(Relids);
+	hash_ctl.entrysize = sizeof(EquivalenceMemberHashEntry);
+	hash_ctl.hash = bitmap_hash;
+	hash_ctl.match = bitmap_match;
+	hash_ctl.hcxt = CurrentMemoryContext;
+	ec->ec_members_htab =
+		hash_create("EquivalenceMemberHashTable",
+					 256L,
+					 &hash_ctl,
+					 HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
+}
+
+/*
+ * AppendEcMember
+ *		Append EquivalenceMember emem to the EquivalenceClass ec.
+ *		This function modifies ec->ec_members, ec->ec_not_child_members,
+ *		and ec->ec_members_htab.
+ */
+static void AppendEcMember(EquivalenceClass *ec,
+						   EquivalenceMember *emem)
+{
+	EquivalenceMemberHashEntry *hashEntry;
+	EquivalenceMemberListEntry *listEntry;
+	bool						found;
+
+	if (ec->ec_members_htab == NULL)
+	{
+		InitializeEcMembersHtab(ec);
+	}
+
+	listEntry = palloc(sizeof(EquivalenceMemberListEntry));
+	listEntry->emem = emem;
+	listEntry->deleted = false;
+
+	ec->ec_members = lappend(ec->ec_members, emem);
+	if (!emem->em_is_child)
+	{
+		ec->ec_not_child_members =
+			lappend(ec->ec_not_child_members, listEntry);
+	}
+
+	hashEntry = hash_search(ec->ec_members_htab, &(emem->em_relids),
+							HASH_ENTER, &found);
+	if (!found)
+	{
+		hashEntry->list = NIL;
+	}
+
+	hashEntry->list = lappend(hashEntry->list, listEntry);
+}
+
+/*
+ * ConcatEcMember
+ *		Append all EquivalenceMembers in ec2 to ec1 while updating as in
+ *		AppendEcMember.
+ */
+static void ConcatEcMember(EquivalenceClass *ec1,
+						   EquivalenceClass *ec2)
+{
+	ListCell *lc;
+
+	if (ec1->ec_members_htab == NULL)
+	{
+		InitializeEcMembersHtab(ec1);
+	}
+
+	foreach(lc, ec2->ec_members)
+	{
+		AppendEcMember(ec1, lfirst_node(EquivalenceMember, lc));
+	}
+}
+
+/*
+ * DeleteNthEcMember
+ *		Delete the EquivalenceMember whose index in ec->ec_members is the
+ *		'index' argument.
+ */
+static void DeleteNthEcMember(EquivalenceClass *ec,
+							  int index)
+{
+	EquivalenceMemberHashEntry *hashEntry;
+	EquivalenceMemberListEntry *listEntry;
+	bool						found;
+	EquivalenceMember		   *emem;
+	ListCell				   *lc;
+#ifdef USE_ASSERT_CHECKING
+	bool memberToBeDeletedWasFound = false;
+#endif
+
+	emem = list_nth(ec->ec_members, index);
+	ec->ec_members = list_delete_nth_cell(ec->ec_members, index);
+
+	Assert(ec->ec_members_htab != NULL);
+	hashEntry = hash_search(ec->ec_members_htab, &(emem->em_relids),
+							HASH_FIND, &found);
+	Assert(found);
+
+	/*
+	 * We mark the corresponding EquivalenceMemberListEntry deleted
+	 * instead of removing it from the list.
+	 */
+	foreach(lc, hashEntry->list)
+	{
+		listEntry = (EquivalenceMemberListEntry *) lfirst(lc);
+
+		if (listEntry->emem == emem)
+		{
+			listEntry->deleted = true;
+#ifdef USE_ASSERT_CHECKING
+			memberToBeDeletedWasFound = true;
+#endif
+			break;
+		}
+	}
+#ifdef USE_ASSERT_CHECKING
+	Assert(memberToBeDeletedWasFound);
+#endif
+}
+
+/*
+ * FindEcMembersMatchingRelids
+ *		Looks up the hash table in the EquivalenceClass and returns a list
+ *		containing EquivalenceMemberHashEntry whose em_relids is equal to the
+ *		'relids' argument. If there are no members satisfying the condition,
+ *		NIL will be returned.
+ *
+ *		Note: Please confirm the 'deleted' flag in EquivalenceMemberHashEntry.
+ *		Further information is available in EquivalenceMemberHashEntry's
+ *		comment.
+ */
+static List *FindEcMembersMatchingRelids(EquivalenceClass *ec,
+										 Relids relids)
+{
+	EquivalenceMemberHashEntry *entry;
+	bool						found;
+
+	if (ec->ec_members_htab == NULL)
+	{
+		return NIL;
+	}
+
+	entry = hash_search(ec->ec_members_htab, &relids,
+						HASH_FIND, &found);
+
+	return found ? entry->list : NIL;
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1f3845b3fe..5f5aa1b314 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -19,6 +19,7 @@
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "storage/block.h"
+#include "utils/hsearch.h"
 
 
 /*
@@ -988,6 +989,9 @@ typedef struct EquivalenceClass
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	HTAB	   *ec_members_htab;	/* Hash table for ec_members */
+	List	   *ec_not_child_members;	/* list of EquivalenceMembers
+										   which are not children */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1043,6 +1047,34 @@ typedef struct EquivalenceMember
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberHashEntry
+ *
+ * Key and value of EquivalenceMember::ec_members_htab.
+ * The 'list' field contains EquivalenceMemberListEntries whose em_relids is
+ * equal to the key.
+ */
+typedef struct EquivalenceMemberHashEntry
+{
+	Relids	em_relids;	/* hash key --- MUST BE FIRST */
+	List   *list;		/* List of EquivalenceMemberListEntry */
+} EquivalenceMemberHashEntry;
+
+/*
+ * EquivalenceMemberListEntry
+ *
+ * Entry of EquivalenceMemberHashEntry::list.
+ *
+ * Note: Before accessing emem field, confirm that the 'deleted' flag is false.
+ * If the value is true, the corresponding EquivalenceMember no longer exists
+ * in EquivalenceClass::ec_members.
+ */
+typedef struct EquivalenceMemberListEntry
+{
+	EquivalenceMember  *emem;
+	bool				deleted;
+} EquivalenceMemberListEntry;
+
 /*
  * PathKeys
  *
figure.pngimage/png; name=figure.pngDownload
�PNG


IHDR�8���CsRGB���gAMA���a��IDATx^��|T����!����%� � (�A�X0�J���V,���U����������l���X��G����5�rP��@��!��C��������L�I2������>��J2�u�u�k��
�;���0��8
�"(@��!�0�
�"(@��!�0�
�"(@��!�0��0��i�IXX�iP?���D��o��s�!�0�
�"(@���N�aaa�������N�\@0+��0�
�"(@��!�0�
�"(@��!�0�
�"(@��!�0�
�"(@��!�0�
�"(@��!�0�
��E�2��),�ST�z���Ac����������cV-s���UF��j�{�MU��I�S�u��ih�J�����?��|?�&�h�>X0^���(�c��t��^��6�k~���^���c���gj�k�*�{b�T��w�d�QG��+u�fg��
�
e3�Y������!U���y�S��f8�i�LM�9��-O�����A=�^vQq=�k���U������\���W����?�\��qL����~�0(g�������^����������m��'=���e���.T��EZ���B3��07KY����@�B�:���U�������g��<S����g���������c��m�Q�3��L~��6����f�` h�(��I�b�z#�"0�T�F���VzV}�%�R7�*S��\���JO�?j�@C��+��i^�G��[Ru�1m��0��i���{��=4P��������&T�R~�;%�c��f+�����g�K�4}�������9��u�M�!J���&&�~�^nS���y���y9�5~^�4av�������n~H7�0A��8U��t������?������7v��V�G�j"�h]y�C�0�
um���Mn�#7�p��R��s��������������g�l�h�;�$�E������K������e��g�Kd����c����9\ow��H�"U�9��^�g=�_��g����/���?S'�p������k�g�i�Fh�s�u�4e���SR�Mt�o~��?s���k���u����Q
��\���W����?�\��qpWT�3��Z��x��p>��4=���C<��!����G~*>���>6��1J�~��������������e]�dw !D�NbY�Cf�����S,��8����FU�@�ymSC|0�M�����S0����R�c��{�+�
�Gz<�s��s����O�����7��<�|��kY�N^�sTc���C<�r��������S<��!�������8&�����M��:\6C@7P��J�&��9��y�}T`�G���M�)Yw\�S&���E��Kr��M��!}��4	�$ X����g7h��!fF�3�����&�z�o���U��Q���i�Ix�Z�f�KtA��W(���@��+���*��DW}It�r�����3�W��\�,��/�0��Wb��/B-�M���.������<�T����e�Hd�4 ��&A����������u9�
������J�j����n}u��~.��:�=tbO]�Y���J�$�rk��.�DW���.e(�?\w�2m�3Z���� t��K=���]��V������.���ml���{��]���G\� t\�^��$�R7�����U���E�@=����DW]�����.e(A�����1+����H��R��+�W/u�	s�(�l����^��hf�J}�}����8o������f�C��_k���tQL����K�T�_�L���I�s�1���C3�W��
wiu�L��CqQe����4ff�V����[?,l����9���q~���`|�zu�q�NL�J��������V�]�34s� ���*��.��z����*,?Q��1�������&'CC�_�c�\q<dN��w���X��u������E_���3u��f}������,�{�hP�8E������w��}������Yof;�G��qnw����j_�u��n����V�z���<����&����i�wb|�?�`��f|/_�634��������}Z?�J���}�l�O��7���l����������
U���)o���Z�Ov>__�������1�~�����es|f�u.L7.	��V�������>/�~r�c�����6�Zm�O=���wiM���@��~	�6	�X*#_���YW��s�9���f�)*N=�>.�c�z}���Ao���������N�����N����^c�g.P������z�t���G�^�1�LGN��3��1q����+�.{�{�`�����Q@xw[_�s�4f�2VV�z��~zLZk�����g��sc���<�E�^�&�.�������\'*]�<�>_�o����y
����Of[�qU��9B[�f-�?�|���O������wNqOlt�����_�=��V���J��J���'���z�_�auQ���:71h��u|�w�������+�sV>�W>�i�����rQ�#��&�\�x2��3�CyO@����>�s=bO�x�y�e-L�x,���.�O��y��w��g��5��S�m�r�*��b�>Yo��z��������=��������)}�JX?�!�7�x/K��'Fz>��)��x��j^��r��xG>|<����d_��1��):������������%V����m�}�i�;H�1���`�u�,����nw~����<O��e?�f���h���&�g�������W�g\Y���L���|���s��t���~,�}��
��
�������LU��6]�T�M�������$��}mf�L+����:�E���c��{=��|-���t�^��6_�����U������W���[�>�19�G�&���VT�6����Y�|�9�}��
�����������q��<�����������~`�{��{t�{
v�~+>��>��}J���w�[C����&���s;(������:�{(^5��X�����M��wz�
��kb��S��<^U��s��A���+��v>�������mr�Z�_��������<�r~�>�/���8>��m���p�WV���9/�������}�X��)�J��{��'\�����U��s?�L5^�n���Y�8wz%E{��R��������\c������zUQ-�U��}��b��U���t����K��*U<GX����������z�>�s^�#�u�n���x��yRm���N�Yj�������=��r�7�8���x\��������|��o�������=���*��(}+?��������$ULt�_S]�o
�|����@�fY�HK���~8����w�\}��y?���������W��"�����V:��:�Z��e�t��9�+Qv���=f�r�}+g��pd
_���fj��+������
W���.��5������u�ew��j�����Q�3��'3+����WF���G�yu��3�t�o�F�d�~E#�^������-9D�T��l�~�6]z�$�m�������J�}�&�w  W�Z������w����z=���`����+���e�l���
��{��t����eQ�6�X�����R3�{�F��z=1�"]?���{v��IW��������������l��'WL��z�����b�V����`��Y�~�~�a�K����x��o���.�t���������G�����K3�]����s��I�����M����:
����z�i�'�������/J�+5H�}���8.���]9Y����������y��h~���q|��.�oJ]�2��#����eUz^d�#�,�7t���i������
?�����x�{:�D�ql<�t��?�u���sb0^��b��W�������f]�%Ti�!����-�)�"?�c8����\�K�f:������?1H]��yH�z��$]�c�^�Y����W�~3��	�#M��P�����E����
�]e|c��&����`%����-��d�#\��5���c��q�e���?�OK��e�NUf5��v�=Mt�[�}l�c�Z���s`]G�[�:��C��*�tIt+S�3�CyO@�����b=��U�N�^}w��Q^WXE�c�������W{�����[:�������|\�i��~e]��W[
IIq?Wd����K��|���9�~e����{)������+���k�����.�~�}��e��0�a���%��7U�Z.���UvIZmz�����#2�&��%��r�W����qn�y�J��CqV�=��`�����?(��/�������7�{��g�W�7�����uk���%�o��+<v�,�����=���l}q���U�I�o�}�y~_��w�`��w�ep>�'�uqt��	�����#�q��%x=G�������m�[a���zN��:����M���d�s��?sNyA���O�fUX��%��7y^e�`5�c����R�����x��"c��X�ke�}���)��W������M����|n��|��}r��5Z�n�=m�}�=�rw�s�|����������'����?�����Y���A��>W�������l���>���J�V�?�Q��\��������8�g �xneS0����R�c�r�����������_���>>����������c�O�9�|�v��b�[��A�����=�|����9�s23��.;~*�W���5������:f����<���o������A����G�d�,�<�4�3����e��e��T���������G{�e���+�Np|V�t�Q�cc��Si������J�7���9��{�������_s5|��*n3���"yA���4d�b���x�o��C����i����AF���+Ot+���u���)�g������O����G�U�����9���GY������k?��YU�K��;�h�+�o��un���Z�O}��tNU���
�Y��?��%�eO.�}���j�>��|���0`����wz}����~����__����X��	�	�
s[|���#�pM�/s�������������yT��88���|�U9�I�~��	^������������:��<��nUE���3a�b��(��a��V8�rN!\.���W���L��#��W�
Y��^�����^��`��c�
N��-k}M�d}U`��}�+�'��a�������6"��*j|�'y���k�<�%�):5��m�9t�,��$�9�uR&���O�Y�E��Y�5Uy�n�W�x���a�+�U�m_R�0Q��<�����}��S�!���s?P���G�_��8y��"��@�&�	SVU1�c�[��O��u4��-��A�L��|@�����(v|��/�tN��Y�=�W.��{$��{	����ws���1H�>��'���|���uX�SU����wK�z�C�����~�>�/T�[�*���H{��J���,{��r��p���^�[�����$b�������:�M[�P�>�ngA��D��C���#���k�J�f_U�6S�sC�����Oql��L����g�9\�>����}�,������\���a�}����sz��3?t�"�9����>�/���k����d����'x���7wls��������������"s�[�y��w@��g �X���)(�(;���]4t|�VrU�?_�rs�Vv��E��K��uDN�b~d��e������T!���e���;�8��t$����E���_�A����etb}��p�^NU��3|��p5W��L�OD���{�;O^X�qi�siIL�b{V��DM�*/��S��(�z%�@O���KF[���/����7�m�����/����l[`���k��������=�|-<`�z��/N��;?��!�=�����O5�8T(��>�\S���r��_�e~P)����_G�]������M�T��K�����5��]��d��
3���O���by�����X����Nt�~R�����s��|���7��������w]S=�!�y�Wv?s��qL��_�U,����w�������]�_�|��K������9�jF��O�����q���%����U�m�{�������s���}�6��>��}�����=�{e#�8������V�;/�g�*���z�U�����L�F�>�Y3��uN�WU�8����y%�������,�>���w@��g �X���)�0E���>}��5��[]���L�"D�h��4��V���9_S&�VrR����4���+�����������Fj�3�W�V"����m�MQJ��I��N�g�MMP���4v�i�����iB�=�����mF����5�~)����<�y��>�����������D��y����;���U�\�J���;�{T���z��y�i��s(��<�.��^��8�^���q��N�y�|=����|��[�����l��]s��i���>�g��'"A���H�G�e��������6�G�M�v�����M�5QC�i�kO�}'!����?j@�	��1E��t��o�������MP�Q�k�?������F���Z������[�G���i;�s5}�`?������L5�������%#���*))6-Eh�/�UBl�����]�N�y/���>��M����s������.��=�]��i�>&������)�5:9II��J�~�R�Kt�������}�}���8n���7��y��;_zL)���)zb��Qsr��Ts~!1y����������P�g��e�t�r�1�r��O���izy�h�U�8E��K������{���[]�9}b����m;a�����������zm3����LUs:�Q���MOy���G�[�/�U�c/�Nw�������)Uo�]�YluJ�2�~��.����b���S/zl0wN����A�g�p�';5�z�S��8�Yb���'~�]��s�5z�CJ
����������4Z�#�4��D�~�`�Jk5�G�����Z����u�5�����o����t�]�{�����~q�6����;�`��juTl���4�������z�����k��g�U���W�s� F]�{�G���k���6B���^���}��66�_������o������;R��c�/�����~fZ��~{m�'�������������T���5�����]K[�,��/�|x���U����=�_��M)1��f��k�`���LzT�(����G&���/�q�Xy�|��kS���Kr�t2�jtJ��&x�7�&�#U^���%[F��������Iq�_2u�~�8v�~}�T�]��v�Q��C��"V&�q�����;W�oO?�?�ZM~}47�����zO�w�z_9Z����
����T���zt�_[�z���O���vj����?�U��%�A?i�c�@��[������������bR��tk�3�������co�A�L7�����DWI�'�z���j�����E���{�]-�&����1���^}sM��R�����NM�sa$�}���5�-���q�A
/���������DW��&����Q�����w�����i��<�K���.�Y~���J�.�����r1��@��t�_<������>�����n�����<w����������A�m�&�~�1)�,�
@��Sd����Y���C����~�!����h|j�{�����!����W���k�.�*����ty/���������H�[���������=��Z�7M�)��W�_X��B�����buiw?�=�N��o�[V{���gV|�.�=�C��=���y(���;����29���gb8J������
��V�.��;M�	���>0M��_&��S~}���
})��m��s�]�M}�3�N�b����N|�j���Du7�j���wX*����
p�
-��4��������K����%���Z����o��:
����"����7�b�����_Pa59�OM}}�F�u��Lm�3��bg`8G���
�^�����S�D7�� 
{�S.����/�~�i;�z��]	��a�i>9��v������	~�M�ZV��Z.t���X����T6��d9��Z���^���������?�{�x�,��?�Rcqx��zn3))���7s�FX6k������[G�L��)�X]U�b��Nt�h�DKX�TV�}�7�s���{n�� i����N�� gq�s�����.����5^��_��6����]���N}aMt����+It�G���={����CKn�c8�Z��k��k����~�a�O��^]b�����}�Ro��E�r������:��i��j[g
*���{�9��,=9����I�/m.E=o=���nq5N�}�Q�:������|����E�]f����?7����mkM�)�R��6q�y��w\��]5��<�I��=k2S/]~�i6��[�i�.�nU��u�������u�����^��/rp���	�W�����}%��� ����{��}�����o������L`n����t��]�*F]R�kv�J}}���m�����#2V�����=yZ?sp������P�����U�*c��zx|�z�����[�ue����h�rk������q�2��:�M�wA��o\������g����}�>��CW��?��.��,���?�N��5���E�j�5��>Wj,����M���n�f�����C���^m�&��5��:S�t�5�U^��n�9x�s�C������C���%ZX�a����C�r~���
y�3b��~&�}�)�3��d��~eoul�������i���W=�v\y�D�FCh�"�`�J�J��Cz�����KVj����#�@��j������
Wj�H{?��1��UL��_z�RGO���<�E���U�,p���>�jr��pn|O�BS�1+�c�����4�G�h�����i�i6v�����J]x^M.����
��w�,���T����Y�T��]ex��)�v]�#����kX������x��a�?��c����
��j��t�.�����\��W�����hj���;����J_��s?�W{��tQ�V�0Q�v��^�5S���s��B�����������3zf�*e9���tF��#�U*�e�i��S���/4"��L�6�����-~�J�Fn�<o�\���vX���������V�{��~�MU�V�����}aD���_�dN���V>Mm����~[��X����(�2�4�v]���Z�tQ��Z���������<\x��T5�G�P�(U��q����OW?=0�z]� ���VP�(;�^s����G�l[5�ot��GO��%o������8�8�GcwD���*:���j����W��W{#��:��������D�X��e=�e�g#�����<��5�/vS�S����=������J�Rg���z������g���w?hj�3�������Z�����y�0]�����tT��V��izd��j
���O�����-�z�h�i�1;z��B�����p]/�����<�{�[�Q�5r������^^�5hy���ew����c58��������v�f������Ba�V-��1���C�������A�m[�������`�)�e+��=��W:�Dg��|-Y�R���_��������/��q��Y�4��jr
���v�����v��2=�b����g��		oeZ�����.S��4�;���0$}��u���3M����o�,�����
kx���_�9A�����7Q�~�D��:O�����K������L�?���/JUF�����g�M��(����
���ML���f���w����N�tp�R���8
 �����0�Hy�(6�c_�FM������>�k��n�����\k/����aZ
��"�!���s��,�n���h������-��.X���w����7�}������5s��:R�__}���(��������I�]���^E��pY2�}y5�?��=M��*(��M[��I�wt��i+�UP������4���5��y:���\���y'\�?��5�w�nS�I99��d��f��C��4M�+���J�n�+��O��Jl�w�jxk�5�1���B�{�?+��j}��i��=�� ���5�9�=D��K��=���<������\��gh����dj��7-�����@}�m]�[&ez��4R��PvA��vo���H[�z�����U�����S�i��!F��#�m\��jXu(�k���������@�t�?fy#�*))6�Fb���y�������
�"}T�������_
�_��c��LDt�.I��9K�T^�i���>��������8_����,�n~�j����,�'P�(���3������J��5��
)����KT�:$j�{���9{M�����|�F�t�u1���=
��tU_��������uTA��:�_�Ay
�v��J��Z��W�w�+���o�{�=���,5��8�v��
����m6���z�|�����)��������=�("Z��i��9Z�e�
��b��}�V��U�+I}����>{�QY2�Yk���tQ���JHt���1��o���1�������`�>Q�c���jMt�U�]��&�5�GlC�z��`V�<17{�iU��\����k2�e�;�:e5�D7BC�>�q?�Z��T������6���p���<�#��|j�A��&J�t�D��.|>����c�`~������7m��a���4�>]��Q��YZ�y�T�L�&������4��i�i;m��&K��*������ ��E�G�/��Y&���?�sH�w��M��������]*�W�QR��t������%�U��23M������NU{��g]�Y��Xk������]�p���j���q�������~1��X�Ke����S��W�M_c~P����t������.���y�O��4m{�eMt5�7��^�fU�l��Q�c�h��\�\".h�����Y�%�J�?��0��t����������e�t�{Y�%n�"�k����6�}rh���k�_��^����k2z��5���F��F��~����x�3����W�.w�7M�_o��,W_��@�;F�z�P�
�TQ{Tg]9q��17��p:�u�����.�~	��]}K�48
�@c��e�������iV'���=�y�tD'kr�
�u�[<:�i�3zg�iW���W��1Q�E�A�<���8��?Oo�_���G��C�z�R�����k0?���n��*|��7�wj��T/XV�Q��Mi����3[�NWz�o���_��������l]�wwa�R�*����V3_Y���KtcX��8��^�������N������w��C���h�������e��������_����zf�*ee�*�o����JuT�k-��{i���&-WY�DW�����i�/�#$�AR�������m[��:��n�4���j����l�U\T�z8s���z����N���a�����M�]-}a�e��Q��BM'������Cz��[��m����zj`���Kt��V���;,�)��|�:,p�r���0�tq�K�i�����`%��5v�G���iSA�6�������5�9�k���)z��e���������:�G���n��
���t����+�W���\/��
�0��y3������egh����=:��A���M�8H�F���������L=9�U��Q_���GFy�2+4�N?���m��6;_g�s����U���]�����N���h��x;k��I��'��<-�q���Xq"'�^i�L�$Dh��G=
�g���'�Y]&q�=���g�y���,��������?Yw���~�e�����iz�o�)Wo�a�c�+T�c]���Lo�;���1���S�[Z��F�[�6�i���M;�y���������6���,���j�.��>R��]�o�Kt�1�Fk�C�$�q^����?�q,�ES�C|#����GFY�v+&���]��m����+����Z�����N�n�P���������m�[6CwY]��i%��y�&x,�o��E��y�V-��r0a��oo��U��*��M������<E�l�6=���_��3�)�u�KY��-�:����o���zU+�m�{��HM���&t�@]t� �U �j���\x`��O�2m�H]�hIt1x��x�3�N���'�zo�����Fh��'�*���j��������2�*�����5��>,g2'i�=������b��TOO��f�A��<��G�`���R����-�)u�,y.��Y�4��W�F$�����H��(s�@�|aK��x�v-�c������h��������?O��,�WZ�t}��;M�xO��o��[����[����.�=���wF$������A���������-��7q�~m9�1J�g��C�N���uO�K�������������>��b4|�������[n�s���H������t��(�v���Od{} �tW�5�����WYX,������I$�����������8�<������+�YN�,~�z���j��;�Qu���R�i�5����'�J��%y�J��{�������K���1��G*e��5�D7&M3���|��8R�+99�X/��i~_1���D����^-���d����>M����'�:��D
�����7�O�w��>��������G-){�������������<n�$}3�&��l�c���MyNwut(����^`}4���<.~w��H��+����<&'�]�Q�4v��tx]�n��W�����!rN;7����sq�.L��-�{62���
A�S��������cta�`����6�,]V��^����T���D�lA��D�@o�{$c�����]�1s^�����nS�vj���9��:_6�ZL��E��{���u�i�VL����������1���}��c�Y0^�����7=3�h�.�/���j�'F�^$��&g����[#=�E�6s�H-���6�Ii�O���y�������l��O_�yMs�\����e�G������T��n�����uf��]����������]5��I�H����	��T�����?�o�q�]H�'-����O��0��yLS}�o(b����Z�Fi�]�{�����{u��Z���?8N}���8���Y����"��@(����XGSq��f�����]�ji��Nt-�������c��i��������>�����z�m��G��I�u��c��k��>s��s����9R=:_�i�DWS����$���b�e/\�@���y����ws��������������*�����=F�JM����g��
;��L�����B�����v��X]M�_�MZ�=���qj��ARR�W�[�<�m�w�.�����._~�����-�/2��z&��n�+�-���s�����U�Zf��1�����}����zz��U ����:�>���^]����������Xf��c�=�E�DW��79t����.����i����@������'���mO�����6?
�����|�?�OK��=�h{��/�G>�b�?yU�yPO��x����;>	����+|�S�����C<�N�)>������,���pk��8?s�a��������ON�����S�eW�g[����!�J��u��������T�|�����bw�c���kJ�`_���vT�����I���I��}=�^����2�WG>�OK��x�UO	S��o��W�6y���,_�T��fy���l�����'�x��"�)��i�8�h����Y�X��+������Q�������i���\������:�����f���\��0 �xneS0����R�c���x����i59N+��S��t|�Zr���|~?��q<~���R�Y���H���z�0���m��j�����g[�1�O~~��96�3���:�Q��=���-'��`��|��e���/���X���0�u{V5/�V�
�Sq�������X��2�F��j~����:�c�:X�6�<O1���h��2���6�SA���jz�>���K�X�#��6�R�u�������r�s����2��'N���s.G�O��y��)�)~}��;%�m�==��� f���j�f_mfT-��Z�'O�?@�������[�����c�����t�sd9U�����h��;4 �JlZ:j��+��?���cy�X�=��)!�,"A7���v��!?7s��g���zu��z�1E�~���K��'��f����7s��Q)�n���W:���Wj���Z��L��,D$����=Z1���0"�)}�~�|���6��.�V��{\������(�1�8X��s�s���p����������>Jb4�������4'����{��������Z]��WD��Z�k�fT��D*q�b}��q����z�q��Z����=u+�-3nJpn���^��;V���f;�~�6�l��nDO��|����f��C��
uLyV�����B��'�gZ�DW7edkO
�S8�&�K���+t����`����{�t�)i��u��F������y�����pls;���)���p�[�V_.��N�E_9[k6?�_�48
�@S�q�f��S�WK5�����y�����u�S���.���stC���%^��'��_��2L���8`����Wvf�f�NV���Wt|�F�p,�=yZ?�j�y�FF������P�@�r�}�t��<Z�I���,26QI�����?�W���5��mZ���7���#�����l�� ��n_�[��d*}�cY$����p.����b��ND����^y�_i���t��r��Ub�h�^��r��k��q�;��]7�Y�#��LJw]�d��K��c��<_K>�V��%�8���Uk}4|�g8R~3���;:P���+�����ghtr��l�W�:���,��3!��Wv������zJ���*8�^sn��8jvJ�U�DW'�
����S�y	�1�]���o�k�����B��J�$��""n�&:�E]��d�q�%�t�@&���q����2��
����
���O�d;&=r������x%��]�6S��]=S��r��������#���ef/-=�Q����y
�������GO��%�+;��LX�9N���|��5��|���v��Z�V+7?[�/)]/�����Fm��(��}�v��LW��.4;�+���x�=�}�L]M�4
a�n��]'�1���� �e��%���&����M�{r����a�i���f����������o�}\��R���3P��w��@m�����t�K&�����8�4o9��C����m�o+&*����}g�(}�g�HB��2���:w��r�������l���������)�4 p���S�u�}p��R�@P�U�f�tJ����	�S���,6�_��{�F�\(@@�(��8���K�����lt������e�N�W�l�C�����Ty�7V��rp=A
��&sj��8��Ac�����wd�~���z^���K��4 `r��O/��5����7mh�(@@�St�|�dei��g������}���*�l-z�9���Y������
�rT+z�k�=�[�M�D�Q�u�����g�����IU�*��Za"�Q��s�!��i���q�k"'��i�L��I�R����z`�{:PUnl;����I��+5d���( ���w�tM~�l_�Q���>&*�+�����2Y����R���G���[�6���b��~�2����4�Q�m�f�^�R�Q��s�#���u����(�Z�����|�O�rM��^������i��Iqd���z��U	�:����
�{~X?<^�]�t���U��������X�^���kF�	�dN�e�.��9�i����y����n�L����.�����4����BQ@����^���s��������i*�Sy�^�/���C�����$,,������d���K���.��+r�fe~��w43T.SS���|iH�v6��'������WMh��i�|�g�q��`}j���N�\p��(C��'�-~K���?���zR������=4i�	5E��W���U���D��Lh���n��w�E5&�S�t�;�U���4�E�����@S�W����7���^��3�j���#&F�y�F�Vzn6�_�@D��Z��f_�1t�"�)�����_�s;��E������
�`Q}n�+��)�����]�>)Q��Yrt�����]�����Y?G#H�J�nz�z%��x"c5`�l����	8�JD����:W���k������x������I�=#]|��c��h�.r(��|�h%��x��}Z�i�6<>X�m�"��4	
%
�`� DP�AB`� DP�AB`� DP�AB`� DP�AB����^
��45��q����m����qQ���e�������E���[�6-�3F�z�)�,����Acf*c�^�_�is���K��	u�y��0��i��3���az���5�U�i��v=�b���e���7���rt���(Z�>�k�PG3J}�����eS��u�]�)���S����k�k@�N[v�R�OR���L�Xv����>��`���nP;���Y�l��H��_�#;`�&?���<Zb#]s�B�|`�����3���������vM#ch�dG���d���@K�|@�����`&�G35}�)��H#{P�nH�^�u�'�T���I����A09���JQ�=kK�����u=00N�s������X�B��M��[=;,�@C�p�g��/�$����3R)�>��T�;�T�����uhei���i���g�)�g�v���������=������l�6���.W�iU�Gzv�)�*ASV��=��Nqx�rm^8��8�y3�T����@=;�M��z�A���S�������<��7So8���}�'��*������
�
ho�f���S��iS��?����7m�L��&E�9a��5���Z������\r'���`�*�8{N�<w���K8�=s_���4kKm����}@�Qn0�^W�F��>=M;����W��4����7m�"����%��zq�,��������s�]^u�����W���+T�����Y��V�+<�e��V��n���~PW��N}��i*M�������Z��i+Y���s<��[M���~��a�>����p�7����T�q���uL8?�{�&�������J]���z�6]��5�O
�>��(7�}��I�i'
Q�6��:c������0�M�����c��\���/2�[{s�tz���f".��d���m�o�����]�4���S��	��(���r�V�����t���5���E�����u���/�W�=�2i��m�Q~�����+g�2��c���n�[���Rn�{(����a�Ui�:���I9\�� WY��S��K8u�;���:&������{��~�n�e��T���+
�
${�'fX*��:�"c5`�d=��Czh�h%�G����p���RM��}�p�>�r�Y�5�*u�S7����yrg�?S�N�0�6k����]�-��4��uE�A({]�%��{�W��<}��E=���z����p��oJ���k:��i���Z^L|��l���b(+�'
U���1$���>4k��i�\<Z�II�����K�i��W�s���#7p��]���f��L�3w�
Le���]'aaa�����C�����&��-�m�L������1imi0$]�?�����
��v���h��v=�b�F.;;������gO�j�;�$����
�1I�����L�O8�95L��N���pZr������� @��|8Xy'=������u�iK��_��\���W����7��0��u���[�S�i�g?�������5��g��s��x�����E�U�p������{�������g�\���������WK�Z�������Lx����Z���P�D�[-��#�z7b����G�4��]�������Q�F�>�E>����4U��h�|��S����������1�7����\����N.�c"���U���]���P�����W��������'��f��i�iJ�n���i+w�i�O�%��s'�j��Py��e����Sj��/��a������e�9����'���Pn(E?���C:�_�H)�I|��MS��k�U:���G]�n�L�r�Jr'���/���#_�(�Py5���c�e���H��km"4���u���d/>e�XE
��".��Dh�(7�}�\���u�������W[���Qni�v���v�U�5�4�u��x��d�s��FJu7M��+��pj�	���N�o	g����q��u��eD���������Ur(�DV-������L���p�����������M�*�?�;���~J����k ����2mm�������/>0-)����:f@E��N8�q]���8w~���3��m	g�����h�Q��j�*\Cz�7�[��*������Z�Q�[�J-��IcG�!��#M[�(����m����s���o���1����������K����^�|����{��������[T�q����W�pj�?_Ty�yo���E
,T���l%v���{�gg�7*�RRsq���N,{�D���t�wL03���1T����@�yt�eW^>�9]��:`�H�9������6Yw��W��s����q&�[��f�e����d�����i�;����2Ui��u����p����p��������*,*1���>��B���\6K�S�/h�/U�~i"4v�D���{Y����>��I�����EfF�"�]6Y�i�U^��Hso�=�~��0���ih�r�|���z��~&8��i����kd[�6?���ie}�4���k]t4���S*�8��)y�{:`M8���9�&O+�*a��5���3s�;�
���n��}4bn����.�o����3E�+Z�?��l0�U����W��M���q����,
(�wxSw��������������eqt����9S�+�f)���_�R��?���S������]un�T�/{����bZ�J�6R����a1���D�@o��82I�3�Y8R]������|��{5��xu�b�2����iZ��05��3T�@u���]{8m"��z�������7:���L��E��������h:(7�����/k\bY����,�K�<�g^Z�-�&�ta�-����`��k&��&����K4��T��J�*<o��������(Ou�9�}}�x��S��\�Z��7�yI������������Y�4��3T�@�6}o7-��m#t��&B(�����f�������'�e<C�45�XT���d�~}�t���>I����H�&&k��tef�k����'��(����%;�����=:Y��e�#y�7��'O�g����(��}�v��VKg�Vrbl�����S��g(=s�����E�A*���S6����&�r��
of"����xRg�}o"���A���n�)	�;�v�8���-@
�����]�_{������dgg�������h(��
�;�$�ESD>����4U��h����N}��b�Y�}�7�}��fB��-���e�Ld����O��Z�t2s�P��+��0@3��{����GS�mJ���=k����~�Q�
������:Vd/�{s��w�D'^�����/Q��)"�j�)���|���iYu9'RI��1B������P�����KR���M`�f"�������>��
u�]�T��5Y��j�vc�*�e+3M`�f���GM��M�p
���DEgO��e����6�S��n&BSF�����f���������F��Y�u�}i�w�"���Dh������9�UXTb"�}�1-���������:���3B`�f���?�����m��C��j~���S��D^Z����O),*��@(����p�5�r]/z��*{�)�|k�t�w���)���\j"�

�!�����F�����N.����Ld�}�Z_}��J(�0�}�������7�E��J��Y����7��s���7?���0@[���l%v�9��)�:������u�O�����g�EL'!�Pam?jZV}b���uK!d�-��7�����K���U�!QQ[�*��f"���S&
E����~��n"���j�������B��;���Ul�k{��jl9T��+&�
k�J���q���F 9{�:{�rEgJD��~��N����*r��u�F�c�A����M�p��D�(��X����8b"+�=���E���
bl%v���{��!=��U�	���&[��&�j�Im����P1�s�����DV#��cZ%��t��L��E�������t03�P1n����.�o��"M���^|J'��!�O�f�U���VD��&BsA �����k���^��
%�V��J~�g"��\��)�a"4'�BHe�;����D�M]���t��e&�
��V���r
���0@�8U\����/����-�L������u�O����#g�E��&BsC Ddf���n"7g�7�W�I;[��<*{Q��ay�h���j"4G�B��;|���=Z�[�4����������DV�?��6i4�+
�!`��:\Pl"���;��2���:��+&�
k�J���UX�6f�+
�!����{�v9'RI�Ql������;���������~^����Qh��m�����C��Pp�O�l�aYE$]����L���0@���Ur�n"�6��5�G{��:��m��4�U�v����'LPh�l%v���=����A9�)+���S+�3QEmo~Jam:����m�[��"������BSd��X'���������VD� �(4an?jZV��U��&BS������K��>j���&�(4Q{8��y�Mdu]/z�6e�{�:���KX�6jw��R�p3p��D�{��{�vl��/�6����ur�,U��W��E��&�(4A��K�v�qY9{���3���%:��c��V�.�Q������0@��uL��������^L��������w����;&����&|��}\��������uK�)����t:��M��E���:�u�_�*����O�pA������hZhJ�C>�x�Q����q�Z��1P9
�M���}���rN�����hSt�O����Mdq�U��[U�����\=�}����M��/��x�G&�
k�AmG?a"�z�X=i����>�����59��y�������Ur�n"�6��5�G{��(�a�N��OU��������&�G@�5����;{���C����UvN��s���9�p�Q����f+�ku��������J?M���b�xs���������\�?5�^���������u�������vV����:`������"�y��>��������CY&�
?/ImR���`���kw7Q���/�'04��r�����s�H�)�e}������*�U����R�p3�`��s�����&�����'0���?�����&��������ub��&���/�+�c�����4s�V9�se��A���pXXX@&h������;����D�M��e�d?u�DV���*r�/M�`���>|��j�y�9 ���Vv���ox.�m*�>}E��
&�j������i"�v(�\e���c�E����c���M��,����`"4v?�n���d"/-�����
��77��pc�����2C�
�P��[��Ua(���������-o����A=�U�[�t��13��z��u�H`����m��`|�z�E���1]i����U��
���q%�?'w�V��-M���^|J'��.��}�6?���	�L�������I�(����@��f�6��q�M����,����M�o���]/��G�icN��S��\m\6W�R.T�
/h�Q3? ��G�(��Du�x��,Z��|w�^��Q��NRJ�X��}��3��O�pA������hZh�N��I�=����")j�oM�
�F���.�?�Cp��;���	�f�g�6�Us�V��1�P��g3R����)�����4z�Cz���= V���U��
>G��:	����������3�!:^���|��t���*���r^�C�]<]�-fX}��w��.�D*����t�?g��S���4�UX�j;z�kh (7B�����I��+�m�6}�o������.���)�oR.� ZQ5O
"��\C�@k>��y��?�K-}�i=���Z�e��oZ�T��8������������4����h�.���Gj�����������^�YJ��gr�)m�����B���/#����)(9z@�����*j7�	���d"��(76��Zp�=Zk�`��������]�$�@�����3��n�����@�>zvJy>�0e��~p��"���
�_�7/�3����z3����-�|N��[V��TJ�f-�����w��h��$��g��K��Hwf��w����v��i�!=������|k�����D
�MIW�
���M���i�8������\smo�f���S�kJ��j��ymMT�������\i�C�r�����i��>����='h�#�&Z���7�v-�y�v�B�
��!V�������uL��=�/}F/�~Su�ex����:������omFsB�:����S�vY������SM{�F�`�#�eV���"�|[��>���e�+Kp#uM���
��.16��*���7�:iZjW��3s@:���Za�u�����O��{UV��q�j[��~���e�d����*��+3�v�S6���)�
0��{TXTb"�}�1-4V�]�T��Ld��������Z�2s����X��Sw����;Y���T��u�S_|`�J�����pb��>n"����]�	���8�}�~��\�����u�����hS��������V��?��m.��&���1�]����;���Vkv04��5-+��L���q;{��N.{�D�I����&�p�pT����y��_M^��Fk4�}��I�XVIC���.����1�z(.*Laa�)*N=���%���"��i[����?�������_���	5�g�`P������&v7�*D\�~�������4k$��[R�b�t�nn�U�W�>���u��i���Nkw�iY]���������,����pE^q�������|Rw�/�9a�z2h�_��;��4u��*��R&����9�/���L�r6.��;+>�����F�����w`�h�i�W@����������S�w���^�t6M�M9���P��)W=n��1+�����;|���c��(Mh��������:���3�����m�K�~N��E��i��R��?Q�hV�t�D�j���z�������J����(\�.�TS3)�*�����/4���u0-���<�3M�g���4��Iq�L��������4�r}w��rD���	 ��K�����\�����4b%��tj��&��"\�n~JaQ�\��Q�7e�^r
���)�SJ0��*P���K����`�r�����/������/.���GtxS��&�_��O�E����h4J��9.[��Dn����v�����;Q���
���������+G����_��z�LT���E��)G����2����������f����^|J'^�.�-1s�Z_3Q-/��D@�Pn@�oL�-����M��XO���pZ1�VrR���cu��mZ~���e~\.Bq'j��%]v��3��g�
��cM������h�U���N��Q*K��g���U��e����f��
���J�v���uK��9��gUr�������_g��J�+���wK�cN����S����I��/���;u� O�3:AU�)"a���r���3/�E+�!���g����&�������G��'�T?�F����U��S���Y/��{�J��e%c��h�3M���z����jDz�6V�����-�4��s��v��u

��0��i�IXX��z��c�VFjM�t�4kC�O�Q�����������k�g�X�������#��	�\~\�sS�K�u�z�j���;wv��>�sV��5�����4���&�III�U7YYY���={�V�P�y'yn
�dhh�IZ�
�(}�g��G�95Lei��|,��)&�S������F��Vz�=2v�n��z���mY���r��q�X���v�-����}�0M����*�H�<-��t�@z�6F�'��a�4���}�`��k����+L�@	��8Xy'=���M������*�:��Yok���oc�SW%����Cl*�:���W<��t�y��b����Y[C�v5���F��[��-�=����g��h�K���g��"S���r�V�]��)�%�::V$�>�;O���Ncf?���^���[�s�_�;z�3[�)%���������_<����C`�(chM*����2v�3.�n`eW<_{������dgg��on=�4>��G[��?WU�7QDx�^�MOEG�����6�3�/z7f��v�J3�d-����S���a�1�<����(��{����J��ymy����5�����?�Q��Z��x%
�gL��vW�cVN�P�0�sC����h����T��"������_�`"����������u70N}�'}������z(��SX�6f!��]z���k������KZ�����|<*������gS3���*�Q����B��,4O��:nZV�F�� ��+�|�����5�*������%�jZ�u
��F���������Wdw������/h�)�:G����<��%��'3������Y?�����:������lP���1�UX�Vjw��� �[4#�^�Na�;������}�����U;���)\b�����S�%���\}Q{�uW�@�tX���HV� [�6��R���i�HC=��-Z��ij�\��o���{TXTb"���>���X�O��e�����Y���
?/�D@��\�����z�!������r�M��=~6$����n}�Q�i+�}������s�S~��~J��������)p]]��}��������T��e���u�*0�J��B��b�Q��t�����K�z�R��0�~���[9������9i���]�@#�����e�����rN���X�X����>^l�;EQ�n3P�(��N�����~�������r�9N�=~vK_3�&�����(����m����s���o���1m4i��:fZVC{�Wx����@��i�-J3m����W9�M�����������|$�
��v��YYYY�-�����j
�G�r�s���o���o�5������w�DV#�����)Z��lY����EL'���c&��$b�~3#��7�N����K�G3���YL�;��!��M����Tzb���Lj(>M��,��x�f<�)�}X$������eS=zkm��~�n�F9~b�����1m�f>����)
"���;��@��{��{�:Gi���h�1(9����`"/-����'���
�p����03
�P�5B�����e�@�����wh��"3�L��.�����*/��Z��7u4��Ow7-��?k��b���R|��xz`~��'�����m����R����6�0��_Y�4s�;�
����?o�;4����VH����/�.��%�������H�,|\���6N�h�n�y�������x��O������S��c5����>�D@����t�'�1K�r�o���w�����z��1�,��.�P9e1����oS|i�&������z�7-����������g��p����E������j�e��z�4e��������a�)
k-���Q7�]�������y������:��X��������o�����ws�_P;�Y�d+����Y�u��x�Z9_%y�Md�2����|����E��8�q��r��%�U��(�2���3z��e��o2bg;n��\��s3@H���I=Y�6 �a��B
��*B='��/�Sy�Y��U���3/i��|GZ*�������R�o�y�o����{����]���X��[�J9eysd������gSD�j��J�v���C���tf��&�
��V�[����
��TT���d�~}�t���>I����Tlb�F�HWfv�v,�]}�����}���iY
���(U��>�/����j���JN�5=s���<z��3�(o��_l����y���gktr�b�����u_��v�-�8��/���O�p����G���ocq���:��'MTQ��3��Cg
/��`�u����=4Pc�W�v��������������g������8�GE?�����>��:"�uuy0��j�<������<M�0�2�'4U�����\u@[�����9�z����b�u�OgKT��$���Wf�U�����W���!���`�����o��>������&�3�>��\=�}������O2*-���������D@�Ah&�����tH��
o����|��G�����EE���u0����,��"\���UX�6f�xP��N�u��w�P���3����DV��YF����[3\C@��&u���K2�����o�}/87�5����*,�]Xd������Y:[p�DV]����L4>��f��C�������GM����m���H�������wd���E�s����L4N���m�I��Xv���y�_@����i��;m"�}����J�v����LTQ���RX.�F�Fq��:fZV�&�StT�����;|���}�����h�!�*���s��K���*"q�����0��lg]���eXO�X��t��Dkw7���������pN�|^%��Ld�2������D@�Fa��)���������������u�g��,�:�h8�{�mx�DVa�����s������0�*��y�_�,���q%�?;�~v��q���N��IU�v�L�wL0��QB���be:e"��/b�g�O[�p�i���C��s�D'�yL�S�/�nu���	hJ(!������������{�v9'R��ok"�����"���&�r��u���
�@�Z�}���������!�������m8?�F�3��D^Z�������/��PB���'u���Dn��������?����DnQ-�EO���[3\C@������2������0�*���vj�����f+�kM���Y�u�Q�N.�����7�UD� E]}������
b�lg�qo��������P�6�+���?�������/�����&�
k�Amo~�D@�D1����
som"�]�M�+�1-����U�s"M��R��>�Z��&����O�E�sM4M�������e�7��"��L���iw�iY��C���f��X'��!{�)3�*j�8E\t�����0B�m���'Mdu�y�>}��G��j���.������_P��,Y����6�SM4m�����c�e�F:�`�/��K�&�w����oxr��d��NE�_3�UX�6j7v��"���6
�@��|�y�@}Z�}\������_g����#:���&��M���n&�>�!�9��sho���t���Se�?;�~v��s��Ge?����V�Ry�h���@@��t�q�������F�t[�������jDz����O_�-g���Zt���#g���Pd;��{Ldu�E�MP���w��.�D���mM�`�)w�N��%yi�v��UXT���
�@p�E`omZ�k@W�Y�/GO����&������^|J'��!�-1s�Z���Z&�3Z(!����'�("<�D�`�p��*9k7�[TD]}Q!�N.����Ld�}�Z_}����Ch��m���'Md���?���d3��3?s�|�_���|	k�Amo~�54���M���|�\��!R=�Z�l������Ld�����������2m��Z�t2�(M�����������+�1-����U�s"M��9[��������D
�M�z��]��&,��).(6�[x�0
�A��wG��;����F���o}8����S�vY������SM�6
�@V���};�U��&��;4-���[���M�`��lP���1�UX�Vjw�������0�D�J��|O�������i��Tq��d��@�y�_�(M��c:���&��M��
��a" �Q���{Td;k"�6��5�W�@}Y�}�u��7g��YFp�x�Q�=�����{�F^q�����0�DU6����E�su9����v���S��5�v�3�U��Nj;z�����0�=i���'Md���P�<����1���>��
��CY:��yyi�v��UX#d���4A���|��!R=�Z�l�����o�s"����&B���O������%f�U�k&����h^(MPe�?_���������'Ld�������T��&�r~�`���41Y�N�pA����������?����DnQ-�=O�Rg�Xf"+����n~�54�\Q���z�^�V�F�L���d�����_g�w���:�|��*j�����Cg�{�	q�`�|O��������}�:~�'Y1�s��-���f�^ThfXE^q�Z�n"���4!���v�Dn�+�����l+�1-����U�s"M�@:������oLd�CmR��h�(M����M���cf"@0}w�H��N��jDz��m�f��Y��l�v�<��Vm��y�4GO�����&�b�g�?�������n��/�6�~��N���kh_Z��_��%�`��p��-9k7�[��VJ:����>�*.���c&�r��7��3��<���Md�t���3'
�@��.��?���������T�8�Y�u�XE�P��LY����v��0�2��&�yo����1�����Ml�
����94��P�N�|�D^Z����Y
k�E��7
�@��.���]�V�F�L[�����}��H��S:��c��Tl�XE
��"��'
�@#�Zl}N�������w������H�>����V�WI�nY�L��6?�l"�(���}�:U\b"���J�c"@0=i�WN���{�����g6�m"��Vm�����!��F�1�yE�E�),�1
�P��86�m[�9c�G\T��8��.�4ff�V�-2���T�����oDx���q��m��9c4�G��L��E���T���
V���-��Z3+>^���Z��.��/�����Ur�n"7�����8g���Y&����S��M�
���-[��Q��[	���������^<F�,���|��n�����ra�nxa������+��}�DVW_����1�)���������2m��Wy�Y�����jR�����m	h���=��s+��M�[���V-������=Y��
@�s��gM���s��h�*#����[3d/*43�"�R����@e�#5J��u����U�u�S�������5R�Fk�C����5 6�5���j��5|�F����m���>�0�����R!G>�����~M9&��������C�5z@�#-U��
>G�p�y�*s����������<�C������Y��}G��l���2��y����DV#�t4-���O2��w_��*�c���b��T�p#d�~Y������8�I�������rd��m���/�������/.�����iA��]�pF[�EO���>�u���^@cd�g#�2o���~��/:���_��/s����&�:������:_t��-�|R��?`�H
��F��|k�i�mu��m[��	�W
W����8�n���mGL�*��6�rN��a�g��Y��E����Y��T�pcc������&�������GO��o���q�0s\"�4�����p��q@�f�I"P���pZ��}Y `�O�>zvJy>�0e��~p���	�������<��7So�)������+�D��������z<)���Z�����d>�W���P���"��;m"������!��C?;����M�T���d"������q�M�����;���57�r��I/��CL��S*�r�P�	��H�	������
�z�&�w�_����!��Q�]�?�N�}0E�f�='h�;����$��~��M�����$M��U<�8=|�	�����b� �|��G��j������!FP''����6�U�EW)j�8��F�`�#�eV�S�w���w��j�a}��
��F�5\���S�`�����8_/��P_l%v}�������4�?{[����4���S�y��3�W��g��7�������^�L����*��z��i�Pq��Dk����*�W��3j����U��#Y�hw���~�D�E��(X����'W�7v������+�k��Uk��bNV��N�]�W�jZ�D���
PO��/TaQ����"Z(�{�����i�;�Tr�U�q�J�g���Z��3��{P�GM�E'|_�B�sd&�������z�c"�VI�n�Z9�D9���"0����(U��wh^i�W��<���:���f��*��iV!��~*��y�6�7M���|��_g���q'��_��~��S�j�p�y�kDYaej���*�����L[���i�PQ����_��m�5O]�*��������c5������g����O����C?'LyCO�������*��:�����u6Mi�r�t�O���O�����4N��r'���_��s�	�6�*�������L[z��'���lSv�4�*M��%�6����cgLdE���;��y�r_L��e|�q�����v�C=v������h�c)���8����~�AmM�J��T���p��>�������P���:���u8O���g��_����&�Ay��`���,
�WZ��za�N�XT:�����`�0
����S��JI���U�45�����o���8���.lY��h�&�
k�Fmo�+�7s��uT�M�����MY��R�U�u8Q�z�:���\��O������u0-hdN��gm�`=oD=��K-�W���L�kz ��:�SXX�Zw����������Wj��]Z9����q��M_�=2��>������ub��&�����
��`"�A���1A��Zz�p���z"��_4	{8��?�blH��z������V�h�>�*���u�%�Q� �|��G�#3EE� 7���%:�l���|_���_�Z]r���V������y��=th�yE����2������o����������������k�gU��	�dU�IkK�/SS���y���]����[�z�����=n����}V��?k"����4���=�URR�i�MV��{D5G={�4�����N�������T�qQ���4��6sj���^G�){M� =�-�S��a�fmq_����1@�9������?�Q~��#5������(��j(����O��l���t�ffx���n�A���j��j���&�*i�c#��������T�BN����TC�e+c�=��_%h���+��8O2l��X�u����P/�fj��a��o�X�o:�#W�oO?����V�V��=Z1m�\w
>�����L7�������������tEgr��j��K���}�_{X�J�z�;��3R�R�\�gmP������
p���~��#_-
j�����Z���^{��h(������[-��qo�^��A�E��i�I���j�1��|��A��|��+xO�m?R�g�z�I���j�p�ym��D��:P�N���/jD�wl������s���x��0�2�'4U���g�?�jw�i�%��F����DM_}����B�7:{�{3�����u��&�J�]z�[���]Yb�o���YI�7b��kZ��Tb�U:��}�)�Rx�u�Q[���iY%w��S��.����M?��p*��p�Um��?��x����YE��)B=������|�]�i��h��;R����4�w�����s*-�F$��g����5�������KZ����>��9���?��i��D|��MS��k�U:�����K�-t.nhT
�J���&������U���	���K8���9�Kj�p�ys���|�V�n�GW�������{������@S���M��}��J�m"���-�T�7+Md������e"�B�9�����)�SVi�*d�s��FJu7M���o���C�tl�������Jt'�Z�_��u��S�k�p�y���m����������Tq��d3�UJ�
o���4'%G����5QE�F?�1�L P(��v=��z�����1�bS�{��
�3�k"����2mm�������/>0-�K�������}�aX����Y���Nm\��j3��_�<���F}k�p�y��p����L�����4@��&��l%/�u~��u�����%:��t��O�VQ��)"�j$
�����?�����o�����z��t��������NzK�iK�����a�m���������i��j ����rN�0�3�����[T�q����W�pj�?_Ty�yo���E5��M������Z���������L[2�BE�6hZ*����]�3�fN�|^%���I���KR���@�Qnf��&��H����2����M[��f�e����������I��Q��V&�F.>M��	�f<��J3�����S��%���P��	�-���Z�g3�/6eg�A��1��tOZ�i��d�����Y����l���h�k&�
k�F���UXK�] X(����a
3��������*Z����%k�{)�[�6?���iel���y��������������@��Q�>���s~��'����Sy��Sj�4�g������%��S�yn�Pe�Nt��J�o��������1�P��<k�����_���2Uv�8a�,��7hR���w���"uq|[�gO��w5QEm�����q�I �(7;����JO1WG�����]un�T��a=|�]�UWL+K`#�8m���X_8����E����9��D�@o��82I�3�Y8R]������|��{5��xu�b�2M�42q�=<Lu���y;�����U�6����tE'E�]�1�:�a�;f����IW<�R����S���f&3�3M���6Wn���>������������Z����G�@�Pn�"zj��_j��D�K������g��K��%����h]9;S�MQG3��&�wR��=FQ�hj"�s���r�8%�N�Z��7�yI��������r�27>���$��}����r�*��2�����o�������^Z�Q�e�_W��F�����Th�>���kt&o��lH��&�?����k�g_Zt����z�D��3J�UT��d���T�G'+1�,Cv����=#]�{��~�`��A���[�U��d�tE���K�c��Z:{��c�/>v$�J=C��{��~�4���F$����y��j���u�����`E�*1�z�5���u��W+��/M���^������M�p�:?�n���d"/-������qL h���]'���x�C5�z�j���^{��h(�����{����<}��Q-�����:������Px�������G�r�� �h��;�$�ESD>����	M�n��>�@/f4���7'��9��S�&������
^��J�0s�Z�|�Z_3�D@���~7Xy'=�������2d����mGL�*��6!]�
����TZ���>P����D�`�|w��5�r�E�����v��6������_���T�����*�M���I���`���}���z��V�\e����M��}��J���j�q���:��'MTQ�_=�1�L��P�Y�Y�>��|�EL,��K�&��myRzu��<�8[�oLw�����+nQ��)&P�(�l��:~�'�E��ipb����&��l%v�9���:�D������r���*<����=h"��0P�>������n�j���"@�U6���D�c��2��
*��Y��l�vc����0(����D[������ ���=����1���g?uL'�y�D9{�:{h8��z�����9�0������RY���"uq<yYuN,{\gO1������{�hX��z����/Cz�w�k
<GO���w�Ge�����)Z��lY����EL'���c&��(����g����&������g����Ge��h��0�+9����`"/-����'��[�`��|�}�������k�1@�8��;}�e��o�V�&�7{�)�xs��?�9V���[��@C������v��yXO�2�`���P�O�d"�}:�|9��?U��>Y���R�N�h(�`��>O4D����)@�}���iY%��F]�aT���Rg�Xf"���h���)��
�@=�t�����F+:�D����g�u��������oe��^'��1QEm��Zt�l"�` �N�h��BY]}� �>����o��-��=�D�8[��<*{��|6���j�w��4&�� [�S [��Dn�
���3 �����{T��^�"�D�t:����w_��*�g��&��&��P���]�L�jh���h� s�lgM������u�����w�N�����Z�R��s�����������hw������E]��!�
�x�����d����YB����P��������W��~��Vn���B�m�ZC��'�"y�
(x�#�������p����>������;����9�����>����������e�����P�6�po�������~_���zC���������LDDDDdF����f�]�zh�����������N��j'����_yw�����EeH��4\����,�DDDDDf�����9S}���O��W��h��n^
DE:���A�RT���5C��EED��
`"""""39s�.���E��pt@��l�����\����]��������RT�5Y��<����LDDDDd&���-F��Z����QTDDDDDDd��_ig��\u0�'�W���|��(���7���'*"�tl����8���Ls��@DDDDDd^R�WyN~R����;qRnq��VhW�������"*"�l����l��K�4�V��j�PTDDDDDDd�i9���CQ��]�H"���wh��98�G����:l�Y6������������Ls�:�"""""""s���%F�|��G���EEu������*�����N��""k�0��]����Ee����������������yH�qOT��t��_��Gp�����9�|N��DED��
`"""""����j��E� """"""��uV~�o�zu���UT����`���p9�-�gEe�����Y6������LlO�|������������|�� ��;5��<��K�Q������y�
^^�=�����DDDDD&tZuYw�����d���%""""""������G���2�3��}��|�����[@��?t�zuY#6������L������P�V
���(*""""""2�]g#C}�����BT���w���k�2���$\�.*"�Vl��4�<1-[T��p�g""""""s�vd�v;OT����P��mxpf���i�
B>Y36������L���l��D�'���V���tN~�o������@T�+��o��JT�I�_�	LD��
`"""""1���t��t��N�]5v�������-)Fwd���>�������=q��K����Dd�&""""2���H�!������������LiO�m���El:pC���}����!E��\{w���H�!����4STDd�&""""2c��4q�>�������4�'����k�� _\1��s]�wr�}R'����[De������c�GD��
`"""""����z�3�������LFZ����|��s���:�%*��(;wb?Uiw��G�V�""[�0Q5�V���=U�t��Sl����6HU�m=��������=����(�'�cU^�'��=XTDdK�&""""�&c�?�h����UGbZN��}.)W��R��mE��@�zPT��4n�;���6�������Az#!1-[T������DDDDDD���~O�*/��}1�Ugq��u�*��#��X{�/�&6�������Aj���D��������""""""����Q�&n��\1�}������-���v
���.*"�ElU�/��������"""""""�w���n_�!�w?�x]TDd��&""""���5������>����������:x���k��>�;�;�<8/*C��A���Dd��&""""��_��#C-sF��Uc�������J�iV_�*�2Z~����2Q��0�C�qk&*"�elU����>���������L�OW8��|[C:������l��|��f���_9.�FC��������LDDDDT�����%9�q�S��3���(�h���5��'Z4v�m���<T��!����4STDd�&""""��_��!F�z�l�F����������L%��k���/����������6���C������S}4��:�+Dd�&""""��\�#$�e��������������v����K9�*��R��~�0+��v�&[���-���@T�����gQ��`�����������.���#�Z���RDDDDDD5���\�31]T�ulV�
��tN��>�X���������+5��8u�'F����	�� 7
	�����.pp�nh�/��	8/s�\u�\�I�����J��������������t�-Rg�A��P���	]�tk�~���NHC��:S��}����5U}0<��4�v��W���@\14�gS|0�-���Q��9�F�5��3%������r�4n�/�/*"�7l�*52~Y���_���Td��O!��X,��Gg��|���8�O&�1UFf�I��������=S#e�Xtn�
��c���	}�U!1v1���s���M���KDDD�����H�~ *C���cTQ�������Q�P�
_�.����^�\�����w�|�[����)�3g�L/�ks^*������S4q����1$"""�J���m12$�.��YOTDD�J��� �xuRE����!S49w���y@�''~�,B�����T[�-����'""2�=)�����L�<�e�[<�)xpw�5x�/��7(u���#6�k�j+&��Q�z%
�.�p4f-�,Y�%_����tZ(Bj������	Z������b�<?��������1co><��o��S6��p�Xy�����pYu1k59w�Z�U���������������X��7+e����|��^�90
�g�U�����o��������BT����!?K�]0$5~�0�76�k���O������/��������?V
O�}w�����Sxo������Qt"Qp��!�-��=�����:�LD�.��E`��ZE�a��}au��;���q���H|�*�*���J���1Y)���l��|��������s���<��1�ic��?8��c��!i���//�nMD��
�Zq;?+Z��IK�B����3�S�6)q\��*-���A~h&�DDDDT6c�?wm��
��?�9�v|�A7�9��
�$Zy
�IX>_�t�"rS�WA-�W}r5F�:n���a��Q�l=��K�rEe�M���\T����k���G�*�A��q�;�D�p��vA�s0a�/�=^���n�F4���"oR�93:e�.�:cP���LDDDDe�f��(������J�u��#'`���R ���E��3������Z���$V���S�l����KDDd
G.�`��,QrQ����^P8�����y�w�����+���Qp��s������F�_�/���B�V�.oBN�C�O�m����Z%�pd�"}��3Q�$�e�n?V��}����cDDzj����Wp�)�'��"sNVi�sm�W��
S0K��u�u�_uLDDD�uW���|MT�I+[>�?����q������g�~WTDDl[��=��3���E�r���3�Y������?���H���O88h.���/�����.DDDDv�����n�9����@��6�������6��/���3���Rj�������8����yv����-�Q�*U�� _\1$e�������:� r��CT��:�������t��pY�V ��b�r�K�"�*���}b��5x��������L��Iy�HM���q���5���� """��,�����2�����$*$�\�E�f9�k!��R������
��i�V����C��s����*(�X&�o�oo���	��*��)�wwch���S/h&�s�O"2��%R� �\���m�YP�`�*"FW/X�������+�"����9s�`���{��0'3z��L%��DDDd����!F�Z4vF�z�""�c��M��h�@���m���j��g���U�m��1i�[�Q��3�I�p���-Qr���w�����k���~�G���2$����o�������({0[����
����O G����A������)���}ES�5\�z?T�3p4f-�,Y�%kcp��-���+����
V��U:����������I�FbDDd���@����U��F��
e�������������DDDD���||���vh9��7C���9�6w�����:n�����EEDd�
`K���E�s�qr�s�Q�Z�y���[�}}�����1g�}z�p�.��g�0l��!�������8Nw�������Yz�Q��0��Pm�r����wDi�OPi�����eC�f�����H��]kDUBG4x�#8���DD$��@C�����A��L����o����_��@�");���2�[6K+v??��o���v�Rm~-���j$�u#�����%$$h?�ha��B��lO�����m���uwU���i��JNN#���#�P���9�R��T�p��u�W�yk�2)g:��U���	+EQA5x��kz�����c>��>F�n��<LDD�d��G�}����9;�����oA���T�qx��Fq���W�C����n�WDEd�l!�+wr�%i�*���Zi+�%ks4�UJ,��-��A��C0����1����=�5�DDDd?>�d������&"�����������1��%""�&����4��o��:���I
�n6��}���z��<����-���4k��j�99�������������Q�kE�S�2�����k?�����G{[�Ed+�^�C�YI.�:X?�
��?[*Kx=��U��a����e����W��j+q��7k������q�p��S����1?���s��r��0/&�9�[?��k�Q}<Ee9j"�<8�w���!W4��O�il��>��%keK�]s�N.[�
LZ>���u�����#�rV3�)�,�{7�j���[4C@���bX�td�&�N�U�V�����V45�'#�B��DDD�b����6}��G�_���v���k���G�*���H�5����9mJ�x*:U|N�r���V�Q��7�l��E��E��n���Z�C��X$Up3V�����9l.=�����l%}�e1���%����;n����9�hLDDDD�d�U�����2�t��bDDD����/�HDr�����_C��m��Qe��}Ob�?����_sW"""�������*����#�z�Q���<�������q��s�8u��<<�
�����dA9�:�Gl�
���U"b���K����RN'E���Y�"O����+�a�,��BQNbXU�}��������]��������p%���kC1""""�m����G���i�����NDD���w_1���\,H���}��! =��a���}S�m�c&r�/QU]�=��UiaO��{�������xx���96m����OT&�0����m_��^�Z��-���x���(k��*f1�c��pSU�l���=�bjh?t��n�mF�&(�g@�C�3U��m�A�m�3��g��,g��E��#�{31&"""�m����������(�c�H1��@~]O1��`��c�Vep
�7���W��;���U�:��T������>m\Ee_����_�U	u����pp2�Dd�(?x�*0�6�"f�W���s ��������Rl����b��U�\�K�
�Z�����MDr�
9q���7�\�����y{�qe�
�Xg1�)D�p��m��H�f��+��a�E�����l�����v[~�@�FbDDD�5{j��_���2�����?W�cj0�vr�$s�7��~OD��>�,�DDDT9���K�rEe�M�~�>I[>��f�vh9��f�����L$�,�Gt���]jP�|Z3�J�7�0�[4�O���`L@���o���o<��&��!�����
��%��pm����&��a��](�,quJ4�O�N�w'a��(*C�^��J���"��������1j��������DDDd~9[�u�j`�����+S��f�a�
����>�o/��a��������0�}Sp�g���'{���zDDDU�����~�!EL�%�vx�����x��.*C��O�%`��L#�X4F�tEx��Y��
\����
<�����	���������S���9�./�O��������$�������J�����p�� l�E&2�x��n���"���'52�����P�^EZE`�_�g�(g:��A<�����^�iP�{A��C�a���7�EZ���*���y�F,~�*��DDDD�E��l��������,�z�c��n��*X�u���w����A���v�j5�SL46�X9S�s�B��lf����N�����U�������e��������4G37'Q���C�� I)*Cu6A��>U��^;��7��m�p|]��J���U�����l��0�N���}��.�F��[��M�h"�Z'.����Z��N���Ld��%^�7 n��h�!u}(�7�D����;w.�N
E��M���(������:;F�}(>�n�t��+_c\���������=�}�z������(7���'"""{p���{Pz�-iV�;�9"��(z�����bYwZ7i�����9w*B{{����&9;{������f���{�N�!�>�DDD����k�We3�d@�F�c�m�3.�^�
Q������P�T7J��
@��g`���	N���g)���3�����h�����}����������?j�u���L�]��K�b��X$��/���b���x��c�>����0Fcd��:q�c���-gx�����>@���0�������-���*N��ptS����������tb�e�Q&�\eb5'91�}U)8,����{DDD���H&.d��!i���})x�w���~����Px�����������^�������*��u��?
��*y��QtR���a�I
�wqK�z�Ak1�p�/Y6�k�;��6#)3��y����.����6ab����?O{�nk�.c�9�2N����a��r����!���L�D�����='"""�Yw�8s���=���Q�\�e�&�^>���!���+s5\��2Q����k���f�o���qg�mHDDD�8����'n���4�V:�W�i���_�],��W�v���������b�~�e���+.����j�-0��"p�0�	���o�N ���})(:��s(:D��]3���\�KV��@:���xJ2��&��������k?�����G�G"�l��[e�J��U���;��:Y���\f�
��U���9���0Y3�'�V|�����������(g\�����A30U�����rp��)���{+q�T��l�w�����Q�q%�s��"_��Q[u��U�����o��m�:��r�8�.z�n}����7�Ou@�^Q:b��������
��t'{`�>>(�F��!�~U�����JUT-���k����DDDDD�/��#COw��3����������?_3������*��5���H34%�Xt5k�5�A��Y���p��Eo���[��z#t�x��1yJL�ZN��55,������dM�&""""�H�y�n�zlU������wDe���aO����9��zec�g�Q|f���w������}��_��Y��&pI�^��>�]������x/�g���b�����HcO������7�nMDDDDDDe��q��[=Y�cL
���O�/IZ�+���j���d������a�7w���}���4��Q�6L��W�=��0g�o�h������bbQ@����1=�<]�[��xv@���8��_CT��&""""���/�����n$FDDDDDDd���X����G�gW���)|�����//���k���{)����HL�%��S������0����������G����\���/�b�o�����Ov�"St��2S���qx��|��&0�<;n���y�����S��:u*�����s�z�N<���7JDDDd��]�ANn���\u���:��P�d���o�p.��������/t*������!�A����L$���F����@�������^EKI+@����L�|<�T�+�/�||J�����L�����[����=K0z�������7G��I��M5���j��O@td(z{����3��������������D��[���A���n�����'�!W|G""""�~���_�+5�&0�%S��GBt$B{{������ ���X�(�s���K#�"�gx����w(���5]"""���~GbZ��I[>�5��vh2��������%�am�����2������}���MG������5g���A:vF��4N��5���� 2;y7+�l���^��	D��X������L����^^h�9��g�������������;�2�������^;���������{,������y,�C{���'G���.U�������
Q�6y���� X��P�1�����[b�Z�aG�OO�7��oYg�f��c��ix�������������7)��1Q���pn�Dx�+`>��Dv�������U��_���5��0��������C��Y�D�8������������=���U���7��M���xz`�'?�3V�^���2sVm.�s�c���s����I7/?/E�fM��t������G����P����;��;z�l(**��K�N�&��f'���%�����}���������7pQ%�G��}|��J���71&�	������X| G\��a�j�$ed�A�U�;��K�`��a6��cd�k>�e�9\�����4��y!~(����`xt��g�_���%��z�cc1""��H����^|EI�k&���I�~����"f�,�1�t�6a�5��2�.d �~���@�����.,��Gg��|�G �Q�LL�����i=��o�.�Ur	E�����������5��W�Uz�&���1����]�-k��D���b(���%F2�#_�j�M6���Ec��>;{��(%R�o�j���1�:y����N�<�=���(�(2n�p<f!B��j?�����zb���������J���o�����3Y��c������������A���L������r������]����E18�q��1X������-���s�f�%""���fk�������w�����F��)F6���Y2�O'12��o���Ob�s��L����"��w8����t��y1�qY��5a����I�.-�3$1�o!�t����q�g<��+�""""�h{R�#C����������j�>D���������bp��
	k�0�c�'7��=C�����O#f^m#8'~�����wADDDT(3G�
�^Ui��j�LU�:�b`�R�K����'}*�&U������c�R|R�I�[����/��=J������b(9��#N�������^8�>r��1f�3�cQHWx���[��]C�h�-���og���_EBDDDD�C:���E�0�&"����9�������(]�t�����E�q+���x�9�~��_DDDd��`��W�������\'i/T��b��������}�R��
�����n��b(�;�b��d�	��}��V��j��5�[��C���y�`�s�j��}��9�2��Z\#""""K�uW����oVL������'�(��=�� "K�������1�f�.�}�9	����]_���#��c�x=������"���xx��y�*j��������rV�J=0p�K��_��Z��]�A��v���^bLT3l�����f�	]&�'������DDDDT	{Rnc��/b��HL������_�;7�yUDD��~}=M��seI��4�"""�w'���O��!i2���^�S���M��F���Y�+��)Q[����3g�������D3895��UE��5��cL���T��JLDDDD6o}�
����{�����g���������t���P�����9�4���J��]���bX��x_�I|�U����s�u��E]�k>�6e|"�`�����l���w���~�\���]g�DEDDDDDD���R�W�I�6�x��c�"k�/2�_�*��K V�����U~������Cz���c������_w|p�m��a���`go��	�.l�>��R�c����DG��_�N��)W��LW�Y���M�EUq[e 3�?����MCBt$B�I9��&�F������������[8��+*C�
�=���������@nQc�$����]����X���67������#K���m87m�����f*�E!K9�:"|q,�����#B��u?|���A�����EbZN��}.)W��R����,Bnv,
EoO8�����3�#6Q�����1��Z������KDDD�$���mG2Ee����<����""�v�V#e�h�����c��N�����F���^�T�������Q�N��h""""k��~O�*/��}1"��F��X���N-�����}�xv@��E�=q
��++O���y}O��d���R��~��EOE#U�rC�����1=��v�����C��h�z�b�H��!Z����g�25u�V��i����8Vt�O�Y�>"���.�-����&""�R�	�k�*��rF���o���"""Kaw
`�������"j���b��,�WE����E!66
���^�S"<�k?������,����7q�nV��GV.��o�f�B�q'�U9��8�����������q�*OU���_Aa*�K��(���F�#���q�dg�v�D�{��I8��X�]�T�Y�X���Wbr�A3)�I��*�6�(J��1X��p�?
Q�����_�z%yP�G�k]"""*��?_��#��W<���������Y8�������~�s\����+�P~�W������&#G�a����
�w�-���������v��|��^�1#^��5�������hTn��,|?o��[�iBI'~���N�}3F�6�]�^��lQ�P'FcV����^�"�������.f����(J��/Q�t=�#~�����5{p8*�I7Kt�����]g�p���O�������^�-������WX};7�7Z��r�2��l��+R*��������h^��a�>�\����T�vDDDDD���g=1��vM�~J${�N��#���*�r�)���7�o�\Z���Q�Vb�G6.�L��P��/�h5!
;����>�� �id�������r����qd�&>�Za�r���D}�5���;���/P�'l>.�8�<&]"""�\���-��d����&0Y&�j�%c�x�%x�d�xO/�����~
{��N�>��#�xC��������Y���j����c�Fj�\�%���^A�~��Av
b&�����r����[Vg`�l�R�F�9��s&����l.�����7�zBt�0��eI\�1�-.��7b���5�4$��.&����>����A}�AL�DDD��~�=�W�/����M��eCQ�%���*����%�v����~+�/?	?1����#""""�T}���EQ�y��oWx2&����XS����������p� �kG�����j�"�kWF�U}ef�k�a�Sk�������%�x����������a�_�������*~�':�!�Z�G������!�z%V�X�w�DA��B�>��j�g�/�I�e<Yv��.�c�
\�-���4QvTQ����3��rGv�����=��?�U�pU`�����Q}=��q���d���J���<�}L�)�
~x�5�z����!S�`����ozO|��:�<vD�W�aX~�FZ��.���l�����m�����x�Nu�e�h�T%��(�	t����b�`�q�0���.�a��?�'����wr��g[������������p����Mz�P-F��=�g�����������&��^M������,��5��C�������������G~�Z��^y����P�2�}���;���K�d��/��x*6� x���,�Xv6��������[�O�U�f���5������\�I��]�S_�N����]0������b�uQ�������l�Q��d��_)*""�g_
�6�0@����A0>���YG�#�/35]�{c��Y���0������j�w�oj��-����k��aVPk���n����Z�O���a���1QG������y\:g����Nf~k��M��2h.����b(��5�z��kRt�]6���>�b�|��F}��.�}�T�5?^���+����!��>'�<��;��
<�aDd=����)�
.f�����3�o`������?������C�u��?)NT����u""""K'�[%ma&G:���B{N��>�X�����_�T��"}��N��v`�� �ts��$wpk�NA����\���sE��Q8t9	���`�$���l��~uqn�Du����g�<\<; h�j��)�D���C���O I�}'��3������kZ3<�����02
�.���l%��S�\���������d��'Y�&���-��p[>�����2��6���_�g�tKDd��`/��6	�
�����D34��D����,���9/uc
��'����8������������"m=����^�tv��������9�F�5����,5��������i���;��sTH��	���s���e86�C��We��a��Y��]F�>1���&Dl���L�bO^f*vn�@h���<v3�������c��w��_����8�q	��%��\�[��FL������'���'<w�d}�E����O�*(WOD�g��q�)�DDDv����8-���4q��gZj?�����w$VTDD���^����!.�����L�� �����Ux�x����-@�~ZE n����pDDDD�M��~�R�Oq�ws������d8o@��_�W��������fB)�^�7���(b"C�����V~K�a}���<�n�����if+��������X5������b�L{D`�]����Z��eq(J�9�gO;Fa�a�E���8����[��t����N�]5>�sMT�I+������k��}������N�qG���85]]�%
g�A����|`0/��7t't�����{�!���������-�
� ����De��+6Q�WV!8�{���f���R+#O9A��*��P���m��Xt2�����]��p��3&���������Fa�a���yqH�!>�A����y;�b�.���vKZ�p
9��g�Jg�Jg���G����<��O*&"�t��o���Ng?@v�u\�����,����+�����s\����0�UMl�FDDDD�Q��_�	LT�~����x����Y~�G����|�h�$���Zz3k��s�2���@��.�G�+�]�t2�M��s(���v\8��!~����]�0lZ~9��%V�v_���W�J�)/
�gp�r��E��V����l�~a�����������p?7	���]���./�����Pe�����'B�]���M$]�+*C�
�1�qQ��{?���+�DEDd}������Gs4��-���L���lQ�9LDDDD����bdH������<�`��e��U�S�v��e��}�G���\���cb\�\�N��9s0a�/|�\�j�v�?�L�gZ�U���d�V���8��/��6��P��k"c�"#Wj"k�W�&O� [j������Y5R���<?6��<������
�����*R�z�y�����^�D`FHO���DDDd9������X���mi�g������""�Nv�&""""[#��qZ%?���^\�kR��XeZ�cf��%��~CK�p���g�WT:�?E��&�^c0c�|��f�r����������	�e��M��?O(�<��T,�����i�gi�gih9��x�]�z��/�n�n���DD�����>C��4��&b��f������������k��WQ�Z�AO1,.�O1*�sO<+�E��C�a�?�����o�i�E*{`J\:����������B��������������s��#�3������l�g{�!���&=Z6���MEe�~�e��(��}Z���,��5����8�=<"��|j�$.u�yM �g�`,N*zw�����j�����V��
GQ�h���:�EQNbh�L��(�������L1����	�=���L��I�������SIyHZ�����q95tq~G$<�#xq��wADDD�n��,��;���4i��A-Dern��$�=���5C��EEDd�l���)��
�9��>�<v
�8MZ��]4�=|4��r���>��g�����&q�/UM'�.����!?��be�����b�����	�:x�����o��~�bX��1�aE����U�J����B�~�X�/	���Jj��
�.����X�I0��W���"������8�����.�����]���&���+��;y��6;������RT%�qD��p��X\ "�|���~	[�E�o8#�[f �}cx��������+'b�(���� x��H���h�
������������j��K9FW��������7�~-@AAy���~�:,��,��(���@Jr��p{����d|�K��Clb2�S3���2�������F����6bX1e�����7�C��m�VK�S�'��������1�v��M������s0�G'��i�.r��@��Ptk������0������m�8�������U����x�����uo�n^��
h
���m���a���Z.^����������5�%�~����;����t!�?��@��z�w�T���'~��`C8��8���Fc�� ������B1?�
7�pE�yq���a~��+DDDDT�b��f��fnNx�c#Q����`1���0��,�8��uH���s}�76k���J�!�y+��Uu
6�_�����|u0��qE�\�;~��b������I�wb^a�E^���$<>n�������`����Y��p��7p����^8A-���u/�����+�k�y�����a~���5�b�u�g�78}����>�9����_����2T�Uw�v������m6��h5t�g\�ru�,i��L�]�������'�����:u�����vi��&k�i�Ms���;�H8�����#��=f5�3��p�s������FI���������v;3�2y���I���s
���a�����k8=��:U��"�nH16��h<��������*>]�#�M��CWV�J�X%*��<��qC�,q�k��Z��o������m��m�S�N�f��_g���S��E�3pQ�c�A��b�.b,�jO7'8�����NA�����$���tq@=�����������g�1X������0�A������I�������N��� /��L��F��m�2�����//�nMDdml�,�������������Z8Q�@�*�����Q�.M��`�5��i���8{`��((/�����1�������V���w@����pC��1�d�4���h'�:��B@������j��a�����<�K�!��>>RS��	n��u�?"6N��B�Pz����Q��{��_%�/#C���c{�X�dg�1[a.h7x:6'e"E����6�&��X,�.���R�MEf���A�����6c��v�����v;��CT��=�<\+�C�
y�����}$����D��-DEDd]l�,(\;b��/p�V6.���	��%���r���0M�q��HX��/�������d^��a�r�����5�s������e��7��!��(+��Q��,�2��f!��rV�j���s���\�O�IC��g7,�qY�}d���F��(\�q�t|q��/G��	V���a���U����al��u~�*U���<��1�iS��l��|��f
r�������������8h�q�H[��d�omFj�d����I\�~�\g�r��O�~�{��m����HBB���������jKJJ�������#�V�����*{�U�&.X�^Td	�Gr��*��3r�n��&aKj�%����������U%����O�Cx���?�`e�(dH[@w������An��:�0���f����U��?���k|8�U�����+���,n��������i �~�g����;Np����c�B��s�u�\��?9������������`t����x�u��m��tm�0Y3�'�V������v���!)/-x���9a������G�Vp��O88�W��%�T_w�Z��s�\����d�x��R�i"�����l�5Q�9����f�a	�GV�f�:��n�'�W f2
�G�VK��S��cz��*�T
�B��v"�������d���^�
��&���~��#���#����q���2�?����u��Zz�l-go��K��
������d�����1?�������K9X�K��/�(��^l��+���
yx�8����.��#�y�����*�T_w�Z��s�\��n��&""""�"�����MQ�f���K���Z7k���jv�6|�"��A�,��q0�a���W�QV�W��_K��2�����_����l�����s�_b���4����#R���V����[�>��"�b�����q���B�%�����DDDDT���j��g�������m�W��Y��Y���Q?h���/�5a���������n�,	�]��Q"2)E�wq���k|��\�
Gd�A}C=�*�Dbx5��DDDDdH�(+��{��|�3��
}������#<�N�!E�'�0FTDD��
`""""�:��l=�!*C<�q�/���c�H�qOT���9a�S-De�m��3�Ee�N�&h�������DDDDdu~L��(*C���#""""""�qZu�O��!�:x�/����(?����UiR�Wj�
6�-An�#��<]�>;8��e�PDF'�|�Z|����q&�B��������Z�Chd4�r��Yi����g�v~���ADD�O�q��B���'\D�tpk�~���NH����������hD2;�������vh9��7C���De_
>��m������h��k�&"�%l�*52~Y���_���Td��O!��X,��Gg��|���X���c;�u�P��MD����Q%"vq8�{b��c������,AY�C{7#""�=j�l����!t~,S3���*$�.Fx`{x_�c&
����������>��X\FvX������j���5��z�l����Ee���D��dQ�����N��`�e)g����8�#.8{�/d
����9��Kwt]^*������S4q��)���W���0��L��;%~���R:~F�,JD��BDDDT��=���_""��FJtz����W�����)��9g
B�<4	�PN�Y�D�N��W����#�^�
�
�aJgvMv>0?���l���������S�p��Qro����^��?���=�UT��������:��q�:w�\�������}����"������
�����I��pE��^����*�Y�%K�`����z+�����e�(�>Y�8�	��D�R�����e���`�t��18���C��4�V��c�G��=lQ��q:����q����u���,�l��.��"f�&o.Y���*\>�A�y�wl>F}�����M~��=�?$E��o������/��=�"b&{e�TMv^��0��������Q����<�\]���(;w��HT�5	G�V�q����T*��S'm]�����{�~ODDU�p�Pc��i�V7���r|�)}=����D�w�a�x�<�Sxo������X�W��
��������N�����W�<�k�������6H����>m\��������vdc��%��8l{�/���}�c���(J��#�u�����F��qX���=`
���O���.���gl���W���jfv""�J�U?���=���^M�w��G����n�������s�*d	M`6��T������L1v���o�G�������Q�mR��W�j�'��k</�����~P�g�r���xo$6%�1Q-���RXNho�-DDTkT���>pb����D+O�3	�����	���o�n|�X��������.F�y!t���2�N�"`""�!~����x|���6��U��@�zPT��4n�/�/*C��f���L�
�����N3�`�0_x{������'�h�1D�>�Tyft:~�'���	C4Q���0Uc3�6�`"""�\�KDd������9r��8�����i��8T5q����{�Y4q�y�R�e|����� ���������HW^'""2������*�������@/���N��9w���:�Wk9_��{����(��#�^�=����h��KD��pmp��������s�����;�.�!������.�V��$�|+��G�_����=)��B��Nrh"""���)��WzC�����""����I}����M�~Oj�Z��p�J����U���_�1���(g�6�����z��m��m�u"""3��{6�!����n�eO`�y���?>}��V�����f��P�
��2T��)���ET��d��_"26��9�rb�����s'"H�b�i�>�I�����F���	�{6�g$w�����O#�RqX���n'�eP��1��3�,�DDDD5�����������c������9WN`�����;�49��B$���p>��d��JC�>p�o�'��'�T)p���I8��N:��������C�_�O��=�W��n��]�l���Wx�on��R>�w?�<����WM`6��\������h����al�R,]�;���|S�4�O#*<>�1v�Y���YY�V ����r���/���IF����d�vhV�I`�C1!�K������m?qS�&GI��_{>���HNn�D4�[�^46B�s�b�NM�M�	}�M���p�x����8k���B�>p�]�'�N�R�4�}��HKc�D[��7��	������pqp��������G#!���) ""{&���V�����D��>Z;i���?�������K�����f�����>��l�����q��	Bwr�)}�K��q�����!������{�K@����=J��y��8l]�aFeJ��M�4�6F1,S3Op�*"""�-����~�!i�o37'Q�;5R���O�`,.?�"�0�"u�8�~�C��n�M��>q6E��NxV7p����/����^���5������35�����J��O����j��""�{�i�F�����?�
G�>�W���^�����=�7��c�&0��Ddnv���r����Q��6�%t��?~���]�pl��x��b��vq���<;"����vgZ����]�uUs�0�WCU��""""��o�q�/QEd)gc��_�(�jr���	xI>��w}�E��x��]�k`�&OW���NS���m�C(nc�l��/���^6a����)!~�(�+����O��L%��DDd6�9jD�r]T��1�q�� �������Y��a�*�j�S6���%��`w
�l�G�JDbg?�������T<[x�P�Y�p�6��J\�����	gEeR���!7	��	�)Gq�*��""""����?�����L������H'[��F��_g��������%��]��pg6�������a�Y�����B���������+�VB�����r	�,Y��1G�q�"�f�i��]Y�I[U�"""2��GX���{��C}kwH�/�����$FUc�&0��DTS����������V����e���E[����n���@�N}�o��2��;x��)s��S��Q$sT��1��=1����_LDDDd��;~S�FGI��f#�����HG�}9V�e���A��
G���.�����Q���y
�%�Z��<�i�v�c����,y��K;_�q�6z����������������2$M�}=�~������{���l��4���%���P�!����P�l}k����7��4C�I? #:n���Pb��3X%
#~B��@����������
�������?7�Z�>?��������ce`�7�N�~
���p��0xVe(����S�����`			��-Z��~$"""��u���C���~^�vY___1������yv���G��CM�N�������7�����2���P�t�3�A?�D�8��������������@��nj4�����9Q~EX�����O�H/�����	��Oht_�G�E���������-F�+mlw�|�Mn.���0�mI���O����[x��#�7��s�s���:�n��r�o��������7ob����x���R��������B����LdJ�����;�k��#�!���P��[1������K���p]Z�Ob�qW�jF����h�^��j��C""""3��J�l�Wz����l����}����]�]T9�z4G�gU����������>�����} ^yE����#��^���H�����|�W�l;�j�J�M;�Q�=lR����F������a���j�}�.6���J��WW�k����cM����nmQq�g2��G��?����[��U|�-�y�O��������<x�`�G"�������ho+������\���4������	^��LTT��z$����V`RM�
`9e��rWkT�k�W|�(�O>�?�8�5�9^-�U�i���5XQ,��Vl��+:��,�;A
�_P�0Y3�'�V�z�.�������2���+f�C`?r�o������z�NA�Aa�2�7n 00���WY���|�%keK�]��s�W�PUe�/_M5�C�����&�@O����/Y��������N�x�g�Y(/��'�+8��>p�g�������&�V^�������Lk���F���
{�>���O?/F��P�	N]���t�:�g�Qm��p����T1�8���Y���5Uu(��I���7��!���$����O���h'�DDDD� ��=r)GT��ws�6�����b
�C��t�U�������'�v��N��X��>}�D�*N�W�����o�bT�[*�}��������B�}l;R�dI�u05��.������7��w��i��cS��n�k��KD����>���G�p��lB��p\1*�,[!�`�����QU���SC�i�h	��6Wl%nz~CM�D�����c$b_J�w��0)��zPWp0����W�����DDT�z��.6U.�B�+�K����z�U��>p"q_
�M����(qzB�*NS�����F����?�#�����';���q""�)��c�����H�?�6�/*�!5����B��.cEe���l��%��-��0�-��,�����Q�l���/���o�
k��0��pX�\n���D$'���	��vo��j8�C`/1��fxjT�_��^v�Y��Y���\l4��������DDU�7�-Mb�\��Q���bA�|��Ea���[�	��������:l/;pb���I���O
�YTI��������I9�OC��{w�����DDTu��\Gf������`D�&��Um�J�>�����O�:����&0��Dd	���@�_>�x�����a�00�'�@�����q.!�v���D(v��O&#@�-��ot}h 3�B����S�1~R��4��I�&�J�
�R��G�<�P��'W����T��=���/����bd�Q��x�����ADTQ����s}���{W��.~?����5yx��y��#?��j]
�`L�N�[�4�u�'W�m}��{�	�&��"`��.�r3{��7I�U��G��K]DADDTI?&���4��'�-���B��=)�����.OO�S�@�qk�}Hci�g�)�����k���#5~��%"K`g
`
���*.�D�s`1B{����3X%�a�xtj��z���O�W�������^��"E&2�x��n���"-W\(�F��5�7J�5�l�K�b�r����h���������${���1��+�;����A��uJ\h�Y�N3,�K������=��Ea?�U�;�_�}����^h���g�A��c����s �?9������qF��IW�A�~����*X�u�������+�?E�s���h,p*g�s��S���&�oL�tV��������h.��:7
�a�0�+]`���?5��#��K�r���
Q�6y��Ts���)����-\��@�������E�w��i\oP����%"�v�N�{�J���]���rTHV��~����c��@M��>��7�[4����>�{�w�T��;s���_�&h����~+�A�|�2V�7�@��(��y�X?�5���Dq��^���,e��g�Y1w`�U@���bdHZ��l��DEDDF�b��}Xh<�B%C$*g������9SO�c�E��#��IKM����S�����U4���{6���&NS��m�B��<���+_���fM�2h�6�O
�
�������
gFm�����<�F���'����?�W:�G����j���:
�[��]J��=N#fa���Z��4
�]F�����dY����?j�uK��2q,v�.]���b�X�	������o���7��A�G�i��-��wb����L��eW�_�D����DDDDr�[��p��-�����=�{.�t�B��t��1�6���ux�tAWC��ptS����������tb�e����9W�h�������~ol�����M��j�Fmf_{E������G|���n]DDD��V�^�]���vM��5�f��l�U�]��������������I8����I���AF����i� ����H�L�2jB�����R����&�B�q2�G�iO�\�`��$\>��!�����^������`������j�!�������������������uIJ���H��FNF���S�L=t����']�{{M>�N���C���6��4p���
���3T8.5�
2�+�|�aZ�)�IX�|+6���J��f#!���IG�����]����/�i8h�q�H���d�oMTi			����~$"�-)))��>>>��Dd�������>��[[`�OcQQuY���\f�
��U���9���0Y3�'�VU}�f��1/&U�����-�m?G�������Uil��_w�Z��s�\���WQ��9�)F���9a@�F�""""""�-��
����F���}�W�w�6��L�
`""""����K�rEe��^M�j�3"""""�/_�@����2��1g���\T�O���[!����%"�<;�Z���{���/��WpI\�������'EA��[^���V:D����(��V�.����Y��������eS�����_��o����]|~n>�t��0Y3�'�V�}���z�v\�!����m���+�������]�V���5W���p����<y�J�D������ K��KD��?H���r���&*C�v�UM���#6����6.K9����Z����
V�I��1�5c~"kU��n�]�v2lNn��b����������������n��:n��25���������r��m�����7����������;~ST��Y�l�UR�U��/����������}����+H�_��_6���������b�������<n�G|�$]����y�5@|""""����@z�Q
��!FDDT1j��b�����n���xH���e>�
&]"""��~�N�������oj!*�V�����3U�}5���b[�w_����#r�0���7G��<��3����L������w""��$��],��#�hr�0��$�e�|<&]"""�H�q��t�fr���=
�����b���%"�����B��_4~L�DDDD5*!�6W���-��ASt���,���X�Ti'����<���v��KDTs��`�&
x�QMR� �������7��_"��j�L�DDD���?_C�]��u�j�=���v���������������W�]��������y���;���PT�B{��DD��=�AL�DDD�e��,��#*C������^��]������DUW���}5��������o~>�`LDDDTC����O���+���DDTm�Otq�A����b\���-�EU���Zh���Lj���f�(_\1��/�y���]0��Yh�e.|�{,��2�W��O���bVa����{L�DDD�/W�k~���+G���G����Ml���;�m�B��
�3Na^@7��>�+9�"MDDDd.��=�Wz�������m ��B�3pj^�����+9�������c�
�g?��vM�aTQ�����.�l���C��W��A�&��f��#�F��N�p��F[�Jl�1�s����(��$$$h?<X��������h?���h?�#��k��0�=�4q��%��e�����A�V����r�&�Y�������������x�������uc&k��D���sw��?�>A��T��Q��<\���)���T��?E�6���1�9�!��u���-=w��;�n0�����b�_I^f*���+���'~%#���q:KT���qe����D���A�5%y�H���F7��KDDT=���/�^UiaO?������DD�gw
��/F!p�1�Z"""� 5�&�������Q�I����.Q��T�=�W:�W����;7Q�6��,�}m�������B����1��i����P	�]1���(��p�+"��J��YNn>���d�����C[��j�%������}�����i9j��O��.�O���tF���'��&]��<L�����������ft���9�����p4�����R���23�<���������r�}5�O�@����,����������$^"��A����7d��p���e�����I\��`	�G����,�yc�j:wZE��I����
�.����i�v�y���Y+���r��uF~������N��E����X�`����]�V���5W���-�o�
����N`������L���?&�.*C:4b����dnA�����KDDT���
������5�f6��}x�8��.���DD����i��v�Z """�m�O��:��lEi�����p���I0��,����s�pO-.����=��I�����A��{��!6��j�}5�[w�1|�����DDDDT-\�KDT�Z��>��I����f}w�&.�!�U���ox\T���_""�f_
`�'�J���7?f0&"""2�og���U8r�/��y�I}��a]""��t���,G��h��-Q��Q\�l�Y>;���-�����o�d^&""""�H�~�_��!*C��g\�KDdz]�-���������t����/'7��i������h����lGy�_�qD����KDT���`��W!.��ft
�������)�DDDD� �~�{DZ�;�'W�����Xm��x0��F����zEDDdF����������h���DT����_������
1�1�3��6�q�<����*{VL�B��l:W/_�h(���9��Rdi���HDT[RRR�}||��l���w��T���n������6X���\f�
��Tcj:wZE��
�w�A"s�
L[�DQ�u��o��.>?7L���y���Y������
QjT�.���
W������_��C���a,s0C���.Y+[z��+w�WX9��E5E������ K��KD��?H�=Y�������V��z�����=l�-���i9J�tx�I���`%�t��0Y3�'�i7�c�~���*��;+�5�y5Wl��4%�23�<����b�|v�4���[���_��z4a��������^��>�Ie�����7a���IM�����-�M��+���Y�x&"K���d/V���#�rD�W���^���H��+���t����[��)x���^`��n��d������U^���lQj����I�
�V�g\@�g���K���]�V\\>�j��`�%"K���H���^������>6���LT>�a�f�Od�����g{����K]����=l����7�|�Gwn�+%��k��K��
��qh""""�����bdHZ�;��������������y����*�E�:h�"
��/��c������,��=���%��W����������:�k�*���r�vq�o��tc	��%"�
lQ�m;"���Q���S�&�""""""�N[�k��������k&*�Wn�W��K��KDdl��}����D�1�s���d��������%��3���R�,�eI��b�/������������d���}X��M���x|~n>
i��UG��K��y���Yi�������������vh���&��i��~.cDe��efy|�%keK�]s�N�[�w������<�	��������%"""�C1Go��!i����U_nd���+:%?W�G��KDDd�2s�����DU�����_[`+�_""���DDDDTi��_�!gD��P8�f�'QM�T����������o7�i,*���/�m��-�U���L�(4<�bX/��q��UG��K�[^���V:d���^�=�����������#c��U�o�_5�;-&�B��;�@�f=�uX/&����������0Y3�'�[e`�	�����0��vhk}��b��X�`����]�V���5W���0�/Y
� M���s����ff��Kx=2�fLe1g�������d�����1?�%8���������Kr���/�E�����B��������2����]�V���5W���DDDDT)1G3���4~@�F�""""""�.9��X���m�J^�oV��k�����-���DD6�����;wj�U�Z�����s11�:,�#��i������{5���'"��$m]�swV-�"����;1�:,�.�3�������2��������zIM_i����6����}5�S6"((H��X�:��.c�{K�qg2R���������4�����Lr�/QmI�F�s��tqy�{X�q'�S��.�������W����{���k��/������~�bHDDDdo�^�W���1��q�/�UR�!�.�������!���R��<�\]����/�}���:�7n�F��w�w������9��fL���=7#""""�'�����MQj��}�������C��L�������?d>o�q��N��0��AL�DDdo�=����=�W:������/dE��.^g�����8h�q�88�^�a�o]y��X��'f���9c��v5Y�������k?������}||��������l�5Q�9��V�^��2CU�Z��ZW����r.�8��z�/��y�����uc&k��D�a��*��f���o���|�M��Y�s�B���1�����X�`����]�V���5W�����xku<Dij���b�Fb"""�\�KDd	���jD�/����C��%""��c��F�����S����{k���m��R��o<���E)�s
������-j���?F>�*�~{8���kd�8���,gR�-�q:������[�G���PE��^�����,���_5�;-/�j�������A��U�&]W/�V>��������oc8��M`&k��D5���yX��4���+����V�k]�������������!���d�l��k��i�
`��pxf�v�SVj�d�x��R�i�v��D��<���P\�������6�"Kg	�G����,�yc�j:wZE��3��6�F��]��<L����j�������IDATs�����	,gx7w���\T�����=��5�9�!��u���-=w��;msh""""2	iK4���$�wS1""""""��G����17��7��)xp9�x��>�9�j���_�>� ��,iV���g�J�����U����DDD�JL��Nt����������s�����A��dq�46����}5�]C������x�Q�v�����DD������A����L�9jD�r]T��1�q4ss�u�5^:.����/�����@��3��11�:x�h���>�Z�S�D��=�k��kM���N��T�1S)~1��{����-Q��������\��3�]�~<�������/4�	i0q�-b���\�N�__��3���T�5?^�f9}c@�F��.l�QIl���ck0�I3t�����H�,v�D�
�;7""��<�c��,���������!"""�q:��#��=����H�)[��s�n����L%]M�M�]������&��Z��o���`r&"���d"��}Q�V���\T��"�_�>!v��-((�}�6�kQ�r&z����q�����9s0g���y�Y|9����f*M�����1$"""*Aj�J
`9���6M��(�FJtz����������)�snN�Y��lq�ZL�s�DD6���;�~���)���J��Z��4��A���"""{�pm����pE��~�Te����]�K��E�����Y~��z��'a�J�U����)�������(���@�k�����q�/U�:e�W����\~�UG�V�sU�|h5�\�_��c�1��=�n����
g���~�����DD����������1��Y�$��6����(���`������t{Q��@��0�E�4\�
���q�h%.�}�i�������(:�(�|�����Hzs��S�g�x�q�/�!��E`��ZE�a��}�����;���q���H|�*�*1�}��qX�������j�OPis�i���]�Ee=��%"���\+���������!��9�=�.���=8)�U��zX�� ?4c""""i[�\�#Q�9�q��>��"""����O6�f:c���0u>��|�nW����T�����oZ*����������oN�����{��BT���_""�6�kC�-�5�tho716����/�HNC�Z�+-)�t�����1�;iV��I���������ITDDD����
qb��0�K�e)��T�Z��k�P��9���������.`r&"�>2�c���7a
H\�z���u5H>`����*�
��p�.������h���$v����:���%��vGv�!��"""�b�����Q�Io�����������qr��b\�������b��]8Y�m��s�s���`g""�s�A>�*�"�Q�|#	���o������q�����[���a�6t����p��Ud�D��\���=E���#���S�W��O��H��Dh��t� �x�C�PDn>�k��k�����e�Us�/UC��6�������6�^���^��,��b����3���>�q>!������E���������,������-�y5���MDe%���7����nq�46���86���
q�c�W��V��$�Cd���>_���Td��L��Djb,����p�9V���DDDd%����V�*��ue�Br�^��h�L���q��X���Ve	�9�{I���5/z�'0�c����LMD��q����5�4_IDD�@��z�R����8���-��Y
6���
��Y����.d:c�kQ����R�����B�\�
g��L��9s0gJ��\?/������0S�(KDDd���`��?De������AE�L'""������h��
��L���V���M9����W
�3<�B0E��s� �_:�I'�3z��L%��DD����\l:pCT�M�e]���_""�"�
1��}�M��M��6�����P����?��i������Z�|^������a��}�9�s���
x�����_o�_�RW�V���o�GqIK���1-4__�������F�n\����HDT[RRR�}||��,�g{�aO�mQ�I�W�����W��Y���������A5��s�U�\����_�$I���?��v>������	���x�C�8�h�.���
�G�L<�J?�`eEI*�����u4�g������B��u�al��p}pF��g�S-g�a�f�OT��GX��4\��h��?uo���U�\�����[��o�6��:�op��#{t�efy|�%keK�]s�N�j+g��(���k����/�bB_O�6�������2Y6{�M����Bu-�l<�
!�����W�`t���?39O���U^}\�x��R�i�t�����R�����m���c\���"kg	�G����,�yc�j:wZE��&7:hr��L��+&���X:�/<+tm���g&��gU����F��V��3��������2�\���y���UG�/���\zb��]�zX�B[�m�l��nE���?��S}q�$�23�<���������r'�&����c�>�ig)1�?�Su3�Zi2�q�tu�Pm~-���j$�u#��3Q��t��E�b;Q)���G���?�99�����?�)����Q�$''���5l�1SXp
�������=��p��6����P���u���������(@��|Q������Ec��,�C�#4�e5�/WJS{�"��y(�k%�d,s0C�&6����3���������o�V+qM��!S>FTl<������� �����$_�YxV��1a�7<�>���7�`Q�Z�U���=�z=S��_���)/������l1&"""kw�N�l�W����DDV!�r��s��0��k�|k�s�7����bYV���)r��	��]��9�ob��DT?��0����������LDTs�������_����%""�c_+�%�D�@�Rj���+Q1���-�r�v��/���c�_������M�M��������A�����U��^��;�����#.8�a�r7>���o����/&'V�.���L��e����,��!K�r�����C���#V��A��l�%��e�����A5�+��Q#%:=���\���(�|2}�����3�N">L��U.�v�/�\)Whem��x����[���U���u�R�����\����y���U�t����������C�}#�i���`��.�}6	c��B_w�Z��s�\���Vk�]?���+���V�yF.��c��8DH����s#R?%}�����)���5����a��q��)���X��5QG��b�W���������t+W��+���e������]�Q��o�O8�5����Dd����e�����h7|vo�����k��n
�D�|���B�b����j89V�����o	��DD�����F��-s����Ee��n_��/��}5������S����ux�2[*������wo$��_|,w}{6
O8�oK���c+��q�z�v}v����g�#�������7n����n�EDDd���#CR�Wj��Pc���5IT�y<���I����7"�0�"���9�}���-��;��A��t���/�HD�J��~�&�=����0�}s����od"�����b��z;Ng������{�Eao��[��#��*��_""�,�j'��6�^���A�����z/kG���7I;*��'����3����5��
gkK\�Vc�:<���3���8�s���?�������e�U�Jz-��bDDDD�*��]�,���E��\�KDdU��WtT����
�.2���|��:��>������}�Pc_r� iLv
��]��@�h'��b�����q�w)7?��W���[��L�DD��uW��	�g�H+�4��3r��o��-�*��_""�
�j�R!Y���=��.��0YuK��b���}W�������<����j�v��>~fXx��� �}�F��q�"[U�=�-�����������U���bd�Q���������-��AW��[
��]�w+���2o����@��#�H���l16���CxB��,6�}�v� }p��+�q@��=�;�����G:���=���+���;7<��1QY66���\��`������!�a���=
�����~��x��0��_=���,�8��N��
����(�JL�=C��1Y%i����3�gS�ViDDd�<,'��Q����n��4W���:l/s;f5��g-tmS���(����L{_?�g���'��KU'~����0g""��~�N��3���aO?.*�V^������DDTe��n��b�a���g���{+6������HH�(:��������!��m�[E�a�����RO��y�D�	��E`c��$+�G-�7��/�X"""�V��_k�%ODD��C}����]l�]&�td��.�M�^��2^�<���J����O���um��x����5Lz_�zm������1�xc=K����(9�_�q�DDf�|�b��w](�������#m�m�6m�����%"�*��p�~��p���m|�4	ehB�oo
[_�����tT�7�[1��T��U�D|2a�> Fa���j7�3�� OE#U\�S��L�mg��D��q�|6W\��EZ���=�����sKH"""kv����G�q�/�uj�~����?0��,-���13�w�?��j76����N�qG��C�npeU0��|�+�S52�@������Z���5M�3�9��)D������6��3��C0n�YMR6���)���o�5?o���DDvL��y�R��Z��>��YOT��B��7�P�a���������-�����#Q8/�
V=���#���������C��w�C�5���?)N����@71���z��j�)���T�y[��X���������M���5�um����:w.�N
Eo��h���Mg�Pn
���������Cbd��������DD���K�1Rd�+��A���8\v��:�0���;�h��]�^;��'�(��2e'�B�3�-*P��<����MZ"h�&o�����^h��,(uy�{6����.�i����}������uEc�����~�h�>����0�����l��������u�j�=�����KDD5��@C��EjL�d�omZ�D�@�.�I\�0,t>�O��j���C~��M1���)�&qD��x��H����}0�X3����H�����M����xU7��J ���3\��]��*Q���_�go�{v3&�0	[taU�3��l�6�Eq�J$$$h?<X��������h?���h?��#�r�rW�Z�yk`�i,*�U��z$���"s�����V�s�FJtz;fHt�5,�>��O�Fa�������=��S}�uF`�I���h���E���0��`��7tK��������-E����_���F"��%��
`}����z&��$�,6Oz����{-��{6�g�ZHpf&k��D��8��Mn���t���������"�]6k����!���d�l��k��ig+�5>��(��\��vn\���CT�	�{K76]��z�6������s�2�v������;��?��F�t��I�q:f&��W�_�3<��2/
��L$m���/�sT�i����DEDD�I��p4*������X�^8Bt97(��-�F���+�V��6���]\3���qZ%��t�I�O#fa��=��\
W/���C��"2�W�	[)&��K������c�j�0���3�=���KAf�f�i���K�r��P��J{{P�Zm�V��]���[C����������
�"YH�]��o�~�,k.�������c�W<$�\�N=g!����Z��%?O��3���Rp&%Y��%�%���_Y|�~�t�����I�I�X1�m����e�Z�������`�a���N=1+������%?O��y������~������@\1$m�<�	OQ�����R�WZ�k��T���K��������v���������v��-\�5����G��g{����t���q��C4h��OS�a�%"K�����?*��m��o��|�C����i�rd�,���
`�.6�+.��	$jr�;���A�_�!��E���2;X������Ax0�Z%�a�f�OT�g{�aO�mQ��Y�?��"����.��d���K�����lU/Y
� M�Mz�Dz�D������M�c1��Y���T]l��y������X�����N�����2YI����/Y���������r���LDDDd'�����)*Cm����KDDDDDI������EUZ���[�Nl�Qmc�����FI3������C�������,�:�k~��=�W����;7QY����R�����c���������V#��YKJ����p���2y}�c���� K�-���Rp+�-��&��]@f�Z\��V�J���}���#nM��-����������~5	?U>�b��Q`��n��d���H���
�8�%*C-s�G#�C�h��+ME��m}�p��_i������X�`����]�V���5W������.|��KX| G\����P�2Pdix��R�i�-�&�'r���
:?�@Td/,�����1��7o�WM�Nk�����]���@��n~*X	&]��<L�����\���]WDe�EQ^h�m[��[�\�(����o�0�9�!��u���-=w��;�o�,%fV��/��V�n?!����e�����dA9{�U��KDDT������5Q�6�s�l�J��%""Kcg+������\�)j
W/��H���Bqh���Dd)8��j��FIbZR���B�}�YY������_�e	�G�f�Wg��������s5Iw�{�zr!�I�^�2��K���0�5c~�_��
���KH�qO\1�����-EeY*r�/��5�X�`����]�V\\>�j�����V�I�3�����O���dCx��R�i2�=)���@���oqR�Wj�}b�lAM�N���PC9��]8.�������v�y������9��}��pU`q��vhK��T<�|3OT���[��efy|�%k�p��k��#�+��p�
��e��������>�s�B�_��'<����lG���.�m{��_""�*I������G�8�q��g[Zf���n���{�*��_""�M����B�Nw1&"""�6�����U����/FDDd;nA��b�%""+�����	��[@�y�	O�kZOT�C���V�>�����/�6�j�T�3�����:Ig�J�>W��C��Q����l�S]0��5�v5����-�O����r��KDD����^�����'Y����
o�\\��Q�W
�����>��I�������Y8y���5�W�{��r���up������%"�Zg_
`��x��p���������*������6�DD����A�t���
H������I��J�_WGq�2���-����������BDDT{�lh�[g�(s�|lLa2&"""�s!��M����bDDD���R]������DDd����>�Ie���=���WQY��4�����mz�+DDD������7|*%�<%���C���c""""""�^����O���yP������`�%""���kH�~ *C���#�O�la!*�����U{��+DDD���@C�����A��L��M'�w���<���y3)<���CF���������dTWQ��IHH�~<x��#QmIII�~����~$2�5?^EbZ��*�OW��JTdO,��H.3T����15�;�"�"����<<����s3�6��z�7`FV.�b��Q`��n��d���l�����l�5Q����%/��{��R�*t���Oq1����s�������K����������V���3�DQM?�`e�(��0�����d;Nga�����Q}<1�������L���s�U�\(1���&�F����`��n��d���l���y��wi�-��HU�	��B��r���r�����|�Zc��B��d�l��k��iw[@Y;�ME��S8:������BDDDDD�A�_��J�����.���}x�x�V���_""�T����6����(��[@[4�x&"K���d.�Y�_�gQQ��k�?uo"*�7��zdl6~eq������i9g�m�W0M�����y������b�u������i��/��NX�R�7��wP����bH������0�9�!���K����������&���KD��?H�9���c�������f��c�xS�j�%�{3���3�WM�N�\�F��d���l��K9X����I;���������R�������k9�e�'��]�V���5W���DDDDV����n�J�����l�QM��Q��?_Ui�4���o]'n�LDDV�
`""""+�t�.�����zNu��@�}H�g�����!���>��KDDDDD5)�Q���
��O^���4U�*���:�h���l�Zi��������LDDDd%�������I
����`�k���C[a�_S��MODDDDD�e��L$��o�6ss�U��H�������u��@DDd�l��3�0������;KFu-��\u��dqx�Y
��B���P���)*C���c\���"*�^����UY��o�j:wZL��l���i�/�/��DZ�s�Q����b&k��d;N��by�e�*��������m���+��T�_>w�Z��K����������V���3�D��
�?{w^eu�{�NB	�! aJ���"�8E#Z(jQpB��U�
���*b+���RQ�"X'� �(�(�H
"C��)$!$�$9YoV!{g����k_Y�	d���
����Z���������#.�
/o��4���=9�����J��PM�M�l�����0j+p�$�:O���8}R���;���WG�\�*�a�2�'����!�����;��U�kc����|v�����W��g���N�x9��Y��m�k\���/���O�{{w����o��5��^�'�x��D]x������N;+��h���������+�Q[���-�Qu���e�O��Yc��q�e�PMr��"Bl�q���U�oRav����/�]�*>��U��������`�
/o��4jkW�K�.��W�����Wuo�(�
������0Pu���e�O�m����������gp'�<���4�f�8ZY{l�(5\��g�{x�9��s��.|�?}v���d�@/��;�����3��ht�y����6���\~Jk�
�F�^j�����ige��'����3h<s��pv/r��.&nL���@C�����^����

	��gkg�x>^��yx��c�����-�K��o�f��G��+|�K�[��u�����q��>#������3�x�RAM=�l��%��YYW��KOn�'��{�����������WC�����u��[���uJ��N�T����G��N������?��m�r5��M�������S��5������'(��5vV3|v������=>��U������3 `������~�)_��R�J�}��'�������B5�n�M~w�����U��vy�F}���~y�S]��	\
�w�R�[�����\��O�R5�t����'E���������|�	}M�kB`w.:��F����^C���]������p��.|�?}v����-��&���.��'��B�����-��8�X�_�Q�*����V�/�3e����<�:�o[;kx
���l�K+����w�y��8���+zW�i4���j<��[�$%�������������~���~yz��xz?p5t��}nQ�����9Y��n����k��U�5������?�s����mvVVDh�&9Nm��l�a5F��g�{x�9�!���_�O����;+v%i|�y��<����?��w�Ql��]�^��iT�yz��E�(���5T��PM������7�>�t3���y�����>W.%���y���������>4��~����7��t9}Kv^���5f`�toag
� #U��h���|v������=>��U������3�� ��F���$�#h��_�L�;���n�_����n��7���,�@��7�_���6���9=b/������a�g��]�=)Zo��F&��4�O~��
i��������S��p�=J)mt�H��G���M���-�c��W���8�����_@ 	�e$aMx"x��\����D��p�/�j���NP_�l��������BC�t[B\��ZD�@y������r�@���|�r\vV�5���E�&v���_���NP�<�~����3}K�Vv�p��&�#���
�{����_�t�1ht+7eh��L;+����40>��p��N-mt��FP����3��8!�;�u���7�U	M�1�:; p��}5��	
/��z��&��������_�����j7`�g@������]�?7���u������vVV�f��������T5�5[?���n#��3�3�M��A#4{U�1h�|���)��Oi��1�n>��6B�=cv�U��A1{��htu`��l-�f���\|�odX��4�_*TX��k%((��J����;9��3=W:��^��b��l�S���W�AW�wu���G��`'�6K�.u�8��
�%99����G�+Pb�����f;+��1��<�8��
PW�������&���@�i���'�\�h��t���~y�]<�39�nT�����kt5��a���m���e�O���Xt��_�+��SEW��KOnmg
�[�_>���S�A��]�*���W�Xp�x�7�Nji�'*|2�N�mhxx.���+������<�v�>��.���V���
��<���.n����;}��U������t����'E������������m�7�3�����ibb�}h��W�����zz����W��g�����������]n�_���C��Q}�n���7*"Dc�5h�[�����������	M�� ��
��t�K���������3o���8��}��������I-����a��3z�{���������
����}�O��Z������t������?y��{r4��M��E��uQ'������?�f�t�m��V�k����.|�]�*���W�X��r�i�;���s3���_�q�]d�ls�x]9�d��(.�������>�?����F�����F���#4�k��i����-����L���;�jC�A[)��vT�NQv�-o�}D��"�-���zg��z������]r:��>����4���1�����l����9������O�Y>�������������_���	����\�����kkg���_��.|�]�*���W����F�w�t]���N:Ns?\_��)Z��\�z��b/���{�7��+Y�]�S�N���,���)+�h�h%����V�+����y�v�&��	��_�V=;����/����(�sW.���	�)�����6�����}f��'����;=��][7�U���Y����oD���&��)�������C4���u��c5�w���(��y�)�T��+Y�O�^�h��p��m�w��nS���������Je�
���\z��4;++2,D������e&M�IxEm~�����-��f~p��zD+���l��u%����'����'Vl����t;+�Ss��������a�	�����}G�O�V;
�=II)i���B�x�Q=��-�6M��,���%m�VM<J���i�5�/���%O*�'����(�[��3���-���D�A�������eD����/v:���sM��j������\�/���%�h��Z[R���%}n��|���������������{_W�^�i�X����f������Yy��j�6Q�vV����0H��������.en�I���j��O�f�-WU��zt�	vR}?M?I'��C���8}���JhY<-go���r����������������X��c/�N��Q�>�NO�}c���KP�����������%GU�3�x�R��rS�������yl3�Ig;��7�>�`�gW���U?�^��m��I�]�|t�j��fh�������,ju?�wO&�m������������
��.��z�N�h{��8�+�O�#��P���U�v�?���������Y��r�{�)8�V�]������p��.|�?}v�������#����k����^������������kl?M�e���^�f�v��I�������������tR��Z��������kW<+��B������6��U�j�;74^��id����7~q��>ZhH�&9N�c��o�}��fLuq�&p5t��K}nQ����r�~?�K����O
�TM;���Sw���s��m{O�zn8���8�Y�p��A�z_����[�I�N^�#G���^�k�w���B��8�}�S��������k�e]�-�������]������p��.|�?}v����-��&���.���_����^��`]pj���w�e:���������� U�/��o�+��9�5B�������.�X���@U�s�����'�&����J�k�.�U�[{��/�!l�+����N���6�8g,VM;�y��e�Xq����Z0�F5�Z8�_k�ey�������`+��>����S|�A��w;[���l��%
�i�~�������������:��h�M�zu�"]���T��#�v��u&}5s�W����#\������/m���OR��;���;��YY&��2�8��:�k���������������N��s���(���*�t��_�����u������nSI��w�z}Uy�yD�Z�U�
��Zv��:�q�����������4��>+���B�������/�E�9��R����l�\�
������+[(���_>���S�A��]�*���W�X�+I���4�X�p%<��^���bC��z��w�S�K_*��O��9����Y�%?�~�c���c�a�]P��2����5 ^������/tn�lO/>��hf���M������������M�j���'�\��4>J�7�
Ox\��z��4x������k�]N;��<����,�w�}R����T���7i�N9o����������RI�@=�j8����?��S�JY}�F���;�Y=*�W���*o�[(�V�����zz����W��g������z�7Zl����\�|#��R�����q����)vX-)�K������U������C�km�h��Bo��c�{��-	ud��)mt�`��F��/m8��j
��)m8�u��z~��Iz����W����L����������
��g7�F�~���+�S������>���M���o��@������S��KM;b��1jf�j�.v������;-����>%'P[{����F�Tbc5��i*�8[+�j
�bk�p�����G^�i�����}P	����9��S�b���-��"��9p%��^V|��&>������H^m�6.�0S��I�+eeq�q�,��-v<z~�vgK5w�?�]��L��&j�N7+��3�kfyM�z|�������������A�_~���;#)�c�2��6��[Q�����	���
�����������M��S.��7M����n��+�������v���2�h��9���q�/mt���oHyM���$g/��5��	l��/��l��p���I����[�Y=�B����)���RPa�~^_�����kl?M�%����<��������$��w��X��sG���;=Y�'�7�>K�G//���
��\�09I����4;�Ia�;�bK�.u��o��N����B=�M�r���h�[OVtx�k& P����Q��__��-z��aG����N��s�:����_q��/RV��ht7��Y�G����Y>��Uxz9i|��+m8UX���^�w���w�nJ2�xGM��G=v��;q�+W�}�0O�J)�����fE�v���������gO)|�W�R�+6^����&�T�����O���W�`[@����'(�h�k�������F�����O9�N�_P�op��u	"����#����FW��Mfp���<{��8�oQ7>i�&�����*������������GlpW�f'������x�f-|Z�������9?��g�����[����/�k�f�s�Z\�R��F+�������<�<p�@�+4���d�k���
D+7eh�������v�z�w]�h����]�P^�g�A��Z&���I�UG]5k�����b���xG���T�g�V���A�W�����u%�V�I�U��w�����u������~����~��
t��_���g+e�m��>=���A�}�����"m������zz����W��g�����
�s�igzQw��z����~�����P�v��j�����N������w�T���:��7*Q��|S7_GO�%�WPI��w��u�*��F�k��z��$��M��F�M��[p!���u���=K+4$H����1fi�x������1�������}�O���������!���5�x�gr:��8���+���j���T�N��@��f��J�V�p��!�����S	/n�'�n8��}]�z.!^c���{�������<���@}��3��h�����G���yX5$�n���	������=<����������n}�����VM�&�h�VO����������������u��v�s�?F+��8}R��*�oP���hxx.����;���}vV��S�8/��y��#O7c���7����N��s���U��/Y���������e.T�-sM�����}��'\uz�������������t=�l����<��&�����^<U9+^����!�5��zO==�{|v�����[_}g����\i����e������b��u�]���J_h}��>*����
�$���v�mH;�1�5�~/?�������[i��/��p����S'���l���g�|����vV��gK���
��k���V�I-���{�4��7m��KG���3���~����j������EO<X��X������������{;�x�-x��������h�9b��I�uu�T��7�>��4~u��~�j���'�\���{^R�t����� 4D�JN����Ta������7IN��f��J��j�q����.�3O�'�'�m�������Q�-7��1������a�2�����/���6i��[)��������Y���������s�C��g���>���wV�U\J����F���
W�IIZ���j���G�������������kW<+��B�����]�&}�&�ixx
.����ow9/w���
P]����������\
�w��VS�����w��5o�{��s��������v7��r0P�����/w����YY�[E8��������q�/�]������=>��U�������-�����4�p�+u����_#n�X�����jj��A��y��6�-��]�#�/
���}���vVV�f����X; @�
����S�NM���s�S�ci��j�p6�����fs���7"4Xc�5Z������}�m��g����RO_?M%?�'����j�&�r�p^g��F[/�e�&<��N�j�����V�l��J�����;�x3+��&>s���3 ���g[5wF��V�a!v@�h��	�p�9m���}G[�6�J[5U�}'����:����O
g���>7�,�v��������{����m���F��N�)yh�nU)��i��"�l�{f�����pe��R���R�3I�J��Un�h���V����VE�N�s�f%�����yi'������G��6T��u��K���6q��9����$����;�����:G�oW����2��I5�t���t��J(�$�\m�y�:�������{n��S�����J��a�w�����S���U�X��$��P3�R���o+e�����1vV���7oe+�>��9�����z�~;�$[[��R2�g��I����J��
�L(.VK�^�Nx�Nk���Bf�y���Y�����xQ�Ozy�e�����P��3&�w������g��������������T1�~��q�������G�z���	����}����E�������[Ti���k}Y��*��K�2e�>)|R5�t���O/��e�^�F�
�������O�+j8�
��F���v��}+��@}�����������i�)C�9[@�5�_>��]|v�����[_}g@����e]�=F-{�����J^C4�����/~=�h�Rl�\3Y�,9n�1E�k_^�-?.��!}��M��/Qq}5��YJ�%M+|0��>��2�m�k\��
�/�q��|�����=u�����k����v�V���=J����e����N�������E���w�������V�6�=Zc�/4�S�����YY!�A�������[����t�����i�����r;���mx����IJ����L=���8�{lSM������>���S			����m�q����URR���kg+���$E�
��?�����9��N������?������E�8��3�_[]�����I�t^�R��g���.|�?}vY\'\Z1�Oe����x�2Vc/�h+�����W\�-:���g����e�P����_�������������c�V������"�V����6���;Dc�^������>7�l��c�?�/��)#��|���)���:4������%��{X�B������Q�>L��m?���34��g5�~��]����-��RV<�D�?���o����(��#���U�<�T����jSz���!&�E�Z�h�w��>T��m�����1�u=[��j��s����)+�Tq��}���1�������{�f[���e��30����	������1��t�^h+���/�C��#�������=u����o~�_��E"���;���YJ�r�n��S��@ ��vP�����2���'��3 �5fL����Q�����}���6��Y�����FW���o����FW���Pt��[6�>��+S����]��"Bl�n��+��V������QK;.�M=��+w�V�G-�	�q��=��h��7�4S+�/~&��P�/�ng��:�X���41P�1B`�_�-Z[���������8S��n�V�otu����t�o�I3it�g��
��')N����'���q���nT�G�%��K���Z5���R�z�iG��q���.�N�f��y}��)?���vk{��*����(�]��(��!0�/p�V�T����S�;����BO�P�7�z�F|��oWjF���ez�!���Y��4�m�JQ7�$��Kl�N�.'��J�w���I�i�3����ltF������w�YY�<��O��3Gk�����.]t��u�����FW�>Z#:]�~K��k��;++2,D�%�9[@��*��7�r��!��Vq�+x=�t���H�zc�m���[���9�w���~K�g�un���>C`�_�D+�U�����F���w�Z���w��/W���ig��������u���Xp���i���YcGG�������W�"N? p�����8`ge��9J}��?N@y��Gj������w�������/4������3��8���s��-�^�����,��K������>�[<>,��z����?����5�h��������!��U������$t��.C`�_�hm�����?�7��\��������)mtE�������'�����*BW����j/����avp7
Sr�������5}���y����S/nS<�5U����_R�����=��v:��
��>l�;���oge�s�rK5 P�EL���m�w���������K���FW=N�X���.M���T���������F��c�������x�>;+�<�z��R7����]B����X���M�h'[��;z��#+mA�=�.�rF�Js�b;$���!��I�u�S�k�Yu�D���dj��;+�{lS���c�@u�&&�*w��)mt��;���#:������]�&��	��x�
J���$u��;Kt��}ve���O��Yy��o���avV;&��z�^����������}�&/yQ�G�qb�X;�BO�IO�+i����5w��J���"'M�Uqv��9Kk���vVVHp�n>���@��$&�*�s&/��e]�tCO�MO���N73E����6��D�^'��P�}��q���[��1vV;����������Q�������������.G^��T�cK��5�n+�����W����b�G��k_�i����F,v�������W��:!0�/PE�=t��i�e�d
)ntU��MxLK^�F���S�����>�������]Z�3���2�~op����/�%����JPP��0u���Y�rr"a��������+%gf**�������K���.]�|8p��Krr���G��Wx�
i��;�:O��}L�&9�����7�>��s����?�JY��8ZC�����u�99E}n�FW���zg�K�*9S�QQ��o��������
�a�2���~L9�Go�����D���:�*����*��"u�L5�h�@9|v����7�W���]�*���W�I�D��[p!�L�{��_�m_�)�e�Ig�<�����:��
2R���c���m����~M:��������O!0�/�!*G?_F�Tj��C�o�/�WwF��N����j�J���g���)�w��z����_�O����;rh�#���������_���oi�3W){�T���	���%N�|/������=w�A��@��O�{O�E�*D���\���]vVV��MtM��v���7��E�T��i+��e+���+��;m����	@�{���Z�-���j�,T7�]��X�o��t�w�pm�����t�)qvo��W�[�x���n�����;����]���m��>2+M�[�����~W�Y����
�_x�[@������;�X�p�)���m���e�O���5��_�cj�f�������oi+5C�[���z���>��U�����������
:o����q����;�����-���nK��k�g���������;���*�}d�x6[;W���ss������3���n���A���;�q���I���6�a��@�����u��_�+�e+e
=����
��U��;������{����_�O����;�$s��+_��YY���q+8��~\���0�A�~����6�,��1��yl3]~Jk;��C��S�����Lh�����V1Y���%;�G#_����i'�6<��[�$�����M+6f�YY���N����P������r�����	j>�	;�W���B�{������_t���~�,������s`w�"B4y�q���5e����nw�s���v�������W��g���N���"4����i��L=��V;+�{lS=��.��Z�?���Q����6�5�V1�P�7��~�,P��m�r���������s4�9�����?|�]�*���W��� ���
��;��,��|v{�_
��'�|��c�{��-�5�Up��_;��?C��o�<��uq�V�pL����I�^vT}M���#T�����
`w��n��}��Y�U)��r�B����/#������P~h__����/�_������YYm��4��6vsC0��-��"�}O;��r����J������2E��c����_w"B�u�yq5�����o�	�l���]i�������z����D�:��b�[��gkU�1� ��Ps��p��s���*4���X����G)�z[���&a�4�+M�^�C����e�3J���3�36Z�N�Q�W���3�SL�����;���/�9K�������9�����������2��_�uz�~�������{?���=9vV�9=b���fvsc0��5����UH�.v����5���:}����}��������;��_W�N��y(u������o+e����h;��_`�g��L��Ixh���]�I	�4g��������������"B4���������o)c�hd������O���3�/C�������j��
��������i�A;+�}L������������/���u�?���3����6)-#��(Ci����5�n���$�������h�g;��w���u��UdX���-��@�L��E���+NA����sd�8E�|�������/�^����������)#m����F�J]%����4��U�l���?��Is[B�s�ou���UY����8�0��/���K�}�
������MTh��T���v��xy���J8��0_�h��;����n�;+�w��o����C9�J��r���V�������l�l���m���Z��W1�:�_��m��?:��v�7,�#CNV�6Q
-�Qm~��w��ukf)����|]�����)vV�5���s�;�:�f�X��.����SW���v�����(6�N�U�����x;���;;~!3'_��L����S�5�Z
�5��`�{�)l����^��?���'�����{�3����y���q�/�R������O=��7�
�q��(mt�]�
��_�����:G���[�Y��w�/��O��P�����B{���m�$W_$s>���+v(;��
�a����9�������
�8|�\NPX�sc����~�+mt5��FW�������������{�c����L_��s��Y��O�/NY��v�����z����HZ��y�[Vo���������T���+g�+�|�v�d�Jyf{���_PX�D[�}R����U��]�tW%�m@cZ�3[��ege���s�V��5*
�D&�'� @�����6�t�\��B����
��;��,s����:_�U���}H���:cO�t���[�)�]�v��@�X�ts]����cv$z��ghw���F��"��j��������4����4��t�YY�j���"��?�F`�����f�����2E�zQ��[�
��??���791����T�/�
`�W6�>��~�gge����SKNE�O~����C���7�C�8N��Lr����W�lvGTD������D�������0����l����n>�X����3�S��$e<�
���Jy�M"u�S�p�����p���j���t���9�Go��/S��(���-�6������ ~���{�yO���uN��<���������5��*�����
��)��G��3m��,9]�����}�S+7e8a�y��r������j��:4�����7�����*#~����`U���e�W�������Y����?k+��v��}�'+?�����f.M����+;/�V*��uS]uz��U�	���
vy!�G#~��������	�g�sB`��d�*s�-���[q/������
�XPW������Y��5	��w����%������_����k�������HU�|�k���%@�37_�k����toag�8��e<7B�R~�7�C���y�����8���z�2��E�DU�|�k��t�rf���������������*�����G���?�VG?�z��+��ivR��}��'��f������:_��$'';_{���|E�����]68_��)C��MT���/w�[�~��������R�+�(�k[���CC��^��*I���S�w���I�����m���e�r����{�3kb�i���wk;s�*�o��?:/x������7�q����]�*���W������/��_c��m�?
�����:�hR��oHlwE�2�L������mG��i�A;r�*��Y�K�*B|��mY������.:�����0/[���O9�����z&(��)8�����mH�8������7��j�k�~��Y5�������������vR��c���;��a�+���t�O��@�.��]�.[)��]�U�[�o5|_�����wg�7������������}�[@�h��t����v��=}�0|��\��1��9�5��Y������"��m\k�W�������n}���Ehxx.�����R��{���Kz���~m��]��_)��{U��n+��E����)���m�=~�nT�0|��\�L�x�Vn����9�s��_������>�������W��g���N��>�������vV�9�wX�X;|W����������[)���+
Pwz�����3G��0'���9`�S�
5���WwF��^�!���|FA���!x�g�I���=�U5�;�V��r=_�U��)}�F��T�����+>������r����������l����[�gW�U�9�.��w��;+��1:1���������;F��,������g9+��[���j�#C��{�"Jz���7�>�^�I��5���}�mZ:[�
���������4���Z��L��P����������z�NE�c�����t�m����C�j��+��<w���N�\���Yo���]�.-X�fgeEE�hx_�����O������k�*[q#8D��I�������"x�����k����z�"���K��;�L��ES4:�8�^4]����PCu������H�X�0ZS���]�Z��[�ESF+�G���e�T_i6��bc��}�����<�z�	-�1�/PJ�:�����M�K�F'�6�u�g}�����p9O�O�x����?�5��Mr����y����E���)��T���V�
�T��(��m��K��u�^�F������!c���w����mT��M���N���Vf�B�����M�]}���_f�B�����w��w�.�+��5W_�S/��d�~m��=�i��U���yx�����I!�A���A{��������������k�i�N5��ia�
��z2������t���/����F�	��k��C�O�uR������������9�����������[�)4�l[��J~A7~�9\	��-)�j��G���3���m��)%��4w����2�6���M�k�kZIc��*��z�R�]�����/}�m�R�~\	6U�M�aO�0��LO2#)E��F�
o��Z���Qj�,�y����b5y�q��W�*��a'Rd�x;�`��R�g����QR�-�u���J���|���o0si��_�]�y��R1�D����3�w�S�����J�?��]�(���
i��V�[eh�c����:�[��(6����s��^5Sle����?��F��}]+f�Oo�4����;�ts����p$��&h����W���t-���������N��)�m������F�o��t����q^f|E���:���������P�����U�/U�s&iu=���������M�-NKx��w���vV5'wl�6Qeo��� k�2_�Iy��g+���>LQ��TPd���Ky[O�P����		*>��������m�l������k���w������Y���[N�|���w�����;OI�s>�������14�������C^O@C#nT.����k{v��w~����:��W?���G��\���(ip�u��=���e�x��e��U��)�+�
�;����g��|�(8D�.��yq��H�|�����^�����P���6�d��f,VM#��}�E��N�}�����F�_p���w�k���mu��H��Rf^�$��q�JY&�5��v8��x�F����>Z�l�O*G%��o��kW�&��z���y�5X���3�K���Y�m��dn�Tw�0�X��_��������5�~��_�.�Y��Wq���g�J;�����F�i]�o�N��~=�������������CG<�v\��h�/����iK�V����t���Y���^�K�S;/3>����9��_����B���w�~I{_w���QU�u�>-��*~�zD�OKg�����+6"HAAE��Xu�7T�������?@=KN��������^� �9����k���$$��s��9�|�&q���t�jG��^}�X��v�VKc���q�Jw���>�U�^��3���w7+3����!�A��������oi�3W){�T��]����e�?�^��/����7��8�^-�%M+�H]#l�>mY�/�P{���6=�0z����]%G&������4eD��^�����oP6��<����'����m����~:�V��?[���K!-;�
���?���k�����B����"_o����z�"{Wk�����N���_�?�N����w��C�R�	oK��I�;s�p��E�T�S|�[u���D���������I~�%���Jr^����f�F�����w����Q��������SO��$B`��*H����o�k�g��^�soV��((�x%	���4�j�Rk�4��
�Ul;��_�2w�����P�.�jb���;����V���j&���d[��o���+���_s�GM���FP2����G��D)��JIO��g��G��3��m{���,]ux��VM<Lsj�P4U�=��U_��
�0PU�M�����O�`+�5	S��?����� �������jjyM�bn��]vW�����S	�������p�9w���zL	�|���cB_�����������f����O8��
�1���;��rP�'Q��x�E����k��g�}�{����s��-YC�m)7Ic�,V��P�z�������k�!��E��;F����R^p�V��q��N��V�+C;�Ca
O���nR��*���3�����������m�l�t�E��]Pa;���� ;*UG?:m����k�=Hh�'�z2�x��R^�XF,��+���E����z��K�:_��?�`'�W�{Pzv�!T���I�t��!jY�ZhhA�j��E$d+�ju�2�VAD[j�G�v��������uV���<����s����T(i|���f'�>Qau��{�_��5W�?�$�G�����^o�v�E%��Uo��U9���U��/��c������=�[������ j��
����UO^���q���@]��������_oG��?����w���t�F�������5�������j����.����
�����dJ��oN������G�>�����WZ8�Q����UZz��&�V��Y���Aw%i���nU	t.���=�Q�&{~�#���
`��=+��d=�/^c���fm��Uy����<�<p�@�+4���d�k��������5���vVu'�5���N
	&F�����������[m�=s�o�so�����#�V{��wt]�K��3��J����������\����^����5��x;m���e
u���/���7k��l[)��[:[?��>u�spMG�U������/�����������_�O�]V����I#o_�o�v���;��jBC�4�_[�_x�������
����HE]�T���P������P�P�V(Ui��a������X�{X�z�z\���S��g�[c��Q~A�f.M�q�k4������I\#>!p�����;wj������������osn����e�N����B��;-V���Kz�"�E���l�2���
�<�@�i��[�����m�Q�����Vj}�Vh�����]��Qu4��!��������O�cG���;f$�h�&�G���]a�k��dG��������������=�X{�?���+�G)�+�m������*�nUf���:][7�_.���t����q^f|E��jSr��8
����I���i���r���}���y
��n+����[;,���#IO2���=nHJ�I]��Z�}��&av��=�i���m	qv����?(���vV=AE�,�Ndg��8�t9�\��j���';��O_��]v�^J�����9���Y��==�V�j���&$vTD(�*�>Y{�9g�rW�e+���6DQ��TPd���?�I�\a���/��9b�~�F������j����7u���m������}�^�����[J���fKi�e��;�bc��oE����2��Q��t[�����UH�Z�:�B��Lw/4�U
$�����L������dh�V�>��OZ�n� �)���+S�f[���eB_���6�R~r��5+?<
Q�%w���:cm���v�������.�xk�J�7ns�`������}�Fh����r�z�d.��K���,��7�X��p��&�}���vV^|�H�Xq���v�2��Z��	��O���3��EHB���{;�����47�s�7�.
�����5��"�������|uV{g�g������2�����T[)/("JQ#�QD��mH�`�Y���b�;5I���w�yJ�\���W�4�-R���[�rx�,-��>�|�R��5{���w��L��A�v���uZ�����N

� ���=e�~�sHu�m�#�9��<
�I�� �:k�6�z�P���[���&i��z��[(��M�����i:�^1WS~�����u;h��vV��S��7n��������~��0��E�:�Y����'<�^v�u�`�����y����US��w����:N|F7xz�8i|i�t�f�ot���������uEI���Y]�w�m:�o.��i�4��>�Tr�EG�{���?�N4:����n;+�$�������(����|[9Jp���l��3A��m���Z��W1�:�_���M���'�wI3��?qB�bO����G��6T������3������')���W<�FR3��s���������71�o��?����m��������!-Kw[�@z��Z0+A�-g�6��T�ZuP�
E��=��[�N�OTIf�m���s�j��W��o��za�Du�}s���z�}��y�:�U��u���HQJ|q�K��i�[���l��I��p�q~�
�_�`���v�Fp��_9EM���������P������M�3@c @-��E_|;O��t�E�����������?�H�w�<��n����_�E��a���*�nUf�������f�g���oW��7(o]���q�����)�E�
�P���=};����2S����~���j��]Eh��3&+i�c����n��e�cZ�b�������)*�~�~�&/���o�Q���70��T��Ig�h��V�3�oE�k����P�	�l�{������������t�������P��t��!��RR�.�{�Za�@
��f�������e��.;V��=��m�N�_���
�R�9����I���Ol�eD��k_��-?j��!����]�[$*N}���YI�(m�}u��q��o��7�����4K���nm�4����������)%m��;;���+7eTx�L�������|e�v����'N�;����m+�E��e_h�
��q���%ZG?���K�:_�|������|�����5�'���k�����|1+�o���"x����	n�J���CM:��8���}��}'}.|�0|Ym�WL�;#)���3&�5��Y��9���V�����a�Bb��
���7��r
��]�*���W��RPo��k��_�l�L���T�����0�:��v^%+<
�nu��;���*C��;���R����l��u�������1�u�,�_���@�X��@�[�
=���w��3����~K����	y��.q�`�2cSK�>������^����G��4��;��cg<��Q!��Y{�1{�s�'!��(����WoG�\jF^�7`L�k`��d���,����L[q��U�F��?���S�3��L���[��*�����3�u��f�*�������V�k��g������:en�<��Ve������f�g�����y��gg�g����p`x�m�r+
'&vR|;�����U��p�z���#�!�>����������k+e�s�&$vtn�
����^���U�9��l��3�V�
L�1��������
��C[pV����=	;a��o��0��p����+S�f[���eB_��h(yk?�x��
�[|�[�x[�70���w7k��C�R�	oK���n�N�s�T�����9�C�!��;�@�X�>]������3�>�����th�wvT}M������1�����T���m+e��]�����0/�V�����
�D�|0��u;h��vV��S��7�7`��d����C�����	}����\�R���V�~���^�W���l���������7�ZI��s��5���c�_@M���f�gOF������9+^Q�H��r��'��8�F[�]���r\z�����q�l�l�~Kp�vT}M���#4&�w<���J���Nlige��Ye/�jgn�8������0�������t�E�&���Q�\n��;7�m^m�����������h��[)o�i��_��t�������P�	�l��qG�����Z�-���2��	M4�����x���}��Z=AM��	��J���;�m�<�^����� _Y���l�����|F��g�
� ��t}���q���g�}6�?
-������th��R3M����]�
��84#)����l�|���l'��[��V�n�J���KM:�b+��T��4g�;+o��m��[��
������z�^g\���)���v��V����i+���#�c������[�d+���W�M���V�0����<��_sC���hH�R~rV��}������&�vV���92q��
��Cl
��&�]�)�V���5Z��:��Jd�Q��Q:��;[)���}�\v{~�T�9���*3'�V�2[>����S�����(c�����1����{S-�x���9�g����:/36����*b�u����������%�)$8�V�d�*�������V�3�=���f�gF*eV������sm��M�hBbGE�ri������;F�=���8�z/�[Q�>���g���������P������M�U �����Z�����;1����7���xn���'���/��
�� �p�Tj��T���ege�����&�9�m�������V�3g�E�:O���x�y_��W)�C��v�7�c��������f�gO�N��g)(,�V�0�����z���vV����l�����lx�e���
��m�=�������n���������;��q���kv��(�
�}��_9�c>@@!��q@s��������F��E�P�Yn�������{f�g������l��f���_��Yy�c����yk�(s���C��D��F��L"���������k��u��&�[��W�����O�`+��v��c�+4�l[�73�������I������)��{Ux(�V����O�<��0('�U�'>����|[)�l�l�~����9�����xj�7������q��~�����"���M������&�m����3f���T��Wq�
OP��7�@�!e��f����\[)��������S�@]r����J�kEBZvT����p������wFR����L�q��N����?�<�Qp�s����@`


r��@��[P�y
��,;+���&�=�FPg
���zf�oA�[t/��!�;_M���x����f�rV��4�#�k���������"5�
;a��.`p�����9\��m����@}�����?7����"AQ���f�?����oX�3[�/�^a�{��!�����+���*�Ft����(4�l[l���n��Y����zj��mg@���z�2�����m��&�OQ����+�V�L���[��*����nC��
Q����Z����7o�[)/�y+E��/�:���R3��s==�o�_u�0'���w��G�|��i�}�,���E��
i+
'&v�������X��uI�R��.��i�Bb��
`��	���[���o+e�-����@]smZ���\U�����3��	�M��==WO~����7�]��Y{�1{�m��V�i�E���u��,`�Y�kV���2��h�D;:7e�:S���?���cT��j��������l��{L�1���������1������{����
�1��f�����q7�6e��l����L�k�_u� }�2^�I?�����E������Se����������tZ��k�����y�>����V�s�����T���t}���q���g�}6�?u%���-�m��V�kw����W�iCl�����������]�mE�5b�������R^�	���A�������n��Y����zj��Vz3����le����^��W$b�u�=W!-;�
|�	}��_O��#�h[6����s<HE�ox����ab+���c�b3�����1��	���p(�'g�o�����{��mu�LE&���������B��N������K�9��
����
��u"@@r\�Vl�9��^5[>����Z+�W�gs�1�z��������c�+�[?[��)�5*/:�������w�9�g+�E^�'E�`g�
`�Y�kV�z�!��iMH���P.P;��~�=���5	S�%w+���c��5&�}��-Z���*^����x�9&���bp��F�}�-����@���2Uk�e�YY&�5��	����[�����k�*[q/$���o���~W�
|�y�����,�5�K��Ye/�jgn�8�����P}��������{��<������)sf���Q��?�0;�V�3��	M�e��I)Z�����wt�k��������
�D(j�?v� [@u�����9�w�YyCOm�������������
��z���g�y6�=�m�����]%���M�R^�����m�� _Y���l���	3��W��g�
j�?������kn��c�_5en�e<���6��{�����������/�^i�{[B�B����bL���v��ny��[i��������0~*�U������o+e�-����@M�m�3_������P������q��~�������y_���
�����:G�	�uc��$����c�+��Y:�����6��Cf��Y��==�V�j���&$vTD(��>�����g�t�V$�eGE������
|�	��q����.Rc��Y{�1{�m��~����]}�\�+�Vaa��������L��mYvV�	}M�kB`�Z
���f���W����(z�|5�;�V��~����wbb'��(�HU�����s��nyM:����5�?���g��O�������l�TG��_������	��R�+����*(,�V��L�a`OL�Q��k���kG��'���/@= ����q@s��������F��E�P5�_/(��W��
���k1v��z%�
��	������	?|����?8++�1&��A��q�`�DjF�s��9��������Le�v����
��m���5M��s/8��-�,KN�4������3�����c*�{_��c�!P?��9�=��Ve���JYf;6��3PU�M�������v���g_����x�e��������D��uc��[+|p0��5j6d����gV������sm�,s3�d;6�R�:����+72Rm�=������l��b��I)w2}�9��	�OY����Cy���E^�'E�`gP?����� �p'7e��l����L�k�_sS�LA�ve�p�~:�	�=1g�5��Ag����([��X��@���Y�k��Y����x���cp��F�}�-������O��?�������f�g�2y?|�l�|h���^���=v��Ob+�'�wf;��x
�C�%���9&{�T�7�C�����h8����FSO*��b|���9���.�#C��{l���0�;����fk������<�?g�������F��E���9����p�����6#b�u�=W!-;�
����j�#C��{�"l��A�����K7��:��}�������#^P�:�6Q7<�H�o�w��0���lQ���V�2�������&�uv����5�
;a����{%�����/��W�^���:�8T�/Z���J���LY�ESF+��X]4}�<�+4���<��_OO�����@E�����5g�U$8��������q�**V���v�zv:QC�_��w�p�������ht�q��h�V�i�Y���w��_�Jm���qs?��#�2���C�7T���;�?lO�uV�V����+������=q����(4�l[@C"�B{�����IG4�u�����'��l���6��h��w���C��M�S5
�w���GV*�V����17f2s���e�|6[?�+��9��u�s�oE�M�c�+�[?[�".%�N�IxEmS�����-�7��!��u��2?�S�=��u�p����M��SN��d�������5vHon����T�2>�>��������<�=FHp��|aG�h)'��[��~�����}�����)���F�er~zN�O�V;�{EM�7itRIG�������oj������m�}�T���?������������f���A�N��M4!���t>�NAF�2��Q�GO;A�'AM�y�����)E��*s%���?��������o�pFQ���-�6E[�~J��
�rW��a/��C�u��{�����V��������m����G5c��JK�E�'�>,o�6X�^K�3�w�������L�{[B�~�*H�/���u��*�i���f)$����1pw�k����_u��cT�����%zl�r;��q�hB�X���#4V}�x[�f���z���h������Z�-���2��	M�cn��v�\�V��{��]�����j[�*2���q:�q�[��(�l���>w��U3u��|�>��V
g��O�t�K%������/�+�V����[���J��������v���3+��q)c�(����Ny!��(����W4.`o�J������9���>��")o?�J�c�MHhi'GU�Q�����#���yt�����O��?z�X�l�l�F�1�z�lf;�����W��lf����lx�e���
����r���&�e���R����
��LH�����(=^�p���4�u��+_X?�q�����f��&L)zWk�|-[c���3��Y��+�e+��:�X�iuP�/�V����Z������5�?��7*��V���=;��;?P��Bu������:���,�c���)���
���n+i�wi�b`h,�v���;������Q�n�v�@���-��*e/����f�`�2cS���0�?5D�_/��=������l�l���R?_����kP�
��_v�w�3�x�l����Z��.;�_p�*���;]���c���T�a�-J�_OG�#�h�-��������V�s�����x�F����>Z�l��Q�z��������^6�t���7�X}����{���+���1�h
�@@#H��s��5���c�_#�d���,��������)t��0��F�;�������
�?�V�.���6����[�v������s�GZS����������U||����.����R��������wl�A��@O~����wP�g�oA�[-/��A��q���"m���+D��{����uG������MUz�_u�j���K�d^�V[��0���'>����|[)�l�l�~F�1+����j(8D���u�LGW�7�l��#���S��S�JN��Q�Y��{�F.�J?����e��[�H�:-+9�X������
�e�s������R��[kP�e�Sa���25�r�sM	�B��B���{���4�x�"u�������tk���j{���8F������`h0f��Y�����M�hBbGE���y�1[<g/~��j&�eGE������
�����
��V���1G<��u���z�R)�_��X�J��b���)	������.:��.�X�����0�������
�D����c���������zO~���q^K�5��
��U;4��+S�f[���eB_���'o��n�\����(z�|5�;�V���4�v��S��S��m8�}K�M���o��q��Q�uN��
�<\:siJ���UM�U�����P���y��9x�����J���\M��T����z�G����m����L��|gG��$�5��A�i���L����I��X������Z���ix�f�;X���6&�����o6{~�p@�V����x@*p��s|��	�8�F[��"����q@s��������F��qk1����U_A�n;5�WI�O�������J�9K7v�S�K���+7e�Jy}�F��C�U�����Fp�s�����/����JPP�����6j�Y�5zy�l�'�z2�x\g���%?t�,m��fU��&i|�y�fgu����-]������;��{�P/~��l�-��� 
�-�l��oSp�;����V�;t���u=z������}'}n5l�����Vq�9@�6|����&��y�
�
��p6���U�]}5�����~�X�3�+�-��1��Hl��W)�o�l����)����R^a�e�u��:�j+�������������=q}����K��K��-���<H��
�o��5[���"�P���T�k��������/p����.���i��J�����%��1��J��8��������'5Z\���[��t�@cINNv�V�4������j��,[)�E�&zd�q�W�������SU��n+�����jz��v�@R��G�7c��xG���T�g�V���A�W��l����I��8C�?(9G5\�'%i�_�{U�K?c�W�z��;���1��+}�������u�L��v�����
|�]�*>��U���e0j-�Y+;*�:�|;�Pj�~�C)L!,<�Z�{������o������x��-����`MH�H��
2R�����z��Z��AM�v� ;�E7Si�����5�J+m8V���������=��#��(%��V_xY����,��5���������iV����;��$.^}�P+�+�+�����t��t�C@�-KN�=o��y_���MNl^f����O�7��������!��~�H���J����J����!���P�_�pj}�N�+m8ur�������*�7�j�F��sx7�y{����xEW�a�_�ox�������T��;[)�\/F�2��FFH�vS;�������H�����NRW;T���)z~�ve��~�a��m��[��!��oW��[t���0�d�Q��v������u����6���j
��(m8uR���|_��>�K}�OTR���Q�zj�J=���
�%�[|�S��o|�,����
S=���I�S���f�g�.�@z������R_$g��g?��Im�=A<�	����-�������&F�)�W��W�W�nZe��5�x���<�7�>G&�S��g���8�P_BuRi���_$�����ot��ls�N�Q�Y��7�.���q�,�U�Qz{����7�>���]��}�jg��j�[�o�_�{6�JyN�[t�H����J[�5l�K/=�v��@�V������n|x_-@e���_�hp�t�)���&���x�&e/����l[u�9����=z�Z�����sX�G�u^flj�c_U������Y�t��|�Y�]q��o��������GUS}��+y��
���v�{���n�~��u��>&�������}�����������;a��o����H[�/#0q��jd��,�WS�����\Z��5��+����$��j[�[�9T����Y�k�����0�~?���g��C[�E�C�����?
�Z���	���{��B1;/365�n4���[�p���I��q�yJ,m8�@m��~��I�k���m�;���%Q�����Ya�{R�F��c�t���*�����E����� �/4�~$i����5[m������p/;��i��j�;[���+M��&�����5��t���n��;��M���Y������e��
���{&����"O`u��T���u���6X}����eN������u�����n��p&�?���Y�������ui���k����	���1%�����]a�{��ot���Ek�;���
�D��g�N�N�s�f%���s7j�����C�n���s�P��I�OLR�����6q��9'���'��x7���#�#�L�����Bf���7:�97�Xr{�z��Z0+���4r�q������o(�7��MCO�S��'^]�m���s�j�q�����G�&���Er�4:>�����Y�Sj�N�����(�����3s���;�K��0���O����?��(��n~�[����m����5�����]���J�����������3u�l�l�{6����"!�����y�
��&a�
��
U�������Q���S�-�7�����e6.�8�����u���n�7��Zl���2;U�^��������<]g�8��y���8PE�k_^�-?.��!}��MI�\�����{g)��4���?�/�B\��@��vQ�����)������W[u���M����&�wD��k_��-?j��!��������N�r�f%�����������������==WS?��1��<�
]���v�Fp�s�����+��������hu���j[�t��u����Wh,y�gm�W�s����+z��3�:��U:�h�
����gf��fC��������d�k�=���/j���>��~���w������![)����tn��vV^PX�����m+����*>�������5�{|v�����[_}'+����B�\�R��74$H}��b�0�FaN�����2��Ri�kn�E&�S��	p��.��o�
4j��J�����xu���C@37e]�Y�7�����'V�cJ���or��L���J9_�f+��v����G�s�����b���7�0G7�����������R�-�R����
�0u�l���������R}'�5�E'p�/3�~�v��/����T[u/("J������WpL{[@ 1+|��q���Mw����2�7W��_���g�����=�^��l�<s}u�,��v�`���4��_���g+�y8��a�}���&&vRHp^-o��O�\������|���~W�
���t���/���N�������e��IS���"*����������9R$���-��������-;/�V���*B�������:G�e�P�e��6y�q��W+�_U��GY����
��m�=�����I���)G��U��KS�����G����;RV�f[)�l�lV����x�����Nsn���Jy�;E���u�qm�����5����>�7���M�3}�Y���Pg�oe�N��q�Ux��l��<D�|�~;����vh�����8���g05��*p��z���������5nPE�����1���s~�y������p��~�yE��*���y���vV5]r78+������
�o����H[@ �N45���!=�x�s.�'f+���kp��m��O�W�i������3[����mq�B���rSf��}6g����h�����l~�)8�V����m�r5��M��v�V�3�}�|aG�i�?��� }�2����w����l[u���k��5���s#9��u�9�w`���i�t�-��e��vv���6E��9�O�������w���5����wW��V�k�,T�.���:4����|�,��s��k�*[�,���jq�
�?�V�bG>L���2���
�7O�d�t�`�2cS�r�����s�y��9x���T�������:g�z��uS=tEWu8&�V��w���nR��Tx(�V��i���W�%ws�*4r�L]��y5-�xg��C��7��m(F@%�
5��T�Y��{��k�����Z4mb+�y�:��O��J���-z���9g��v�c�@y�c�:+O?��V�������k�~�F@�j��o��?���.=���8��s�/�C������'6ApEBZwQ���8NAM�l(��v�p�w���&����0�V%
�� �p��<����U�l����B��t���u����_g�x6[=�����
Q��?�����I�^�������1�l��f�>)�������(�76���}q�zbV�NL����1�_g�y�x��Y���W���W�[�9�	���lH;�������&�j�5=�aG�{�e��L��|�����a���]ub\3[�/+��V��W�7)��������l�l�_�Y��^�������Z�R~�#�=`����{5m�6�\.O��E��]�E�c�m���i��?s�r�z��U�f�����*b�u��@��
5��T=�l�30E���9�wh��;+�1��[���Y-�6�����LX4I�snQA�v[u/(,R�����Q/*�u[<3�>��V���[)�9��U_����p��Jn�|�n���wE���-��BC�l����3g�o���l����}����(�����@�lO�����&���e+����+�����/�}O;-((��
0 ��=�������)�1�4��X[��*�NW���*��;U��j��ED���I���y���U�b?�(�1~uB��upm�������kQO���)��Av�GX�v��on��}�o�����}w���-l�*o��O��>��B��vV�����V��}��^=��e���J��,��;RL�^[������HT��Vn���w7k��C�R^��p=��.�oi+�Ef�o��?+�����	��Q�+�(�����V���j�g�5�����H!*����5r�L���j��v�������0 ����nM�x�s��'&�5��	���������uI����J/���
��h+@��C��.�����?l�� [��=��3���B��<��sd�8E�|F
�������Q�T�k_���{���q�}6�?�7�oW��1:�h�
s2m�=�����'������@U��d&��I�v��R���:�����h+�E��Z1w.t�w���\����Z��W1�:�_T0  �3��Y\���?���bu�9�d+�59_����um��V<3g���~��5�:�������2]�R�������U�C��R�Y��l�$E^r��[vT�sovL����y���q�/����R3�4��_�cJ���K�������wk[������2^�I���]�y���^pL{E]?���e�@���z���h��ce�_��vO�������5�E��D�����������j{z�����i�3���w�����|�,��2���m��=3[��U�����
P5&���4E�)��	|o�5�	�+��c/g[�&q��
P��~k��=�x��<d+���	����*�]���[d�*g�+�z�.�O�������F�����~?���CyN��u^���-�����7����������J���vh��u���H?m�sMjV������l3����_h+���L����D��
�E����s|�OU��%Nl^flj�{������F������ 8D�+^e��[�nC�A���m�}�VJ�<��	M�Q�5i�K�S��t�@}"����B�[�i`|�&&vRD(�z��7��E�T��i+��3~]��;�?W$�]|����q
jf�@���������B�o:�>Wz���3~�0[�w�~#3'_���~K�#]}z�n>��B��l�������=;���5M��Z�:�9c
�.�p�k_�9����	|o�=]��/P�
l�<s�ot�5ih�>��?`�_�������U�wf�Jyf���:���[�
����9{�vVs&�m1v���j"�U�iK����w�J�V��t���tJ�J[q/���e���nk+@� ��u;8��	�=i�������N�e+�6yk?�p����-��V�f������
TOjF�&��I����6���Q�������V�q�K���a�!@� ��e��zt�g�gO:���������
���-��Q�7;F�c_U���X��;999:t������y�����������wuk�cj^������E�|���h$�����4=�l�s6�''uh�I�uU�f��ouh�vT!a
i��N�/?����=�X�=ZK�,!������}��I�K��y^������_�M�-s�������s\�����Mo�.��.8���|aG��_x/��s�gsT���V���w����wk������t�1�h���z��7���j�<P:g��u���1�{5>�o�w`���v� E��/���h+@���8���?xH�.��3l���� ]f;�e��N�����Q�c(����B��)*�$�x;������]�vv&eeei��y2d���i�+�����:��1�}��_���h�r�5q������V�Q�������)(,�������
0�glO�u��Z�3�V�3�}�
�����wr��e�|��O�����P��N����iG�'�
��.X��Yl�`
���f�0����������J���+*���g����P�so��;|��)����I�2]�R^��M�����w�([��0!o�7���������r���~�����9[���N<�D;��lm��6g�3��v�&��m��J������eF4g����������]}�<���m+�� x������[���|�k��zd�q��*�V�
������*}�E:�������~���-��
8�Y�n���j:�%K�8ap��u��gk��i��@�����iK���/�j�;S������	}M��5(�0�k�����4�������:G��K:;+���w�W�h�?.��Og�0;�~�rA������v����������������}�Q'���������tU}���z��'���?�
x\����4��1��/�9����.�?�h�{6�>s�/�0�+������y:�"��j�;������FV���uI��s��?;\y��W��}�6���S�;���?V�%wWxS�����q��L��L����<����a�z���+��R��{��u��_~���o�[�kM
6L�W���A������!M~�W-�P�L�~�i|�����V�3����~��}��Ox���"v\+AAAvT��~4PmK�.u�8��
����3S?��M��Jy!�A��;�����ECrr���G��WT����o�T�W�)�V[���������6DA�1�Z� }�r�O����P�ON�I��
i��9��-���	��G�
�w������4o�<m���Yuk^������]���*eV7m����:x���4i�|�l�\��7��O?�n��V�����3}��/L�q$s����^����m�������'[�����+�U|v����7���=>��U�����>�~���]�������h����gV���Q'�5�����&��Y��rW����L[��F�q��z%��h 4���� �$����+�����CCC���}��9A��~���8?�h?���N<�D����Z[8�q��?��t���@B?\���z��������m^���w�P|�����9r���S�=�����
|�]�A\=|v�����[_}.�e���mY����
��6Q����]}"��W�6����R�������op��z&(z�����<��|1�/��<�	jM�k�l2d�����uoV�z���]�kx:��$���9���z�!}��g��h�I���m���������Wi�1�:���_�`�W����z����n��{lSM�������
�9�������x�&��]���"("���9f��|�j�������;��v�{�9M�8Q�w���)����/�V7��i\�s���0��<��@ 2=��m�����o����s�Cj�_~+�AM����+2q,�'U~A��~��y��'}�F������)72�Y�����O�HY��{�,��0g�E����{����~�p��W***J�{��>�l�3FS�N��z�i[fO����k����O>Y]t�n��v����>�H;v�p�\�[�n�WOn��V�^����T�2]��B�l.�K�9���������9�Z^pt[E�z�x��G�y2��m����\����8��s�/F��_u��G������id�����Y�kV���c�������l���Y���3�h������K��w����e�������R�9g�X�~���.�m���BB��(�����x�b'�5!��A���];�]�+���\�h�f�������*x��8�������J����e�T%f�i+��v���[��I����&��{���.�#C��{l�s��yEw�������M��L��5S{�}���d�2�s��C����[<��[�i�a���
���_(��;��P�~�@����w*a��=�b�;�9�����vy��f������sN����'M�6M����V�Ze�DY�V�z
t=��b����rV����KZ�b�v����&M�d�D������~�AW\q��xW�Z-zd��u�UDIo�A�����K7��;�b����������K��5��O����h��C�R������Q��kl�����)���
n��V�E��\�z����t�P��h�6�*}J53e�M���bu����x}\udh��������i�A=��&m����[dX���I�������~�H���J�/�.����w*������	�;����������J�W_}�	L�n��y
t7n�hGey�R��[@���&�5[7�s�)������#�m��J��0�S|m��6+��\%�\J~�Z��t����H+7���N73E+M����{�t���F�H���+y��=����!��G��q���Mw����2cS��?O������m/w������:!�G�!jv��jv�}<��A��\���x����F����M�!{���{��n�T�:U}pg
zd�2l�v~�7��!40s��w7�{2�Hm��4��.�yl3[A}0�:��Y�|��}H�iU?�0$��s���M��J	@��-���W;+k���N������	�+��A$O���-�+
��������Z�������}��������gBn,��~�6�������:��h�M_������E}��c5�w����X�w���G��N�z��I�k�h%N�Q��%���7~qB���2�0������zc�m��������[t
kv�	?m������5�/���%]cx��z�R�]��>�Gg,��)[��S�*>91W������Ag��Z}����'���BV��d��;PC����yR����I|�HM��������C]���^Yo<����D?����t������LE�|F-n���M^P�0�*g�b���{u��W�w�����r�l~�����(�S����@����%���5���a��0����v����P�>}������/N���kcs%���F'~�9����%�[-�Q��>:C�M����Rbq�����k���j��s��o�Ozn�`M�j��w3����e����o+������wOs���s~����&{�
�?�K�=6n��t���4�O�Bm��>w��U3��V=~��~���s�o��R4�4�Y'��f�b��|���Zn[�#
��B�]�YQl�V�
���.I�sn��g�+����|��	yM�����N�kB`������Q,p��u���>���g���;eee9���������~��:y���:x�������_��)S���[��w�S�&M���dh�c�t���X&�Ql�FW�}����f�p���}�O���_W�g����jK�Y��|�~;����R�;��^����.��2E�z�]l���I��O����q�MHhi'GU�Q���6v��>�[i�5�ici#�7����1�TEgrm����?��������������q
	�����l��xE��U��?�����aG27�"/�������9�u��}������K��gO5m�T�z�rV���1����<�&8v�S���	t;t����5!�<�l�l�i���y�����ti��)����1J��6���6�n���Ki������h�j���b��y_��Y��<�Fw>�c],�Q�%w���I�d�F�(R����v,]q� ���[��u�m%
�.�X\�8C�_���p�{|;����L��n��)��.��&�50�NA�ve/��������{��W�����v����oTPd�� P���;�5�sw|�AM�4�~�,����=z����\>�9�� >Z���S����^�E�	~z���[6���~�@�;�+����]���6�tg,VM;��z_W�*����:�~�>�,�u��U}xx�.����2�l���W3��M���o)w8/36�V��t~���y�M���(�\�F]?S����7�-{���W��+_����+���A�hM-���Y��o�����@-T�L.�;>�E�&���f�g��C��S�kw)}������{��2�!
����Q/*���v��N
XL��Ms�1:���5|�p=����^�k~N���G2���U��x
�o���7NO?����;�������nS����Nw�GZS�F�����)�k�+���QJ|�[}���:�[w��#���An��t��{4t�<����	����M���	�<}�BT`�Fy!�����y
��� ��a�F�n��G���aB;�R_;����b����Z}�����G���t�}���b#�T���U�~Cu��_j{����Q�3���>&\���.�oi+�1s��(��#���M�[���UEPD�"�_��q�U��O�I�S�w�b���Z�t��{�9�?^�V����w��������h�g���L��ieO���rJ�k��05w����������TPCLC�%JQ��*��{��3-������\��_������~A�/-+SoZ�%���
���
��0���sxf��03���������9�����=��<��������_�K�3��lC��X���Z���.����A=�!�X6VM��9�4������`��O�����������1��Z�mr	��c�2d�iCLE�DcY��^}h��.d����al�U\|m�����lgr�� c�2�z�'��������"�b���e�]�:���h�y��B�����9.�����OCQ�!�H��7�@��I����O^#"�n�z�4m����7n����U�Va��=V���ywm%t���NY3g�G�s�=�`����p�yyyHMM�< � ���4-�EK�]�79��R�[~�[�����|���F���r)U�����-���j�Q�/���M�j��S""""�Z��Y�'dM�Q���]���BV�"���r�*��S8%��<S�L����T����/��S�&"���\�h����7G��^�U7����o��;}q}���u��|�t�����[o�R����LDD�E$p�����c���{�U�AAA8w��\��������	`�a�Eo������um
�<h� <���������������E���X���������������S��S�k������A�����c�""""���	�Jp5�0��\+�[��F��9��!�r6~_�!f����.�����+�"�j8�y�C��������������uk�z5?�"g�����E/��{CP�k	��B�H)����B����s���~9�/�����$��^��'OVKRR6o�lL�:��5e+lk���4Y3���/c����q����799Y���=z���y(s��J�[Y�K��k�n��>�SP�b�����%DDDDDU�U�
�91QQ
����bB�V�vf�u��[ap�\T��q���+�DT���r���U{r�[�Y���5�~�
.��W���e�.��kiD����e���0�}�"ti��#�����z��J��z�(�y��}����yyEO��-[���n��]�a�]=z3l{���� !!AM�>����rKb�`1t` �I�{4�;,k���A������&EQ�"���O�s�����do.X��H��Vc�<6�qt�_b�'��<�JX���X�(G��B�'�ek�[�G����a�����8�%Qyxw�M��v��CB����4 c����
��%�����8���P-�
j��F@�ZT+��k8��^�~���P��y����<��y��'�;��C�~���Q��	�z�j����1CM[�����O>�D�4S�N��Y�dK������Z:t(���{�����"�[�F
��sTt��8�	I��j,�#�^H<�cJt�2�j�.g�Jx^�����\��=3���~<��\���V���8�C�"""""_��MY�^�w�0�>p<F�:�?�b`��������3�p���?@M��JSw��������W����>G����`�����S����:#��i�<p���e�����|��G�����o���}����;�`���r
s����N�������9[=�E�\QD����^�7�|���T|���r
su�����/�>o|s���������\��� w�S�7����K=�������q�(��S�J<����eb������������p����?C����F���������"�[�j�D���!������\JDT�8	�pwle���W�b�]Q���qqqjR��������zK�4b(�w�}W�4b��c���f��AX�l�li�����{�li��������������S��8�u�'�K�f%<�����xx��Ll9z��]��N��.u��l����
y+����������}�=�}Hp�����+�MY�++'dU��z���Z5����J�����Mg0���eN��W�CL� ���]����n��6D��/����8��_"�r4o�<��UK����G��i�����-2d!*����"ql���k���bn���s��Q�����N����/y��:�"]�t�����J�[Y�Kn���l5�+p�B^����Q��pe�B��bg2e��+�pFV��h��u��K8w�����Cc""�n�R���E��wG�!��\Z6C�������N��;���j��G���R���>
��!�""r���v��u���0q�D8���S��4i�D���J��j�J��9����k��j��Q�FX�`&M���	d�L�/QZ��4�]\�]tv)����%w��������yA��������lU]LW����.�lEZqOu�r���CY)�Z��3N|� �j5@��M�����*��w�&�/4��N�IT�����W�����p���n��3��wh [UG�l����R��:�]�����B��������lC6�?�%L�Z�
}���K/����]�r����^�[kl%t�]_$t�������7//�v��W_�	����&�,lu,��V-�E'W�J{^r�}����-gex obs]���_�sd"�*N�j�U5LW�N�o��c'�����mG~[%k@���J8�����u�����d��C��C���#b;6�u"�EWn��n���N_*�Km��a ��l�@�N����wo��q�Q�Z�:�V�
Q3����
~���}C���cV��.�""2%F��yEB7>>�[�V�l1b�\����{���5s��"a\TT$[���5�+����{���1c���o������9|�5k&�yt�]����R#�#������.��*�y������~9��>$����K����#������Q��p�h��C�e�������m�����x!�8��s���p��������R���a��s�kJ��O{Y'"�".���{^��0��=�kV��������{4��!�zw��=
���E�������_;6�R����C���?qyn������V�j�OB����"��I$x7o��%K��%�DRU$EX��5��u6�+��Z�600-lk�WoJJ
.]���g�b��MHLL���S����|U��Ca�t��+������0F�/�kS%9�������5�Yu��[j������v\�F|�?��(9DDDDD��$<~<F����07��lX�a���0��v|
��������"d��Z�L�����
��8%[���`�������iK���]���/�<�
k����oL���cPt#L���o�Q�������^@������!��D��o�\/���5�u�5"������x�����[7�S��{��x�����D�^���d/�k���a��<��w���+z��9xE��VbX�/zU)�����676#�}�����5�]��z^r��;f'�TG �\���_�Z�[S-���N�R-��ZD]�<~1{=��/�	&�+K�8Lz��l�����_y��G�.���!f��n&0�f6e������!�k����o� c�<�E����|_61�����X��/1k@����������a�i�N�g.�>�s��Z����s�m
��A�\�5�������znf[�	������5""�q��i�[�N��;q�D���H�._��w�6K���H�Z�lB������X�B��W��+��}�����XM<U]
7�-#�y�����]d���������"&�Q�]-��������^9</�1�����2� "�;����7�V��AO��}'����LJV������%""""��	�J��N//Ab��K� 6G�fq5u*��0w�7G��)(�9�KL�/Xm��A_����6�}�;<�!aw
���
�C C���g ��G^�$"/���`y�y���(�e�>�H����H��$0E������q������4��6�n�G�CeW�C�""�~={�T�����@��}�!����g3�k+Akk�eg�qqq:t�:,�W_}�m��!''O>��\��\���e,I��G�B2D���7JoN}C�
G�n��b�7#'�������n�=/9���gp��5��P���D&y�������pe�o�1?���OD� ��\�/��O���eH���)��cf
v�w��m��
l�}!��4<krR������'���o�';���}��b�X�[���G�,�y�=�a���[�Y���9��7��#Q�k	�|���-��������a�E�������2�,z�Z����K�����5s��o�����^|�E|����5k������5j$%"���������'`9�2��K}�����,5G����t{�D��w��x�����!��-G���v7�1�KDDDDTN��l��1|�a�<�3� 2����������c��6��[�����c���8�tF��Bx�|����������=G�#�zY���0k��:��K��6���-�t���]���l���q��p}���y��|�A�����7jD%,�dw�.[DD�I��	_��}����D�H;��uv������=z�|�M|��7��k.]���g�b��1r
"�8�h?|�<��3#&2�x��>�E��iHL9��m��|�qe=/9bC�eu$"!� ���f���fm�>��_"""""�)z�^&b>Kn�4��6l�����Ue�������}J��+��UOto�^���%US��{P�g���U
�����o�5����Pj�M�6���-n�}��6����������y����������xd��={���T�Y6�K���r-�H�������_�]ki��i�={�li&M��9s���&))	c��U�b�]��B?���x�����T�Tt��8���'��b$"qC���W$��~��D�7h��q{������W�<�]�a��M�9�u�w�[���[^q'{�����S��w�;2�s�;`�#�U6�+�x��k���;r?}�-s:�[-�6��}!��AO������_���������I���bQ-��ZD],����b����EAA>ls�����1b���]�d���z���s����%k�lm��X�v-N�:�7n`���j2��_""����|���i&�����*�DD>����N���=��<�\j���u0k�x�G�*7��:�������8\���|��O�G#.R�D�H����H�D�-j���1����2)Y-�.�q�;"r�u��a���8p Z�l�N�:a��AV{�
���J����B��u��kW5���k�����S�[�������5�K����\��S�����B���x6g���o��L��	�DD>B\T�vW6�-=����R������05�v���?^������������^�.c�|�qb��:�g ��5�O1�3QE�|��:�����C}Z��};�����+W��	m�r[	]C��Ri	]K��I_1'o^^�:G���W��kmxi""�~">��|Wn���	Yo��-#�T��:���/��0LD��e���%X���:��=���0�s#�34=#��R�w���H�_\���}��KC<Wo��,z��\���=���*�H��������_�>�u��������5�����!��9"k�����nBB�:�orr�Z��Q�������|��I���bt"C�7�����j�Q��Y����\@DDDDDe���i���������0<�����W��T����E�9�������{��qpM����o��u(��R���
����1]��#!==]���M�'�%1'��oW$f���p���c��I�Q�����G�-�8�III�-��!,���Z�h������F�*�eKb�f�k��Tt��8���'��6db��+hT���Y���/9��y+��������s8�������}���N&��'1L��z�M,�=k]*�������{��Ar�o�^���_g{��!�E���Cu>2W�D��lY�j����z��\R�w����i�li8��;���Q�F����-��nP�����7([��X�����T{��y����K�	`��Uv<���l,O=�&}E�_��1��U����y+����	`�p�%o�p��&��0�������~�\Z�1�,�����O�0u
��Vn�\����Z  z jv~����e�i��#??_��k(b�]��=h�
��lB�������e���K		�-M��M������s~��W��y&��JW���k:�<�����8�}C]�*�����l?v���o��I���lc�D�����`�9�w�[1\:&��'1L�����r�9��������������!���F��(�s�\��� m���������4��	Uk�b�r�p�dke{	]����m����9y�m��=z��f���jO_��W���xD^�	`���w<,nH]�=K��&�,?�����9�v�]�VL��	`�IL�/VD��
i��Y��+�����*]�5��C<��N�V]��]l��x.
O�����wW��s����!�*�P�F�l9��p��)4k�L�4�&t�����u��m����s�������<�<�/`��t���
�y]����hQpT}�D@+��yz\�a:&�5<_!o�}�s0�����|i�e��	L�/�^��sh��[���T�5��K#����O�|��	���lC<��B@�x��\�Hy.��0\���Rs����G�-dK�={J�H�v�Z���G�4�	]S,���?/[C�X$}k�������&��JW^�������3�~m#�\Z�Z���G��)��K�-��x�B���.y+����|i�-�����IDDD�<���|�Y���=#����H���W��C��%�M�+�
��_��N���A`�G��B�{�;��t�%�����1D������E\�I`kD��k�v[�����Y��:t(�x�
|��7����\����+���_""��s��a�G\X��>q-���	&�����*�DDD${�6�����s�����L��/�6C�:>2�������W��������o���~�����Q����� d�*����o/$"_t��e���_|��M����O�G����OX�re�����H�Z����f��W_U��NMM����&�{�1������*���y�x)��m�K��&�'��7&���<��y���������>��v��x�G�r��#�z71�������� ��Q������!�E���
>5;�)��}������Kh��)����n������������,��C�[	���;�w�����s����e���~����r
s!!!�FDD��N�<����ca�3�KDDDDT��&"�di����e��p�9\/,}��^��a�#��gT���=�a���}������s��"�b���g�Z%)�/#�����'�!��7}���=�@������)T��P>JD�H��]�n>��cL�8Q�y����Qs����s�dKc+���CY3g+�%k{I[1���M��9|'M��A���;�T�y&""�V���������
�_!""""�hLU�+7��`C&�\y�/�����0�h�q��^-���� �G\�`�'�E��ujWQ��c"�k$�xN�$���q���(��|�A���f�X�}�_���Z�GA�&%�4D�}D�����������y��!�����XK�
�������kW����x����v�Z�={�.]����+� ""�}�o�������5"""""�(LU01����������"��&�{~�WS���["�Im��3\��5\[6J~�\R�Rx]M��~6�W�S��y�^V�N��$JM����H��$�H��:�<V�Z�&r�p����h��5���+�0g+���f��sY�tn���:ds�.]�sbN_��I�>}��I�&�""���E�����h^�0U???�����a������iK���]�����Km{ *��
iW�c�{6=��,[�+:�;���n]� �8F�l�Y_D]�LD�C�,�m���/�P��b(�>�@M����=tm%tE�����9��7�|�]�v�=ysrr�!�g��%� """K5��T����DDDDD�	`"�
�����)�1;�O��\�p���ja��Zb���!(��z��!��'�K����FM���A��#d�*�L�AD"���_�#�<���h�i����?�!�0W����h6]_����������������{L���\�DDDd��;e�y���e������*
�T��
�!�-���k��b��uE������bm]Ql���(�X[W[��+�-���k��b��uE�V��
����+K�b[F�\j�H����H��$��*<���a�]U#�=j�u������;����C<U0��W����y�\bN$b�z�-,Y�{�����W���z�Zc+�kk}�����eK#�����q�����X�s�����?/� ""��#m��\��W���FDDDDD�	`"�r���UL�!K~�V������0�b�g1���+:�[��N���S���w~n!�?�!��*���+z�v���j�BDD�:d���������+�5j��-�H������s����������+l����v���="""r�������\�#�8�0�U,?EO���Z=7m�|������G���a����(������*k}O��[�����z���QMj���Mp{CN^��	���(:�tG��������w".���E@��������|�����m��QU�\IY�����+j�XKb�^1��%1<��#��(��W����Q#����������H�R����|��s��<O���#r�x���7�$Z�x]���K�S;.A����Q<_!o�}�s������}���/���w2L���?����pI���'�|��'?��Pj�_�^�x�{c�jUO.�,7���.cGq���oP
��G��/0!��B�3H.�m<���v��e�k�NM���gu:�l��_���{��M����X��,�I�:�=�E�|���Tqx<"_`�\�<�+�����]	�v�E�u�p9�+n���t!��|���]����+�]�V����W��0U:g�(;�>�%������5v�Cz�u���.s��i\w��� u�o�a��s����\1%�{���>����5=�b�r��L�nS������W�v�����d���D���������iii�����5��b�\kC8w��
�w���VBW-�6�/��������K���xD����by�'V����3	���2qrk2��!���s�\�-.�E���hc���b��Z������%��|���]��i�=�]�V����W��0U:g�(;�>�%����ic�eu(���7�s������jb�@�5���;����=� �T��E�Bwl'tl��si���%.���l�>�H�����"�jm����P5	l�VB�g����}�li����&h�q,^�X�4,���?/[�u���Ih��w�y'�5k&!O���k���y�X��G��&��%/F���-�bS��{�FDG�<����C����Q��v�����P�9��+����zO���
������}����j�'Q�;��b��uE������bm]Ql���(�X[W[��+�-���k��b��uE������bm]Q*��
��d���_!_w�S�;��mP�/�6����+5�+����>G�W/����!��qj�����7�� DT�m���1&N����x�l�AAAj�^k�%y����,��a�����=�������/����Lf������������w\J�Vo�
��Z<���2)Y-�.n�d��������1LDdA���r��l��u?����D��`����a�R��^��w���G��������pY���5����� DU�~Y�lmfa���7n����U�V����rg�������5s�fn�����i��=���<uh�o��FM��o=[�+�%�,��A�Ggshg"""""�0y41���R^���b�gw���.f?�G����EW�������<W���s�qm�����^�k�F���o}j�%���oP��M�����A�Gv/���}��;v�:���Y��+��5�h�m���f�V]g�����)z�������>F��.�$����7�4i"��/s��a�]����?��/�7`�*����(D�m��<��>;"4��F�����8��\Z~�N���_�������������������=
�B��x���!�7"h����7)NF
���������5"q,���wy������+z����{���K�m��III����s����������4:�N�#X��+��"	,�v&""�����d��}��i��y"""""/�0��#������=C�
�;C[�����R��u�
v-��o_Q���#q����;�[�`����BnC@��P��Y�?u=��[��}_R{��D�5��E��S�w��.���E��������1/�7����hu^^1?���W��+����C�����H�2`�L�4	,���k�����5��F��EDDD$e���U�N�5"""""�t~��������n�4� [�}�����|�������{_�r���U�������\�vH����6��>J~t;�;�E��7/�������[����ou���������^|��M������|5�j(iii���F�%����;W�����1c���([����c������RRRdKs��t��Q�4=z���m�d���x<"_P�q'�\�F������E�M
�[�!Z����ET>x�B�������k����.y+_�w�+�d`"�r�u��z2�o9��������(��zNM���Z��	��n�T{��H�_�w�����}E��2'�UG������G���U�q�x��%_&z���UKM��D�H��e����\�������ue}�L�5k�-[�������L����y�,M���_B��Kr)�2��JQ�wb���p�����|5����5�g]��[�?>�a���������|�nE�����>���
"P#�;�[�����,����$�IY>�^��&WEO��{��?E[�q��W_��4+W����eK��kW���K�4�z�6j�999����E����Y$zW�X!�U.��Tt��8���i<�/y1����yM�j��_�H����W�[q���}���p�%o�K�ny��LS�(�?�LS^�M�����W���U\�Q$)?C����F�e�r��L�nS�������H�����-�V�y}���D�uEEE8�<�4i"�h�M��8qB���R��u���'[k	]�K�.!$$D�4�G�x�"�+
��%O������;��72����~D�s�uw8�~�zl�l��������9�������K�������;�&��p�#z�����g��=}E�Bn^D��;qGA:ZU��h�cm�����P=T]�_�3���B��%��E������������#e�W�&jDtB�;b�Gwu�g�<�v�����p�Bddd��=zE/�����4�&t���]Kg����dn����:�6
�y;w��_|Q]F�mx<"_�0Q�D<\p,�N��UE~��9�+4m��!T�x�B���.y+����|i�e��	LW
���!���|��z�M�H��d������6b����u��p�~��1�Ql
����7�_;6��I^�����
E�w�I`w����|[���w�_����4<�.&�"�{��94H.������/[��V��k�7�����S���Y3��DGGc��=��INNF���eK#�C>[��7���|�D�[���h���q�+���,��>/[D���+��������K����]&�����oI��s����U�����
�#e��~����mu��6�,�oDf;6\�������������:��"1���r���V�a�<���o�^j��CU>�~�����t
�_�Ng5���lI�=p-9��}������'��^TT�Z������D���=�&��J�������w�Uv�!��~B"�sz�<_!o�}���]�V���2L�&�}GVn�:�s���H���|�-�H���Y�#��cx]�^
���G���qm��r�_��P���U�un��8����D�G������������Y����o"00P>������?���N�������o�-��+0`���������eK����"!!A�����=�&��J�c��h��[�\'�}���4:
��/U������K���.y+_�w�&r��K$x���=������<�|��Z��B�fu���Q �W+y�����+��o}��T#�=j�Q������j���/�����c����z��\R�����e�����O��/��-������s��/���y###�� ���Kd{�L���?��������[����1���-�v��9���|���]�V�w�[����0���.^�W��b>_����-���hlY37<����V�V
t������}E����o�����V��r�E/�V=�^�~�C�#�K<�dD��5���4�������l��{�U��l%t�����$����3�&M�-����1m�4�*&���2e�����9��0LT��$�sk4D���V��*�W�[q�%o�}���/���wV�?�*�������ob[F.>�x/,J������]�j�_��������]�:x�[f?���������������n�f�Q\��&����p#�����W�6���E��NA�	K�_+Qg�����/U���X�n�Z��+������j�*u�^k���D���+�1l�5�{��k���o���v���K������/����!k��R���y'k��E!"�j�&�r'��i��c��9���q�[��SNcc�e\�Q$�rM����K����~X�d^��������kJ�ueR�����}\���q��!�8��:���o_��_>B������Zu����Z��!x�g�?�W
������z�r%"�������_W�����FPP�,Y"�0'�K�F��k��	]�^���DO���;�%�DX�'��c��k��	��DDDD��
v=,�~&"""""��!��R����]����|���!��������z�M��sl
���u._�6P�_FQ�Q�������p��	�<��[W/�5�_����xX��]�$�������>}5j��:�����Y�fa�����s�N�<Y�4c��Abb�li�/_�����F$n7m�$[�s���CF���(�uQ���^�*:�d�K�h���������/�8���7G}�y�R�|���]�Q���}
�]�V����W���D���
d^�7;�0��L��$m:���j����D���qm���M�Ko��K�D�����O�T������B��"�[/�?�3p:j��e���l������"���cG��UV��l��=x�������l
mX�n�����������/��-�$�XG��/Q���[���]����i&�����|�DU������E���i5Q+���e9y:�V��r���E����:����?���8}�@��1_o�;�`Bg���[�2�����]KPtj�{�nvQ���e��t�/_V��/6�Z`i����={���V$e���{q����?-�J���������?{�,����������^�Y$y�����s����S
��V�2C:���1�EDDDDD��C@S�(��88�mb�����l��
���G���o�r�&_w��^��SW���U����(�+<�.�����y�v�,sO��7{�5<~1���L�#�!��b�������1��7�|#[�U�V!>>^�4=z���m�dK#���];��4j�999��)**Bhh����t�����H$�J����^�*:�d�K��4�����$55oYZ�,z���ou��B��+����z�����k������}���N&��'1l��
��r�
Bn^D��;qGA:Z��;�
��`��q�z(�����E���j�W����u7o������<4)<����-�B������D`�%�F������-P=�����(�n��X����q	���lQU#�����(b����p�����c�'#���C��}��)1���QkI����iS���J�
���jb�T``�������.���&*�e<|>�$NnZ��YGP����+�Z�f��h��?�}&����V�w=����K����]&����pI���'�����ri!j��.1W��C�����U�n���8���4-�Dc]&e���O}�����(�Bn+N��$�����E�1��.m�l���A#>�?Iu���
1,sll�Yo^�4�kz2b+�+�7n��-sAAA�z��liDBW$�-�;VM���������5��QU���|�D�c<L���+����z&���}���/��L9��91����������-r�}ju�'��d�\u�B�N$y��q�4Q����z
���B��ZuTo��F�Z����
�5�(N���-Wr\��oqc��P
�'������2�=�3��CD�V$v�p�b�]��W��km�e��~����1M�Z������Y:~�8Z�(9�x�n����
DrW$u���+�LDNa`O��	`��1&o���V�w=����K����]&�}��W"����l�^d��K��c�����0�I<�2P]�^�������U.��_�����%�YV�	����B��,��E�H��V&��I]��U���Go�5���D���gP��g�<sE���e5����m�P�C�71y��
	)��[��=n�	]���$z����%CB��d�2�k�b�
0@�4K�,Q{�9zE��&M��G������|��E�}+�^���6coF��3(1}���	c��-Q.����������7��
y+����	`�p�%o�K�.��J��oG=��_g�V!n���zB4�%eVY�[A<%�=ow�S��=�]��UX-�i\w��� u�;��n!+�g."�r�����b��)�>������&~Cn�k�N$PE/����W�("��lBw���j��������7������<y�����_�u��5���G�CG�����:�;
���N����_O@��]7?oe���a���W�[q��L;��.y+_�w��E�t$�u��C$�����H�6�K���F���+����� X.qYe=o���W���b�!D]��;
�������D@+h���n�����w�*A^�M��T��={���3'�����R���T����I�����������RDDN�>-[g���-��A�dK#��MJJ�-��������'#�����"�k-MDT^��/����q�+tHO�C��)���B���#��&(�k������x,z&6n�����n~��ze�0y3�������9x-�9�w�[���[^q'��FL~��qr>��X���/w
��O�.�<�_^�<uA&�z��W������\y�!���w��L�]�&]m��-���!��j�!��~riIg.���R�����K�����7�����C}�y��<�E���([�3
��QQ�\��8?/O�����s��o�{������SSS������/��['[����C��-��q������f��Y�:u�lif����_��7oTT�����y"MD���#�w2�u�.�c�F�Cq���9������Nd�^��|��NDL����L7��y+�}����x�B�������D�p�%o�K�ny��LW��d�	{���G a��k�'�E��W"�n�����*�y-������e���d s/B.Q�]����f���Qxsu�5����e���5����@�.�:`:�VGM��m6$~u7�?��
o�G�.��r�@_+��u9��P��`����������\1T������C�Ry'tE�y�����3feK#z�Z�yl�i"�<�/����q��r�<&��HX�=��Z�R�<���G7c����������{0L���+����z^Kt�]�V����W��p%�\��=�\��_��?Gxq�$�6�~f��F(f���1�#N����T��
o������p7������]����H��K^��;>D�[���,V���������������?�#�%�M�|_$6�E���hXt^��+z��R������'���yy����M�f����7�|��{L�4/��>��������x���dK�lBw��U�9s&��m���H��������i"�<�/����q��2��fO�8�����?c����^�=�'Bg�@����{������0L���+����z^Kt�]�V����W�YM��
���KdVo��>����O���P�����;e�Y���G�����#l�<tH�|4��|��9[\tu�.!��	���X&�%>�C��f�����j��G�����y��%�=��a����-�
��x��(�O����?qa��,U{G�Wn��?���A`�'Pg�t�����W���e�1�������._����w��/�P��V������_��2A$e�IKK�5s"yk�x^k����M�6��a�CX�*ss�^""""��6/�IS�A#��~���{����d����y+�}���A���:b�X�m���Y��k�!C��RY�[AD�������hRp��9rK#�M�YO8Yqq7v-��N_���������;�s���E�z�d���������'!��P��������1n�����[7<�������/��-��E��Zc+�+z�Zc+a,�~���1g��X���N�S{����O8�R#��{`�8s�b�k���������0\�g`��]�RV����#��Hm9������z�
��G�������%����Q����~�`\;���G<���O���Q5;����!�:�V���?^��+�]�8q"���[�={�i���%2[	Z[	�#G����d�~HH�v��=z�%��5k�`��I0`��#�F��Q""""r����Ntu,�DG-��A�]7?oe�""""""�a�2d�iCBE�DcY��^}h���B�+wW��Za-�'�+D�����8�h�pMt��{+O.�<"Y+�����W�Y������P���P������W������������D��V�V�����;o�<�\�R]����;�����v�Z�={�.]��]������5������e"M8�����Mf���Z�������ADDD�`���(DDU
�� +����5
AY��q������-Ob��F;��-�W-�1�[vE@����l���B��/��Z����do���j�W$�k��U��~5k#���jr�Q�����.[�KDbu���7nz�����P�L�<Y�a������5����Gn�&M��wou��W_}U]fI����/�O�>��DDDD����E���X������}��zDDDDDDn�p%���#k�rmH��z��trk2o��-����M��a�{=�:�������
f�B��d=�	���Z��A���FDGT��Pn�>59��)jB����v\����V].%or��9�[�N�����O����?���oW{�
�z��y���f�^B700P�4W�^U_�5���G^^���w��M�!������K\�C�#N�]�>oe�""""""7b��[�^Y�~E�G���_Qo�7���;j6��P����xf7%cEO`�SX� =�E�bQD],�XM>3��=DR��GAtt4�����iS���c���k����-**�-M�F��yv-�u�%�Es��D�H�n��M��5��q��ue������������\���i|???Y�pl}�2�z���-��^�8�y"�[v�`����'[	��+���-O'��/��.�V���W
���(��n�����Wt����ar	Q���Lcs�>��3�Maa!���d���?��Z�j��f���j�\K_~�%�7o.[�^x��-��o���LDDD���������N��N�HB�VcQq�B���Sz����~xP8�8���y+�}���
d������w�v�mh���ly���;�������C����s�����L�A�����g�����X����D�H�>����2e
�{�=|��W�q����5k�T���F$�����N��>\]�^����b���>}�:|��+��%""""�0U��""""��|!�[�����|
�~U�p�'��w!��������"8���y�%4����+A��?n��
M[�h��]�&����������]�Je�|��:��29--M���v�Z��+GDD�s�ZJMME�.]dK#�|��Z����1t�P��������dK�������o����K����<KE���s���O����������b����{����UI�w�p%��P��tE�)�vee���5Q��\ae=o�
�,+�#���%��B������)�m����5�y~����u����y.IDAT������������{�n5	l��yzm�/��������_1k�,,[����W{>|��_""""rMph�E����N�t-�u��V�� """""r#&�+Cxbd;�`}�VW.��������:����5��:���X�!��i��*)���y�;���x�l�j���"�bY�#��SL�z)��=h�M��Gy;vT�����k��������AlMd����n��j/a����v
��S��?E�Y�&""""r]8���i�������Z�������ADDDDD�>LW����*��V������[����	-e�)�����Qxs���Y�r�����/P�J�>���Y������$�����Z��A�����I�ju��z#^�db�f��?���?���<{�l,Y�@~~��z��&tM����0`�:����g����mk�g"""""�k�H-��V�Nl�Ntr-�u��V�� """""r&�+�'�7H��[�se��#���5 ��p)uXY�[����i�j`������������b�h?
��@n��ju�L��=��R���]DW$t���^4m����W�l^�r�\������5s����7l�Q�F���7��y����x��������}��A�&M�""""����NZ���[�Qj�y�7#������@���[Y��������}����{h��_}���p��m����������TY�[�:'|��w��&q������p! \�3�fbW�,���	���Gh����E��2ks�R���[+�8�<�o�����y������7o��s��R����lk}�����Q��M��`�$$$��`"""""O���P#��>�
�'���!�����ENr��V�� """""r&�+Ix�x�0L]�<
sS.��%��3
�d���H���z^K�z����	\���q���q!���j��~�����G�����)h��
���L��0���g��jO���P�>b����r
s���C@�"�j����_�y��er
s���j�_"""""���Z��isS`3��7�i'^+[�������ADDDDD�&~JY�n�����i��i����w�������H�[���"`������������"0��x��`�e)e��'�qt��Li��y�*JQQ�9�&YE���@������-���bHgKbN\17��u���o��������:�����O#""B�4��5��S�d���������N��������;OFq���qK�2"����������Rp"b��8��}�q�L���"��f��r�����������������;��L�t$�u�XC���C��F��t���g���������X4�K,9��s��U����c�����4h��^�b�g��������Y��k�n������-s�����QQQj�^Q��DDDD��	`o�CzR:�5�P��~C�Dt�<���?�������� �v��P�����������?�E����_ghAe	A�1s5~���n������%���"�z��a��\�|Y�����nPP�^�*[��<�"z��#"""��������E����_#�v���3�����i?�u0,��yU������;9pel��������98���y��AdxOKD��llsw�����E"�k�Z
m)$$���"�+�o���w�U{���N������d&������������X:s0b"Ca�8��3xS�!{�����~��zDDDDDDe��D���[,���w�y�libcc�a���Q���W$�_x��h�B�EDDDD��=����������W��0U
[I=�3d���bJ�<t�P���������DDDD�]*:�d�KDDDDDD����N&��R�J�q�!K���1c� 22R����wo4i�D>BDDDD����N��DDDDDDT��+�d�*������}�����*:�d�KDDDDDD�����j�'y9&���������������|�D���P�
��0U
k	=Q��������������uL�&���������������|�DDDDDDDDDDDDDD>�	`""""""""""""""�0y4???��������������Jb�*����(DDDDDDDDDDDDD�:&���������������|�DDDDDDDDDDDDDD>�	`"""""""""""�@���@uZ��Ir���q|C�������)����LO��?�tr]G��}p���3�^`X+t2����?#�wa�|D�����[��8���;�LO����MD%��|�x�p�8�����	%���Q���`����wo��<���v*�?=1E.s�.� ��=�[�!P����'�6w����o�����q�<�>�m+�A��D�z6����eocH�V4�^0��D/��K.��S���N��GV������
�d �Z�a��e8��t����y��)ob�W��8����*w����e;���'�+�A��e�56mB�a��S�_3���������!H�r���
r2�s�,<�3a��C�E�@�s-y�-���W��Xaz�*�~�->��=��p^�p��&bS���k��(.)LS?�X��V�����+��	{�DY9^�/��������uf%���i%@��B9Y(�.wo��<EaZ� �'��KQ��-~B�4���7_���O��9k�Sy���#r���a��PFX������Pa�b���+�gR�������)��	r���
�-]����d�Z��He��������=/`���k-���DB#�)#�LQ����~��P���4��[a��k~|	�_no�2����9YY�����������F>������R�kjq4o���.�=�;�-z-�HdW�?��?����������Jt��c�3w(W��QUwNY<�p|p��>�5
�KP�!��7?���D���������^I����:��(��f7��F+��������E�Tv�=�����d�LKy���#r����v�PBM�;���p�dO@�=x�~������=���NN�<~:��<�G��o��ON�O	2},B���
�{{^�0k��{v0�Y�W�����"�)����Ms�Y���a��S"�;�g�&�2[o����
�X�B%k�%��X1��J���j�������(q\�F�<G���[_"��I����s�w`����?��?����]Y����]��L�B�P&��`���_�����X���r��.�or�#B���E�pC9�t���eJ�g�O}��="�7*,�0w4\��@�e�=���9��,�%k�|%��"�����="k��i)O�|D��q�<���b��[�~+��4eA/m�
����2���D�Y���
/��}�c���dqq4|AY3>�d�,=f�+7�)K���T�����=�w��Be�EbHG��W~m�9$���Xv�X9'��Pf�������=�����+O�*���s�g_#_qAY1���^@�2#%�b�����'G�$Z�A�����
�.�cZ��	`�^W~VF��D(	6���H�J��FD��J���:C�_����P�'v�2;AMP�[={p����fmTfD[$Eq(|E�y������5fl]����Yg��LKy���#r����
�5������%�-�0K�D�c�;�Y���dU=7���Y$Eq��xp����;v����P�1�z����y4�d�y)���y�������n}�$A9BYa�����D�{��^eN�a���\Q���������0=���������U������w`���������P�{�U�V�U�]������#"P�?5C���y����"�Ue����V��u��Z����Qe+y��Y��:�P�7�Niw�*[_5�@d�����Gd�a�1-������a<��GJS���eA�\��8'���v�1TY��,{��G.�[K_��_b��qz�R2Tw��<��c��4�9���[��	��#IL���^�-\��7>��c�����D�Q�������R�7��5y^�������1C;������U��mQ�����>���sh�s�w�������%���?����n�O����%U>L�)�DTU�����][�_�]������X�CO�G���dX�x���f���#�������������X�W�,b�b�Y�������K`<�4}������^�����dX��x�����>w�#��c�a�
�����+�6C��3#������7h�}>/��&�WMG��;0dA
���h���	���J��O��:^�[O����@��c��������2�pD<fm��P@$���;�����%����Z�G!**�m�P.���j�*pY�e�����0p�-T����0�g���;/�gl�R�v^�'_#_q5�@|��,�6�w��bdi8����b�vQ6L�w��������E7�u���yD���Y����~�7����Z��&����z(���B�]����Nn�'Z�lk�?rI��q������#���yV7��Z^hB����=�>�;j���>��1q�(��'��|r�b�����o�����q�<����JV7�d��}�]��C��}��]���Z��d��#�gA�?���?v���N������B�GPz���q�H]���M�{{/�O$b����h@�����������������1�r�=�~�=���d��}��?bk��O4w�'����-����'7%�U�|�|�5��ATT���a������&�3/���ke�0U
k	=Qv<�e�����j����?4[���QU����Q@����.��Qqm�,�pL
F��q5���sf����������1-#�G.�`2LH��y����|,�&D��\�����N|���t�N|p������#""�x�%�t����aQ�����4}��
C����
��L���g�/�����N���r��������,D���]8y�#�p:����k=�b:��P���C��k����'l=
��N���1���g�v.{��'����9��}1zD�l�a��t��."����i�]�@�$�]Q�C(c�\������t���;p����U�n�\lO�al�"��}�uS��,�]����2������h��_�z�q���?4����:�����z0�U8�I��k��i�'O�Y�/��&���<e����b���Hh�DV����0��!�'��U�������c���6���K���4��-;�A}����N|<}{DDd����N�*k������6�;kvf���P����\6O�GX���j���D���#�ec���wr��FR Z�*N�-���O��E��j�B�0�����Q��=��'?�c�����t�.�KL�uJ����B'Y�4��YK�LDq� e,��7���Bq�S��S��4<����@���U��������'_�*-3�S;�c��"�*^�(+&��+ee��5�F!�#�v5CY� "���c7RdWN��z���~#1e�}�~��0���,�qI�%O~/d"]V���S��BC������#�Ju��
x�K���UeeC;�i��N|f�����GDD���K)�~5d��S���o@h4�1���	i���e��e"RJ$��`~��������5�O�������B���a������v�����	]:>}f,R�����`T{���>��3�/���}�ti����GlM7�i��' �	$����:?��k��<���`Tu]L�����7��~���O�vQfL�W����3��iDT�����	����pz��={��|��G�q�X2&GN�2�^��+�����a�\ev����GD��j^��f� z�����>w���c��i���m����;�~(b���p�����]�� ;���!~���u����}8�����e�)�G#t��H��#)����������'�,�G��/��{�=T����S��/�**��^}����X���0v����i��7���E�1LDDUN�=�/*
���5kv~4��J�O��?�Y��u�p
s^��d��������n���1��
GP�,=�z�Vr�0t�[
6�4+H�Y�	""g������k�����u�i'��hm#�C��53���������1�c���~|66w����������x�<�E�L��~������t�l��0LDMQ���,:������|l�
��o6������j�������NDDDDDT�5��,��#GN#7��08������x�}|/�(���$#K����v1s�:����	mE�wV�ics�d]z��I|'{ DOF�����})>T{�~��;N�B�.$���m���u-�+���:y��Hy�t^m "!K��-LS����������W�d9�f�>Y'""""""��c������'�b'`"r�.{-^���������~����)/j��Z�};�A��%�-�XW��v�'�?�9O�����
�	�F�Bw
+�t��s���"�c����oJ �1L^)�����^��\V��J���"�zi�dM�a8���.�ir����GD�/�	�|�cz����#""�x�%����:p�������}:�{9�S��2G�0������b�{���d�tR@$&������K��em����T(^�3m��B���F��?5�l��pE^��!��v��O���"z�V��_^�(3&��+�i(kz�"��U���qBV���^]V��J��5e�R�4�U�E��_@f���Ox�M�G��y��:��|t(r�����j���x�����>w�WT�a#A��>���2�������N������9`/dB����Iwo������1�r��0�����v���a������X.��~=K�
��}6L�Q�]'e�U�u0�y�S+1���0V�����������{���E�1L�)<J�Ktg2e��+�pFV��h�BV�����!��9�;w	�r�����?��������� �xw{W�	�U���#"�(��i�����v���f'>��=""���]�L��D�u9ye�
�vr�tl'����������E���'�����m�Go���W���P�@t�)�}	#�a�����@���7�l9�E	`O�F>�b�\���&����8�c������x����&��2R���`+��w����U��Z�����JDU��WX3aM��i�g��,��.���BC�������h<y���c\�8���*�A��o��="�-���`�c'>����������o�����q�<�	|��j51VSt�w�\^
���EP]Y�ki����gK.���������o��uF��
�Buc�����<�w������]cW���0 v���"��k����8�\3	9&�K���~�|��k_AL���\����G"J�������b���'�7H��[�se��#���5���;�NP/���g��,�n��*�����~�@��X�,���b����w=*��/{��mY�� M����)��y?t�N|�sk:J=r�
�3����������GDD���K��:���09?l�!Y�������c������w��e��(���h�}Bq?wrr�6wA�A��zo1F��P�"Rw�����)�����I�c��Ip��o��1�����d��-X���#�DyI&�����/�W���C�_D�v��>�]��T)�%�Dq\c�����������m����P�o�M�����]��^��~�e����O��p��$�,���=�?�K{�;l�{�c&�}i�i�{@D3�����k�{���V�?���?4^P
}!^�] y�����>w��D�7���I�y_)=�t;�����t��e]��7��>_�#�;9t�~�v�����_4�=k����w��GW;2�A���JD�n�����'����a�c�n��h�J�_�1��A��V���R���|)o<�O
��B'aXo{W4/�F��������'��^��vQF���MY"[��/�8��
eD��w#�����
��s:j��q�rP>B����Y��8�x��?�������������5P:���?�X(����hX'@�ML+��ta���F+?_��r�����M�%���K�z������������3��s���m��2�����o��:�>fZ�SE?��x�%S�C��+b�4[A�~o]�a\#�Vv`�X'"A�}��4~��l���!�����^J�Q����i@��hs'7?F��Y���w{�f}�|�����/���x���=&8��zO@�d%��
�������"M�#(���V�>;/�F�����jr>P���U������wp[�Z^/�|���E�X$v"�q+N���)�Rv��5�7�W���&�����Ia�����>����eq�Z�d������1C�a�����AJ���-N n(���V"���O�?�}�����gq>l|(��V('�O|���9f�z��_m^���Yc�wLKy���#r���i.����I��6l�r�2��L"�"�9NYa��+�Y��9��I��
/��c�O[&����"u ����78�*�Cuwo��8��,�������b-���>���Jq	
����2E��/#�E*�&G����m'�*��	`o�F��P�1��xe���U�k��1-������^ �&k��(N���J4=��������P��<�49���5-DeU��XfzG��h'�#�~�Af�!b����I��]���o}{Aq���#^p�������AHZ���m���)������f�DN.�N[O�QI�}�����~>"���K����uF��>�D��'�(�G�H�D��j�:�eO4�d�7���"�����a�$��,{��������xq-ad?%�4y&�b����r����3I�3BM>���������������1���+}�+��	`=��F^��B%��;w�X��}����u�wp[�Z^/�|���E��8�,|���Qk%H�1�~PA�������Ga�Fef�\+%��Lec�g��Y���=� +��J���b�Nz��="��%�����O(�fBKq�������o������<U��9��]�4����$Rn\Xzr&��2�;99����C��>���d����-�Ib���j�v�*�)����1J�i���p%*f�2-1EI���O����������[<����\��?���k��n�^���o������B%��Re�����e��D%���
DD��1+'�A�QJ�����{2�(a���%��~J��������;`9�N���="�|�'��fP����D�������$�s�����#20��1)������U<���Q��y#�~Q�Zb  T�t5�R��X:Sc:k�.���>�<�����%��/���n��O9o�r����wo�|��	�b�����uKi���^�0����?��������i��i""""""�
�;�Qy*�����IDDDDDDDDDDDDDD^�	`""""""""""""""�0��`���������������G0LDML�n�QILS������������������\�0���S�d�L���t������k��8���/�K�;�M""""�]��
�����������������;����������������G0LDDDDDDDDDDDDD�#�&� ��s(5���oX""""""""""""*�	`""""""""""""""�0���S�4������i8L+�KE���s�������<�W���DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���������������G0LDDDDDDDDDDDDD�#�&"""""""""""""�L�W�]�����%��)�J�����0��,��s�_���tG����(J`X+���U�A��`�����z#)C.v���CDDDDDT�rW�)��Y��qw��������{�`��K`Z��	F���@Ro�������i��y*&����+�m��=�{�e�DV�j��Q����Z�S&���xu�Nd���z9����lUe:d�N�����C��p�v������<���`����X-��Q����"��)������;�bd�kF��7�O�9e���k;DD���&""/t�)��vEk�������e�/��NV=�n���<��M����S0e�����;��TQ�)x{`;4�6_�yS.t���CDDDDD�aN��q,�Z�a��=+ ��?�yZ@���G��S�c���P�#��}�vBO���Y�o��.p�v����1LDD�G�v�[XC����U�8�kAqu�}�d%�4�k���]��`_&6|1�g���K�p�|��J�������\���y��bt(#���2��<+ �- F�������xx��XZ����x���0��wm�����DD�}R��{Y�w[Y3�����:�����Nm�^����'��"�*
�l��H��i��QEI�V- F��8�Z@���.��=�o0"vV$�l6��
����������b������pW�w�53{6�;Y��������������AgD��%#�=���`DLDDT51LDD^����l��DDDDDDD��t�FqD����0����5{�UQL���HBo??�������|<g��x�Q��pW�������e�����;���Q�k����F��q5����kZ0}�����E+�v��;)C>� ���N����l�^4q��(k��C����������|���I���{�V�^�_`Z���U)�h���.�.��8���������b��r=�����g�]��0��R&��e��5���X�4C��~�k�C�'a�y�|�%?���y����M�Y���v�+���Qqh�,XnC_����|�'�vp;DDDDD�92��[�����Z@��K>�5��������c���1*�-�k���-�F����g���5���Ah���m�mG��e�]C<�C�ns��l2.q��a�y���[|
����^��T,�g�^p3�v�^����^�^���O�`4>�i�j�_�~�I����\��5%>��3{���!�nq
C����0�m��V������BD�C7��,DDDer4Q�e���k%AY/7������"���5-AJ����B�{��^�G�/8�t�����+�#JlW+J���7���Pxr�2�G����V�����+���[�$������'(f�+K����	V�����d����~l���3�Y���qJ��v���>_���ha����R���H���i��.et�we~�#�U�2,���DD�=���SE?��������!WK���rl�8%2��vMKP������c�C�H�7c�]�]P�O�_D>�,,- ��X���i�����PN����T��~�����k(���{YYnZL�U����{=�����+���1�<�*i��(e�"�^���������DKT~g@LD��q���?���������6MDDUU�F|���8-���?��?��W�x�5/^lti'>�$E�#84�Y<S�x9�b���� [��
����r-!(��<��&@^z
��n'2��czAq�a�����0����0�~��X<�L���C��}�e�������}�;|���u����'��$/k���9���M���c��gB����Nc�b\=�������m��~{)�Y���oX���g������*���@D�)��o�����b��_���g0%�|�[�������3�.z!��f��T8�d�[�c�K��O��c����B��#��Ff�����E���9�;�A���a�|1��P�S����3A��|��Ss�0�Bf���1�_X>��1~YA�������������� �?m�&���GE���s����da���g��?���q��H(c�g� �8 6�7����������>���j����~C�dq@\2>��g�W�i- 6y����@�>��!{��0����xD,z������#F��w��.~����J|��}�1�Z@|Q�u��l&�_Px��="���H]�3~�0����s���I�J�-z�N���\�*��k�ok�R�����7�>����V�����J�7�C_D����
$�W�nlq=k�x�
������h�G�'����R,�/��5���T������o���|3>����3��������pp��`DLD����N�F�Bl���[���w&V���\h�p�x�x�2�W��e����L{�F(��Sr-�>��C4B��W��f�'h�s�g�z�*sveY���\8����>�������F$(������ql�2���e������(J��^�7r�?O�>z��e;����v��?zF�R�#)yG�>�t�����aJb��K�t>G�5�z������Gkw�G�V���]����M�C�h�g�;&�w0�LJy���#""�+m�#��Z�������������z�F[��++Y��+qA�z����j= .=�s����"��UbT�*�L������E�������7�K��9�&�s@������#T�����zC�5�<�b|[��9�s�M�q��_r�.1RU��K��6�|1BYQ2 VFc�e��c�O����G�\	PF3 &"���������H.�����B�G��US���"k��;JV]��2/-7�K���{��������?]'����X��#��9/��C�QQ������Y����b����1�e����0��~;b��w���@�6�r0|�*Beq��Iu:��y/G��cl`P���Sd|���a��;������P�#	D��agr�~/)V�2��se��}�[�1%�/�A�$,��Q���*;,>�=)�w:�������w��|�W
_T�B����y����r~�P�o= �1".{@��/-7�	������X2 FX�	X�;�Z@�+> Vc����"��%����L_����s���YZ�7b�<�1�D�&��u���>E�l�`+����M����m#�S�r����������1&-�ZD��K�X�������O�����[- ��5�q>`"�2�&""/rGd���F[Y�d�����jT/�sb4��r�e�9d�>4������?��I��d�����[1:�5#�
c�����Q6����^�����/
Q���*Ft���w�������'e�������Mz�C?�W<�� ��#�X
�
��_&h����$��H�B'���}��w�`�uk��M�!��wWL�����{!������c��~Q>@DDDDD������1�oAqD�^e���eh1>z=��f^�m� Q�����e���������#yA2�"�3~(QQ�
��KLqd!�#4��w"��kKQO�������N�L{1v�{0X���d@lLN�b������o/ "4R����c�������
&����{d�!�sOWM��#���_:����fR���`��qv��b�����|��d������%�2���d[~�m��l��9|����F��GK�{)���#���l�H�:����z�&v y���_�zE/�	��t�������q���o����������#��/X�y�$������_87�4y��c�A��1��b�5�2q(`���+= F�����Xs���������u�>r������q�A���8r�4r��@�\l[�4�UG��E�*���t�#�~@������*��d�Q�w�_85�4�wc����G�oHV+��}�f��A�*�P�����dh)2���x;i<z�wlk����@1mN�2
��D
BL�������'%�&�wT>.�;���#i�(�5�;��GJ������`��/EV����p��{���������������+6Nh���d�2R1'��
F��Q���{O\�cDDDDD���[q@�����dD|����#]����c�u�h�i1�U\@�A���G���S�y9�pd�|=���'^4U������E�*��?�Ss+7�;���xU��:'��Bp�8�����{O��A����`����F��_���������9;�!9#3�eh�0Go�W��*p�C&��F!�#�v��AWYE�7e���3���k���l��E�f�����M[����1��/�&����%��FuY�'N`��m�hWg�z3vNC�Y-�B�������6�;\,�k��kc��K���q���� �y��C�U���������M���w�;�P�2���h�x@��Z@�c;_���Cg+ �����������[�!��&����]�8O��Rs�=d��Uq����c�{K0�< F^�|��X����x����w��DT1LDD�+#	����'K�8�2��Xm������|<3�[>>Q&�d:��3J���t���}T��ec�� ,$]������iH+��
Bx���K���M��s�{�G��$�K������y�X�e��h���=1}�)0�%""""�H�m�F�3�����&&��,5����^�����������tC�[�|Z�4�l�6=������k��p�e�7���#�x�������?b���!y�`t��7H'��6�
�s�Z�b@LDU�DDD�T���Jv�Y��
t"�l>_������V���_�c��#*�FN��y�Vc����x#�W����U|N���)��1�S�"����K ��E���F�Cd���7o;f���'�����������b�����e�/"��;q��_������������S�V�2���8�s�����_a7�V4/��[����8�{o��y#�/2����<l����A�U
L�����M��)�<������/3+k�Q;>l��^�;�t�dg���8#�@g��\V���K�"�v�<�4EQ�0�8s��[m�t=9�/���{G����g��'��{�&��_EW�b/Y/�����3H�3����\�3&�c�G\q��m�'���q4;7.��e�x�~��7��[�m����������E�������b<["������[y��s��P��~c�b\�bt�����qP������|#�d{�FK����(����#;����xu� ���B����7Q��q��}�B ����'^���f#_�Az_���G�}7Q�0y�������1[_�����`���U���g����8;qn��gE��C"��c�����2��G��#�%���"����\Y�c�o�����d��C�a�rc����
��G[5�k���y�}MKt��U���v_����;��d]\Ph�DV+A`���>h��������7P g	��u"""""�P�q��2�}s(���TF�����w�A- �����P�b��b$o?��T2���^����blu, �G�1hk�s���Y8%[����o��GDl����o���x��o�z�� �yo2O&c�c	b"�"�&""�px�/����d�Dn�V9R(����Ig� �IC�S����E��-��p2th/�������X�+�����3Fc1�>�:����eUo��bM�.c�B�T����f8���2�+5�����>�7*���!���UY���Q�k���_�Z���a��b�l�=�/�*y��{a�����*����;�����.�OG*X�9V�##y��`
�^���6����o��i7l�����Z2�h� �������3��u_���6~0����m�`�`? �X�b�`@LDU�DD��px���z�]����#��]���me�L��g�$c�������{��Ic��q8��xeXW�w�]��&l�D���2����	xZ�vKH;cV]z�jo�'�x
�e�_C���v�z���F�3�0b|<�m�������X�����O���Rf1���.Y�uy���[d�2
��������' Fp������f���e����a�Z�@K��u�_"A�����
���Od���z1o�������<��o�����5iH����--e^�dh���(��|�DD��`��/����=�����+��qS�K�����F�y=�yv��I��m	�������:6E�/(a>��T������>�`��|�@*��0��I�5����� Y�K=�������^A�Nc��� ��C�MYu��%?kW����a�=���)c;��+�`���8�l<b�������5����G^�^��������WR�.{�������F�F_+��������3�i1:[�Uy���2�{���>a�zj����hv�����=�j#F$`�3�*��hA�����|}���$��8���F��G��ZkI�|����G�<�y�����}B�4�r_��\����1��cD�|$Z���t����G���F`4b"�"�&""��~��@"�k��A���N)���m��n���`F���nl74n�q��b���xaHw4k��^^�<�VP�gX�N���+R�/� u��C��Q���:*����HH���Z�Mm�!�f B�P��qBv�� �c|��h?���# �	�%��N��#8m�	���3mx�%8(;�-��G���`�0��R��9��#$w
y����
�C �p1!r2���A�r���~\f_Z*������V�}s��0�{34l|f�/L�C�*x�$"""""�����u��h1��/ F���2#ZK~7�7�����{��h��e��b|���Vp�P�qQX�8�Rc�Q��|}	H����5��y�oT$��5FC�v�E�~�=��;�����'iI�����2�lm��d��~@>���%;�M������8$i�1Voexm�b�^s��Ty3|�����DTU(n"6eY��������oK�2c�\h*m�#��Z|N.t���OD*����7�z	Rz���d���f}��~�D��\����J/�s'(k�+�"mk%H����rA�zI�J��'��k�kZ��sZ�r�����j\5g����z%��;��D����Ga��Vb����U�����W��l��C	2����D>�P9��\�s��p`�[��=��:�K@�8e�I{;'���v|+O�|DDD%\Y����B�����wj�b���
��BG�D(A=f*��.���U{i���&MY<�Z��������b}x5S�d�w�"��De��}�[<H[>b�rEn�`}��{�z���<1����e��r���^.6��������<J_�����X]_"�q+N�?""�c����LDD���}s��b�Z�1RY���EGq�X
�
FLT8�F��PD����U8�u���������F�G�m�1�$��~���.��F���yK���jB�����h��"8�
��4nC��}��-�����I��5�W�Q� �����8
1��OYo+;uC������{�o���X:o$�EE"���!<J|�����e]4���K�i���e#s�R��Q���^Px����E������0 ��vN"""""rMi����ckq@��^����8��/���"����1Q�B,o�`��D�:������������8�����i1[@h�-�o�D������tl�LG����V�q�t����]�
��X�}���&�u�>&�3'O3�����8!��p�vO����>V���#cu�����~#1o�v�|
�(9���Y`Y/???Y��i�DDDdMFz�+�vM�z�]m�c"""T�q'�\"""O�����0V����^�������W���DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���������������G0LDDDDDDDDDDDDD�#�&"""""""""""""�~���������i��i""""""�
�;�Qy*���=���������������|�DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���������������G0LDDDDDDDDDDDDD�#�&"""""""""""""�L�&���������������|�DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���������������G0LDDDDDDDDDDDDD�#�&"""""""""""""�L�&���������������|�DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���������������G0LDDDDDDDDDDDDD�#�&"""""""""""""�L�&���������������|�DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���������������G0LDDDDDDDDDDDDD�#�&"""""""""""""�L�&���������������|�DDDDDDDDDDDDDD>�	`""""""""""""""�0��`���R�`����J^����;)#	�-�71E>HTR&����@��j��q�03i��sq��
H���w����k�������%e�v�h(a�m�kg�����e�>�&�(����/�a���mF�L��?�\���CFRo���@`�k����.&���<N>�i�K�AF��"�^M���I�1����|Wr2����K�66mB����RqQ>ZQ�������y��������|�v����t��;	��F��� 'iik��kc�&���b�����@���Jb����g ������1�]st�5��)R�Q��_�]&�TL8w���aw�c��r!�]�'����O�������C�nc�5�*�9?����LDJ������������.Q��0�'r%0>�������|+3
d��[�+
������2�	D�_�;5o$fo���r�����y�IDDDDD������'�t���0��
	������07�S��	�@#goD�G�9������@���Jc�������K����K�~��;�"/�����x`�"�}b���)�����EDDDDD^J�#]gG�:�I���>�e�K��?(m��E~�-���9����.U&���<H�Q�\�a�1*�1)�y����1sR�l�%/��,Y'"""""��mFi��S�]���<��.y���=s�H7K�Q`��H�xM�U.��_Nc�7�E����<��rYd<����3���]�c��Cc��n�f!�����+�<7^-�.���|F��i���/�e]8�S�������\�q."��N�ve~�s�U�w���H��_`�2�%"�
�0��������1,�~;���A�(�.�M[����.���S8�|`lvWt��j�a�g�wu��������?Y�Z�m�(�_�g�����m-��Qqm�,�|;��LG�������R0Q���<�Ho��V�m�ML�KK�?�I������C��C0=i�(��F�D���N��/���E�,�xyp3���f;9_U�(H�W>���sQxp�$�"�b�x,��rm����<����Z�y����G!�������fm�c��$l�#��w����x�Vc��(6����`���<�Y[�����~���������5A���;�O�e���������������*����yYDDDDT9<'��G��v����]Y���\���3{W���m��@��X���a��e��X����2�e~�����q�z��5����.Z��7Q
����cHw��/����:-����5�M]�	q��.��M����2[q�g��8��At�f;��.����8�3���E�v���
��e��$�V�m��&Sg�{P�F�5kl�����u!'�u�����
��a[���������.�W�_i�K\�r:�&"�LQ��`C&>�x�o�%%��n������5�����|`�?$��^�W��bN����a���,f-�����G������	C� <�?�K�}q�b�{�/�5$_�ICf�|@Olg��Y��N4�|Iv���)��
!w�b��e�i��r��sf��E��v����F�>H����|ko"/S��_������u���pm�(�&_���:���O�-zY.��c�&	�D������H��mz`x��X�f����0d����Yc�&����)�\�0�?ce?��L��/0���h7|���ec��=&���� �dc������;����T��?F����1������#����TZ����v�|]���qc��X������P��r9yO�s��.�{��j����{a�����tv-��Cx�����i�1t���_&A��R�����{���� ������@GQz��7��h�
�!J����5���%`��%Z��5"�1�,�Q�JZ6 HBQ!���.1�(�4	���DbM��@��y���������$��w��3�;����������3�sv5sN�M�#0#��y�D�������qs_�������5Y����h-y��:�7��$�I,���_�����s��8����1�8���,#0.���t�� g;��������=t5���v���\�	����|�AM[*r����.��c{le�.��g��|^���0�&o�%_�����������\�5+m�f�%�@$	�x���e\�P�DD�Fn-���H��������Tw�-r���-p�	��5 ��*�_�`��,�%�U����3Q-Fc���(-������8c�W��	���&`�����dA���x0�������e�}��i6�������7q���P�(���_�'�YL����/��9��)������4�a��qH��.�V�>��8���}b�l�����q������[1����O�����C�Z��7!nE9�<
�O�XYa��p�~��X/f����Yb}�9����;�����!r�h9w����G���T��K��a��*\�>�:P�8�g7�'M��m�����E�3��}Fs���_�g���38���.�F�8������o,�^-<
|s�����������n���<�	�m��>��Fl��W�6LqVT�L����!��%v�������d����B.���9/��LH�\�m3�.t�i�s��*qY.�����{s0-����bkq�fk�+�����%�T�.���>ND�[.v�d�%�����\������!*���.,���a�x/
l+�r����]�~��?�FGM.F�U��8��DL���aK�m5(��nM#G�	������.��s$S�.�b~���F�1�s�1�Q�.�my�A�������=�����?5����?N����Y=z�oo�n�o�[��-�"�=���,���U���B�a���Xg#��wn2��}G�����n�	?t�/F���J�4��������[�_W�����4f�G?~����?�Z�����A���F=0S�AC�>�'��k��h�1��OMq�V�D�tm=l
W�.�E����u&��V���dJ���m3`�t�v��1�.�_�\��#���"gZ��q�En!������n�L�'���������zV��NQH���r�������.��P%%[�ujH]BjjmWJ��=R���)B����)��,Y�=8�"R�w��w�����L9��9�YJ4���gI
���t�I��R��,1�Zk����
SMjY�R�AY����p�Sj���;�����Y�8����qRvI�XZ���A�t��[������{2����'��D?���V��������g���	�������w��w�4(�����[�����j�5�m�]��
W��SN�����xPZ�n�0�e1���*�:�����Y�e�n�6��k�����{��!�+��\)���R�f�%K2:�W�h��$E���*
�O�����h������
�������Y�YJXuPr��ne)��������{��7]��#"����s=d��y��k�o����"�y��AW-������9Qs��!]�|�X�8[���0U=7O�
���T���(�R�q���]g��%�c>q>>.�� ��K���t��[��EJv��� ������;���E��>�s=\���Y�Y��f�������r*�����8Mf3�e1�Q�==.o{���]�����`�[�����Zc��l����
���+gh�X$�K^��R�f�M3�<��/����h����r�����9a�{�w+Kt��"�w��'�h"��:p��kwX���d���V���n��(�e�]%�s�&���dL2j6i���l��h%9jv,�����i�`K����DU�=\}�:GP��(l6V���aeKB����(�B^1>�boE�� y��Sn}z�']'����P�I��nk�I�����������d%�vi>l6�����������<����p�P���:��qJ~6�S�1T�79� D��i��5/9�0a��-��Q�����1xv�Z�~��P}R������\6����yi�����e�H�{�y�-H�~��~�KQB���+0J�Fs!>�Q���re�sv��!����Z�<l2��.BF�����/hP&����{�;
bsa�Sc��3��""""�>�/�\}7�o����2*�sm�\���d��.����;�c�q�u��"���%')�s��^�
���.�r��32_�Cq��.^I^��K4�ze'd�����V���.�W��u�A+�v�+���E����sE�E���������`%���������%5o���-�>����q<�k���}]�hx'Yj��uw����el���;���w!�������8�:	?w��{��P�.
���X��S��8V�#�(���o���(R�.2������������bsph�S�y��,"���]@Q����$l;����o��?]Z�l��L���k0y�@\����S7B�n��*��B�<�����e�S�N�0���w�5���Y����Q�r
�^��f���i����w�}����}J���)�;]e1���O��+"b��X�L����v�]Z���(-S����]��5��>L.���t]�Eb��*�"/�}�s%�������F�������0EOC��d��)�
�u�Uf$�;�������U��?����
��e.v7���B������F].�>W�D��e�n�QXu�Oy{Yk�������qG��x�����h�}����?�F�|��\4O�����/w���,�;��eY�}
K����Q�u�,G�6\�DD=�]@��E��2OY�S&�u�U�&���C�"�tq�0��E��.����'<���b]N�z�o��I�u��^���ca������
��m����c��+10b�\�L����v�=����4�N]k?�v�������p�Lb=����~D�!Rd��Kt�~�u=�\�u�U��u8��Q�c�dUx�?����h��sw{���������J�=���~�Q����{�EQ��|x��H����]I���6��\y_��xm�H<m��gw�m_���Y�w����1k-���DYj�e�D��z+w�`"���P�����t(>�����9����/a��6���F(zCi��V�����;��P�t�g��,��A�����l�Y��{��U�����^^��	���w�w���K��(v�q�G�}U��Fc���&��/;���7���^r >W���+e�5��w��-k�2����c�����D�RP����h�Y�+�
�\��vC����w�"�)��-�B� �U��wb��=o���1^*e!t�2j�n�;��_������c�B~q�q�}�4����|ge�)�.���c
L��Te�����zV����f]�z�:P�7���$O2|>��kc�
�wV���	�}U��B�����]����>��7�A1/|��Z��;��W����wG>���W6z��j/[�e��t/e��C����3�-(�1��_Y_)@EUN����WvU�]��]<�;�bJn�m;��������D�x��t��t1����{�����A�j��]��.f3�Q�`0�{r������*c���P�H��)���������SGQ�^.��$`�}�����%B�y
������!H�����Gq�SJ������8yG
�x�?�{X[�|Dh>r#g�(�����[��������S���dADd��W"
Z��c���~T��@[�TUc��'p�����3�v]���;d��G����_L�Pe���z�2����5?��z,5����W�E��9W}�����*�����|�~�1lMDDD�+�S����u��Zm�����
����)-{��s�0�>�wt���"$�A+����W�`H�d����S�>w�IT�A-�o���������.F]��+������,��IJ�����i=���w#������3U(��
O�+��L��3��n����"�z7�s�EG�a�6�`�R���mx��A{���S�J���p�h�t�s]"
�&"���n���+kZE'>4�w�f�ue"D<����!!A�b ~2�LL��������;a���-�.�Iik@����;�xB�$`��M��{��3�;��ZS���5��l��aM�Z���6��2�f�����|���SF����I�.K<��;e+P����9��Y�u�������1'!
Q�Ct9BB��% 9c���	�J�}���2fD�]�t���O��>?���>�j���[��������QF���q����"��[5}���u}�v�m��&u=��h�&�z�	�?V��!!
�2�LL���P�;���-��'�64o����q�0G��U�T�w�u/��Z�*�K���kJ��-1��9�������s�]*�[�p�v�lE�xz�X�g�z����W���l<?'QQ���CB���!!9��Fs�]5/�����>
�
�J�����F����U
�hV�.��A�g���[5Q�
`"��b�_���}F\����{0���~�����oX:)�����"D,[�
���+{�a��v}eh@8�����(I#���zm
�����q#B0h�B���owev�\~�Te��<�����r���M�B0b�|�f6W������K�DD;�C��:�TF���]Z�)��:�u��v����M���I�&�LLF����v@������q��t |Z.j����4R|����s���@���X����5x`�)�^��+���|<z� �������lC1����*{M�hL�~�E��#���A����XLD����Y�}-^ui�3�&x�p��q�-�{N��`�Q��:R��TL�d*��y���E��J3sX$"�S0�J�����h�������.eo�1l�B|��s-'�?%R�#`qt�����%�����
�]���F�Gi=��oO���t\11
?�jS��#��+<v�X���p��<wn�����o��H���6�9���64}��#�y��z��4����o�>�W�=[�A�B""""�#�C��u)�z����u4t����]�y�pLXT��43�s��x������;���?�r��w.�_Iw�d,,���Zpb>V��#�8������������F�GH��AW�Y���\�tK��M���z}�uv#=��J��dS+��>���]\�?�.j
�u�t��KD�cA�����H=

R�T=T4Q��u���Q�������g� e��J�L�����!Os�*4����!��g,��h�����|1�n�������[����<�;�Bu'���~o'E�2�Dl�F�{��`R~l�U��'py�j��|��
�����(J?Ay��j��e�>4,�{��v��J��\l����9���Dl>�
3��K�A�����}}�������/S��seB�cQ�z�v]�*�_EO==�������/����V����n�����ql$�)���/�.w��<c�xy���)���O�OtZr�������������b�#W����)KV���C����w�c%���u'��Q_�c�����CD�];w2�Qt�s��<V�(�dR�t�N���AW=��(�����m����i��d}d�Qf�(��8M��|�LB����'.��=���Z��
���T�h�G�_�k���-���a	������Y���nF�=�"q�l�~��\o����+�D��y��:�����R�.������{|DR��"R
�n��_�S�M�2Qz~9��ts��p�������]�o���V�.*�^@����wb�tqr��>���-�?�����z+w�`"�^�[*G_o�e�����������a0&=���;��������[E�R���N>]��Vd;*aAF��X>�s���d�!e�+�����HL{
6|��t����-{qB���(��v}�����Ry@�/�)���<>Y���Z�{�K��_��I��i���W���:�&��a���wa_����t`�����9������{R����}i�~W��f(FNT��}�'����
��Q���s�H���3�*�]uzk�A�O��sD�ck���W$��"|�|���_��:t+��(
c����
��(]�kE���|�E����h�'"�,�s�H����%5��Z�6��y������=t����w���:QQ	�������{�h8�n�V��^�����}s_%N��G{�.V�����G��?�u]�����l-�3��e?po���t���e��8�����Y ����h<�O�Q ���U@��<����?��9	"A���W
��5A�T����-����BD�_��v��`�;	F��(��cq��c������
W�3��u�'A?���"�<��L�2����
U����s�|����Q!�U��c��D��`{���W0�h���\W���B������5z��j��t	6��N��/ctp(�G���/��QyC���Y���7��E���������Q���s��P�:�e������@��.N�A����Y�\W����Vc���$���!!O`�������t�p
M����tqa����GO�-;Ap��s�0������9Wl�
U����s�;���g�.������.����u��.F�����;zV�.�l*��8��/�VaG%�}���j��������5"�~��DD��B���#��m����f�s��A�_�N���;���e�`\���
��V����d�hW���k��@M�\<���cdp�Y��T]�����Q�#���T��)��-;�6SO�^Q����5^AK��������r��k��w��B��'��w����l�X�y\~-$}��;�~�]��j�[J�B�J������w<�L�'_���Q�3�e��Z��t��*ob~����������[���������B3������A�V�����3��.lD��J<�NQ��L{7�DDDD�7���������P����.��]q:�����.���$����b���5�N�m��<�q��A��sQ�=��w�q��A��jx-9�~r��h�tQ�B
V�L��j���'_�/y��f�A�{/��7j�Es�<�=������*�����A����j�����y�������n�����C��\""�e���9nf�Q^vqfs�s��d7�J��*3�����)?�E��fI�l�����9�=����(3Z�S-Kbr�je�c��Q����L����@�]j,���M�yM���g��T�e�,�'>e�VZ���`�V��f>'�ej�S�����]��P�����O���Kd�-��TdY4e�K����9���*Mw����[S�U�u�3E�;�����"K
���Z �o�b{+H������J�X�O���v0����L�r�"�I�_{�A�:=�9���2K� ��LR�<��)��*��)�>��{������#��bQ���I��T�\{�4I����Z�[�8#mNt�'S��ct���hb�}������o��2�������2)+Z�����%���]v�"i�q��j���2�9a�t�=�J����L�w��w�{��\o���������.Y��);<��������]2�2��b}���9�JY���j�'��H���\��Zk�Z�A�����J���2EH�
���F��u�>�g�JnG��s�\����)�60��E�;�(CO`0�%�g�p������Cf���E�p	
&���.��?_�RRL����$E����$s��s��?c���Rl��!=I�	�W�6V���W��,�t,���)s(���2#���b0�I1���wF[\���^�����^��N����B�9L����K���� �LT+�c
�x� �m `�����TzF���]���/�����-\�{����+�m���5�����x��l?�Z���a�;'��e�L�u���!"�.��I3����}DD���
`�J[y0��9�j��3�F'I��yqzR�fV�7E���{;?w��?c����sc��,�A6])N3�y�c����I���'3��!�Y
�q�/�3���~�Xv���
�����i��_�P��)>B�������j���k���25��u}�����R�.gk������J�W������*��O���l{?�*U{8~���)�e9o��������9�9��'������7D�c���S*���5��^-m��*
�8i�����������J9g�eK�\N����������X'.!�6���N*�8��D���;�`�-�5������f���v&7Z��Z�k������-��*<m�I�,Ufqj��(^����w�����W*�e�.�L���c�w.��I��fJ.���A�F�1����#"�9�c�}�M������r[g����5t��5���f_���`��-(��t��e(�8�J���{�1��
�{�b�
��D���=�J�X��b�=���g$+�{NoV~ngr��A��n�-m7�i����>������E�������H����AoT��].�5�<�������f��H�2����"������h�C��'��DD����H�i~}�e������P_��I1����7D���$-�����x�K'C0�1!�1O5�*�t{����Q���#+���0�������x�������v�4�{Z��qX~� �sb�+b�	|��:
�FZ�	4���� 	1�a��L�D� )}%
�4��|!~��uF}����5��he
�u���6���1��<��*s��wP�����V�EaQ�-�8TwJw�������%�H�����(�[v�44����1Py��9�K����4�a��O�k	j�O ?�F�O{���q���P �g�z�������^���5��������Ozj�5a���g�]%��g����r���P{~o� "&	�����(_:��Iq��d�<�o��=�bay�`eJ<"uD>��D|�J����?kWc�������p�:1��+���Q���N���5J���ec{fO���#hh*��_�vc�Q� oge
�)��9�������6F�����5�����#�Px����U$�CupK��wc����K��e���|��/^�u.�s��ev���Ok�q"?7z��1�b�����5�E>>����z��p�f}�gAr-�2�-AAA�����&""""""����9�������zSo�N�LDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD XLDDDDDDDDDDDDD �$A���� e�����������Hk�9�������.�������������������(@���������������(@���������������(@���������������(@I=�$a""""""""""""""��x0Q�`0Q�`0Q�`0Q�`0Q�`0Q�`0Q�`0Q�`0Q�`0Q�`0Q�`0Q@�?_���"O1IEND�B`�
sample-queries.zipapplication/x-zip-compressed; name=sample-queries.zipDownload
perf-master.pngimage/png; name=perf-master.pngDownload
�PNG


IHDR	E�M�sRGB��� IDATx^��
��e}(��;�"�$A�`$XD{�r.����]=���F��rW���Ez���j��^���T�i=,=h�E�x/G�Ub�R���~D���M A&2!Hf2��]���3;3{O�c�=��o�b��������y������7�� @� @� @�J!�H���]$ @� @� @��1	B� @� @� @�@�$K��.� @� @� @���1@� @� @� @�D�%�l�J� @� @� @@�� @� @� @� P"	�u�K%@� @� @�  Ah @� @� @�(��a�:�� @� @� @�� 4 @� @� @��H@��D��R	 @� @� @�H @� @� @�J$ AX��v� @� @� @�$� @� @� @�%� ,Qg�T @� @� @��� @�������7G����{w�e�� @� @�����[WF� @�{���o�4�3����9���$K��.� @�@y$����� @�@$����	�l��V @� �	��0;	 @���L����q��m��i�����cWH� @������@� @�@�$s�y�j�a��Ck @����aGy� @���
, A8-!t$�}��q�_"����1�n�J<������|:>����{z$�}eO���M��]�3.������5M/u����7�iD��c���w��O�qg|�������M��o���x�;w����!�<�;������+�c����������{��+�=m�#����n�;�����Wk����q���5�{��8�yg�E���������~)��A��sc�����������xk�uNt �������M������O�����v}qY�����k�����O��o|5����xx������/��~����y��6�;��w������K�q�kb��>���O�����-.|�����\�^�b�x�4���^�o @� @`�$�4� @����)A�?����q���Z��XRo�d��eC7��?w���15u5� ����#w�����O>��Kn�O~�u��?�����7�Wv_�i�z�����o�{�\��Y6����+�����g�G�rs\�����:~�����C6&��on�7���O�����+n��q���<Sr[\�7���|���[�� ���o������=�|���).����_�����-��{?x]�����
_�g�����+ @�2/0<<��u����%K2�^
�. AhT @� �c�v$k����k��7^��v[,�����@�����/�o~�Eq�K7������}���[����3�����s�
�����H�k[���[�������4b�H��������j�����w0n�������������x��nK����q���$��-���cOl�����?��]��M�v
��������=�����~K�<}n]>�����_�y,�w�5r��q�x������{?�}���7M��� lfpRS��m����+v�%�.^�y�D_����_�^|������������
/nH�I�������b�x=�7>�\���4A���	��1�������5����+�tm�fn�~� @�@T��H�k/��b�8qb��===���K�.�/�{�$g�C���#G���5kb��e�a�S����O� @�@'�� �;o��q��_���P�w���[?<����S���*���W���%v�t�����������f)w�M���q���[��#����7�Wn�����L��;���������L�b��w���c��l�`���&���}w���xgS���R�<dCaK����������g�!Q�� ���o���<wZ5g����
���El�����76��D��5q�Go���h������^p]������p\�l��� @�DT*�8z�h�	�V?��/�U�VE�$�&� �}�H���o @� @`���%����r{<�7������=�p��l�k��*�{���������������q�
{����[C_�1�|�m��������
�q�o��M��g�!v�������%Oq��������������JN�.��y��L}�����o����}�n�/>|���7����6{��s�T� @�(���c���+V���^)�V�����pdd$N;�4	���}/Ga{E� @���@w�C���}{�����>�?b�'��W��=�<�3%' &�'''��M�OB��~��`<����O�Xlo�p(��po���o���{?�����;
�o��l�e�0Z%�g�0���������_�H7�r��5 @��2���c�Mo59�����I����i21}�_�DLoKZ�I��?��D�1��������m-+����-i;FGG��w��\�r��q�����9�������+=Vz��X������LM�����q�[���	���L���H�S����1��9����� ���� @� �A�Tt�*!Tk����?y����N>)��Eu=A8�������������S7��4<�H
���c�&����]	�#��-�{���;}��L?�M�J$�j<H�e��] @����D\�H[�z��hi����K�5��	�4��&�����0}�]����4��&��I���i%c��JS��z�0Mh��J��S��[7v�z�+=F�d��me��i�}�&��t�6��e��� @�@�:� z��x�/�{R�M��eW�7v��W�+^qn��4b��5qd���a���f��3�Bs�A�����#A8��kb�o�]����3�������s���l���,��Y������ � ��H�� @���'���Z�dK�K+����U�������U���}���?iR/��W��z^a�B-MJ�]�v�*��L��Z�Y%b=9�X	�&���mI��'��m�W��iB3Mr������0M�Ni�6������J�6�/��;�J	B�� @��t:A�� ���������L�Z�3�� <�������#�
��kre�����Uw���O��[�{S�<�������,��>�8�g�� ��[��&n������?�
��M'@��G M�	�4��X��&��DQ�X�'��$���^�J�����V�oG�&���]��i�1=Gz�4AX�%i��i�1���I��q�_��L�m����t��4��^��U�:���C�O�J��^�V���v���^	�vj: @�]�p�p�������mq��~��R��`D,^�0b����Ko����x�����O��H�������vFs�h]���Bl��ks�p�+7��o����G����Ivy�: @�@��K�i"/M���5�4�%i��k��&��a�g��JoY�������M�w�3�&gJ6�c�a���G��&6�*���J3M�.f���D� @�@�:� ��m+3A8������rk�\n:�.Oo�y�o��������G��.��pV	������������~x b��"gu~� ����� @�sH���d`�L�'�f� L+�����i�)��gZ��V�5K�I��g���7t @�L�p�0����]:v��]8���&�yG�;��q�]M�N1Se�������1��mo�4n����9?zYlk5N����#��������;����{cS\�w�M�.�~�#��'~���{�kv+�YV��k��*��Mq����/hr���W^�~��yf� l���?���+B%E @��E��R�� L������s��P�T�6�a;n1��������ia����<��� @���
t:A8{��M��?�������?�w\�)�Rd/��=��D|��������M�J�J�0b�;����xs���M�\������m�f��Cqd�����������x�������l�"�=��x���;������x[,M���`��g� l������7v�L���C�<�}�Cq�mw����S��� �������{s�8{S��#C���%~�]7���gNM�J�5h7 @ i�`Z�V�5V����?;0� L���4Jo�>0�e�|���@��O�-M<��,L��S���1��9q��X�1���-F�[���J_�V#��C�&!���mI�s����fV��#�DjzK��o�p���~K����(g����[����o @� �)�N'�$����7�7���:`���2ny��q����~��.%�>x�q��~/v���-�i�����>2�
��8/������q�Z\{�u�{�/��0=���q����;�^��������#W�5���p�!���n����k�5�M�-	�L�l�!@�M M��	�4a����L���4)��>4}������j�4�TO8M��4��&�f� L_?�9��� ��mf�&\�~M+6���I�n<�Q�p�}�7	 @���@��5��}������Kq���sc��������]�)��'u��@��������7���}��v���K.��aW��/���Uys�����?u����;�"b[\���q�;�W_�)�� �����+or:�
��y4�������;�������K/�+�sy���f�<1C���z ^����s�����_zs��7��+����%N�� ���� @���	�I�4Y�&���]Z����I�e������V��J��u�1�Db���
��L'M�Uh������� L_�x�49�/M\��z�a��z"4���Hr����gJ��}�&w����_S��o����	�N;> @�
/�/�������G\�wO5Na�
f��N���	 @� �	��t�f @� @ �����^{M������;���u���vx @��$ AX��t- @����wn�7���x��.�K.yEl[��v��#���w����c�>��������N����� <��� @� @������@� @�3L<����]x�]q���O�*�aW��� @��|H����� @��xfO���{����P|������Snz���������<.�`S��1�$���	 @����a��Kc	 @� @� @�,L@�pa~^M� @� @� @ W���.�%@� @� @� �0	���y5 @� @� @��\	H���4� @� @� @���$��� @� @� @�r% A����X @� @� @�� \��W @� @� @����a��Kc	 @� @� @�,L@�pa~^M� @� @� @ W���.�%@� @� @� �0	���y5 @� @� @��\	H���4� @� @� @���$��� @� @� @�r% A����X @� P`��`D$��_�K#@� �����Z@� @���g"�G��h @� �a	�;< @� 0K	�YB�5 @�� \��W @� @��� l��� @��Q@��!@� @��! A��~�
 @���H��]  @����aN:J3	 @��. A���~ @� P	����� @����a�;H� @� @�@i$K��.� @`q$���	 @� @������@� @�+�]av @� @����$� @��! A�E� @� @�. A�pCG @� 0	�Y � @� @��]@v
 @��F @� �
	�l��V @�^@���]�	 @� @�@N$s�Q�I� �w	����� @� @���H�'] @�@�$3�A�G� @�J# AX��v� @��+ A����N� @��$� @�]� �
�� @� @��� <%�_ @� �	�v(: @� �p	��: @��YH��� @� @�]� ��S @��� 4
 @� @��lHf��� @�����bH� @�r" A����L @���H���� @�E@��(=�: @�2. A���< @� @� @��� l��c @� @���:#�����>� @� pj�dp��jD��'��<�q@�q@�q@X�8�&��d�v�S����T>��q@�q��q@����~� @���@���Hb8��gw�lNA� @��*e�e�;����k��^�o^;���G��q@��<���m��xk���]��8 �y�*��v� @� @ S*3�C� P`��
���3��^�Q����`<���x(Z����������L&�� ��X�?���������{�[�������
�g] @����
�<��� @��Y�E��{O��?���|N����a|4�s��0?R�V��7>�����?�B|�G����qz����_z�����0?���U��� >������
�<�w�� @�H@a�:�� @�dZ`Ja���q @� @�@�$��.� @ S�	�����S/=����Z)��`<���x(Z����������T�$�#�x���>�T������������Gg�|��������j @�(��
����+'@���@�
��
��

�~����aa|������
$�A|&�G�Q|l����0?��2���+��������e������J`a��
��&d�� @�h!���� @� ���
�z����o$p�`���8 ���8 ���8 t3�*�"����r�K�?�P���n�a��x3��;�@y��
��$b�� @�8��
BC� @�@wT�F�o����\q@�q@�q@2T���*TT��P1��q@����*���u @� @@�1@� @ ����)�� IDAT� @� @��b� @� �	��8; @� p
	BC� @�@w$���, @� @���1@� @ ���O�������?$��7�c��?���(>6�>X�������0]��h}�y}�:IG���&��1~�������������
����w��0yZ� @� @��b� @� �	��8; @� @� @��LHf�4��H����k#V�V�s5 @����?�1�+��b���vp @��hxa�^��������<�����s��X�R<���|���Y��C�!q@�j	���]�3������>��q@�q��q@a���.��H�(��nWA�ng!@�F��������]���\�!@� �E�2�2�����s������V���P���'�����8 ���|��
B�f>�F�1n��q@�q`�q@a���D���� �yj> @`�T.�� @��N`���t���	 @�����u�,M� P4��
B�-Z�� @��l	� �Vh

!�����" @�]PA�ur'$@�(��)*��,m���k�6��\��0>�����SW����P��0�L�'��f�������C�
B�A|�\�������t�������*K�v����
�N�:6 @��*����� @ [�A����B�����LA� �f� l3�� @�h!0� �����V����q`���	���������c=�q@�&�?c��	���|J�q@����r� �v�m'u@ @�@)T���]$ @�@T����w0����^�o�� ,�7��:�]�qn������8��8����2��?��W��ye^���8 4�*3���M@a�z�� @��#���;��B� @`J���I���{-���&`<��x0�x8=A(~����1��a�!^����xY�x9��������y�������������:�PA(IL�mPA�vR$@��B@a)��E @�d@�3=��3=�0���=��}/z� to{qE\iw\q<qE\W��r�� ,G?�����n]�q@?� �@�V P4�E�Q�C����
��8; @�	Bc��. A�vR$@��B@����"	 @�2  A��N�M@��h=�z @��� ���� @�� 4 @���m'u@ @�@)$K��.� @ ������A.�� =��CY�C��Hq ��_�z��"V��"]�yj���kq@�q��q���Q���]�3��a��o��o|�������8 �;� �@�V P4�E�Q�C����
��8; @���
�:F=c��5�o���� ������zh?`?`?`?`?`?`?`?`?`?`?`?`?`?`?`?`?P���
BIbh�@��}�lX��Um?� @�X����TG����_�K#@� �����������5�oD��� �-�|���}���s{=o^������������@I�i�pt(������G<�C������q����c���?���~P��'i����������QAX��uA @��L$����� @�@�ZT������}�^������O�^����o��e��+����7�����>��_4���(>���qZ��a~����>_����
2����v�T�;A��	 ��auP @�@�T��]! @�@&�Tf�MA�9� �yj> @`�$K�y	 @�J&���d�r	 �
	�n(; @����T�D� �E�Y�m"@�@�$s���O�X,	���w^ @���	� ,Y��\tC@����A�(��a;�% @�dQ@a{E� �s	��w�� @�K@�p���� @�d�	�$"��O��y ��#�| z_��HV���X_�/���q@�q`�q���"=�u���f��F�5_��E�q@�sPAX����%@�@7TvC�9 @�PAX�NuI @�YPA���Y��s�f�
��W��RU�J\qB�(z�s}��8'���*�?�9��i|N#���8 �]�*����&�\@a�;P�	 @��%��p���� @�d�%�p�K��nHvC�9 @�� ,`��$ @��,
Hf�W��9� �yj> @`�$K�y	 @�J&0%AX�@]��k��Q0��A<�]<�|�������!~�.~�o����x)^�<^N$��?{��>��~��1>��y�����>�����~ ��>�i�S��o.���1��q�@������^m}����~Z?���O������_�{*�����;��1>J?>|~����W�_5�>X����������t���V���'���s�* @�@��b��C @�@w$���,(��a���� @��' A�>KG"@� 0����A�m� l;� @��! AX�~v� @��.�������Y�g3��y �7�|���gr���������!���8P�80���Q]���r�KD������{����q@�bPA��9Z
 @�@�T�O] @�+*���$ @�T����w1#�����*A�q@�q@�q@�q@�q@;� �$&@���<�d��uKNk��� @��#�>Iu4z�m+�5�0 @�Y� �BS���"P=�P$�K�C] @�Ki�0F��o�9]:�� @�(��
�r���&@�@G$;��� @���
H�k] @�@�Tf�C4�E� ,B/� @�@�$�o�� @��PAX�~w����aGy� PX	��v�#@����
��u�� @��E�E�@����a���� @���	�������� <���|�������KVZ_&�����/^�_���W<��q=y�@��P�m8�~�~��S>��~���q��?����C�;��Pa9���PA�Q^'@�V@aa��� @�dL@�o��F�o�5�%������|#Ka:��'��=���3>�O�������C���
�l����?�u��x ����'��y�
��el5�EPAX�^t
 @���� ���3 @��S`J�0�&D�������0���& �%V~o���� ~s�3��������������������}>���O�|��I�G�<��Q����� ,gb�U @��*;��� @���
� ,l��0 @���	4<��~/mN~S�^����g���`>�:LV�lx��_g~�_��y"���8 �;��Ax<�6�+��a>���uQ�q@:Tf,c�9(��
�"��k @�t_@a���� @�����wWM���
Hv���	 @��� ,l��0 @���	Hf�C4�E� ,B/� @�@�$�o�� @��� ,g��jtT@����N�(��aa��� @�dL |��!�r���\:����/��/����d��KV�8 ����]�����8 �'�<{ bd(�6�c��8"��'�7��qc��q`vq@a�2��C��"� ,B/� @�@�Tv��	 @��)�PAX�g��&������x0�����Z��^A(~����1��a��/�����G��5� ���>Z���G�c��G���}>o?����
�r&�]5:*�����N�(��
��v�#@����g���'�{2�'�����i�N�A(��/��/�a��|1_�q@���Ah>��E��8 ���8 t'� �X�Vs P�E�E�@����
���;# @�@9T� TA8����d����o@�o��8 ���8 ���8 ���8 ���8 ��T�HW_x6b��Hzz��:v�X�@���QY�>��W.�(^J� P������];���d��H��Q9c[�K������H����2��F @�� ���s{��3�#Y�E�0�=��,�@�c���_.A����� @����~<���=� ����-�����������L @��PA��_$�9`���|H����� ���	���l>g�����e���@� @ �*%s;x5���
Hf�o�� �5�Y��!@�(��
B	�2�s�H�@�$��t @��� �q�i: @�@nTJ�v�j8�� �n�h @ k*��#�C� P��a������Pyf_$��F�����0/�q`�q���ct�9Q]���1��9��1�Q�q�K�q�lq���c��d��g0���HF�� �*G��C�g���xQ�x�z�=qO��q�}q@�
�2$�]#]PA�ep�#@��X@a�;O�	 @�r+���Ee�
B�D�M��}�7{�7�T�?�}���u��7��{q@�oPA(~���|�������!������
B���nk8�PA����2 @�@�Tf�G�� @���e����.Hv�� @�9� �q�i: @�@n$%s;x5���
Hf�o�� �5	����� @��A`J��~��������g�G�nk$==
���5�_����a<����� \9>a��C��~X?����S����~����Z�p{�]�����Ge�9���r�_b��=���������zk���ZO��e]O�?�O����T�H��["��-C��5 @��*���` @��B� ,t��8 @���
HJfthjy� �s�i; @�����v6 @������@���H���	 @��� ,l��0 @��HJfxxjy� �k�i7 @����7wF @����O����?����g�E�nk$==i������Y{�%��y0s�{���|NT��?�M��uS�q@f�'���%��>�ID22���47�����{��W�����"���8 �����
B���h��
���:  @��*��.� @ �*[d�U�S!�RT�����
B���������?���#�e�*�=qO�+[�s����'���@��
B��_kyPA����n @�@�Tv��	 @��W�!��{�<����<j�G�����`>�*LV��0���!~Z?���O����O�n�P��B-AX����7<����c�3�/������Q��C���?�O�[��m*[$�k	�-���J# @��T��� @�J,�������	 @�M@�������q��o��7P���|0���x ��AM@<�C�P<��C�������T���~"b����e���ubdZ�Z���s�,[�G�|�D�u��#���Y�%���q @��/P9r4zV��H�y��Q9�|,y��M6<�`�x��8��W,~��� @�@��T��J������#9�U�mvu8
$�&�{"�5/����8z��5� y����{F�l8�@�R @�:!0��@���y"A8��S�l��	�������?��N4�1	 @��F`<A��6��W�L.�a<������x��P�D��'�[#���y��	�����c��	������ox�U����p2A1����d�p�x�'o4��'������M�7�������X�~@a�\�
��$�](�PA8_9�#@�hPAh8 @����
�v���������a24�*����X��C�P<��� 4�����'���d>�O��y��������� l��UA��l�3 �3�9�0�%@�dS@a6�E� @��-�PAX���3��W�7�36���B.�op�`��q`��g��O�E���3�1��7�A(n����!���8 ������'���-cD�g�e�����g�g_��u�:#���8 ���\��
B��N��::'���s��L�(��
�u�K%@����
�q*}��7.|�b���(��� qB�(��w'��%�QC:Tz�m_m_m_-���8 ���*Uf&[�!�L@a�:Ls	 @��PA��~�* @��bHJ{��::' A�9[G&@��H@��D��R	 @�2# A(A����!�L@�0g�� @��l
Hf�_�� @����������	Hv���	 @�%� ,Qg�T @���$��OUk����Y��W�������_:��r{��& >��
�E�����d�Kc��?���[#��)#O3zz7E���������!~�o�O�O�O�O�O7�L&#F���y��X�mG�����?�'����?����5>��~��M�7}^g?`?`?����
�������ds5��PA���� @�@^T����� @ ���y��N��b
H.��s @�
# AX��t! @�9� � ��p�T2% A���� @�@^$��s�M� �g��g�����A�������mx!����8p0���80���b����AX������BqC�7�
q@�q@hF�?�gm#j����S{��b����������_���q@�q`>q@�
�<'�����PA����M�(��
��t�!@����
���SA(�>���oj7�7*}cYe�����8 ��@��
B��J�>J�7�7}�$n��*�q�q@�
���5��L	� �Twh @ �*��s�M� �g��
�<_Bg�^�$g�*�YgN�����a�{P�	 @���� �I,�vI��
��xO���f��A� @ �*[��a^��v �5	��Q; @��*����� @ �*��3�E� @� @� @�*[�VF���3#bE��H+�d���{6���"zz�.l����:�<��{a.�% @�tR :�%k#9��������g���w��HN<#���zUDom��� @�@[�=�5k"Y�������T����_��%�Kfi�j��V�����9gkT��d�����G��������;��f�!@���@r����<{�J IDAT{,A�y(*+#b����3M��|~{�Y[$3��D�(�@��Dl�Z�a���g�����������\��C|���j%*���s�D���H�l����/���ckb�k�`��?��uT�q@f������r{$#�"9�����F,������^��C���8ksDom�������8 ���8 �3T|8b��H��.�~S�
�b��]�PA�}sg$@�P@a;�% @�r.�����tUA�'�q�'���I!��
B�T/�>A|�O(d\W�#�����fp��-��U����Y�S�'N�S��8P�8��0���4�3����B@a)��E @�:-������O� 0W�U�i����Z���B5���M��x0���X���9gk�3{����3w�O�C�?��O����~�~�~��~�� �?�������g��~�?}�>�������2��/�m�m�m����~�~�����
�U��oza���
�����>�PAX�.w� @��N� ���c @�,D�D��!<���AX�{
���8�^�s���A�YU�U�YU�Y'���8 �!x�����y��y���y�s0�����80s����Ws�Z�9�@�'@��*;o� @��� ,A'�D @�@�JTA����Bs%����[@�0���� @���Hf�#4� @`B@����A������	������� @���� @�@�$��#]l�a����|
H����� �1	��u�� @�DI��F9�a�'���K�}D,��k����^q��C{���<~0z�����#Y�%��ol�dwT������a���c����k>|�3�Iq@({�L����������������E�������G��9����4��#��<�q@��v����Gl������o&��O������N!���!@�h��
�6 : @�@[JVAX������L�+��19����7Q�����P��0QA�5���5TF���
B��|/�K�E������&����z�j=�^A�^���F�
B���2��7�7�7}^��J�����j	�za1��
�9e�mM�;EPAX�^uM @���� �:� @��B�D�u��=j��� �?��GM���}#�x0���x����c�c����O�C�?��O����~�~�~��~ 9����<;����EeeZAX���G��C�w���AX��_���e�m�m�m=����s?0��U��o� l�%VA�8��
BC� @�
*��� @�mPAX��1��q�'�q��o�r<� l�t�/��olON��|0����x �.� �����������	���pv�am�/������S*��o���T������EPA��NI�(��
����+"@��]���y����_��}��D�@A�����-Q=�/�5[#zZ=���.� @`��
��E2r����kz��g�F�Y["z�� \p3� @�����
���xa�>� ,�`wE�Y@a�A� PN���wWM����
�,��� @� @� @� 0g�-���Q]�d]$�}s��_���s���&��s��6q�����Q]�m�o
U|WH� 0[���g#�_�#Qyj_����x���^>r�`?tw,[f��xc���n1:[d�G� 0'���QY�:�+VM�.9rx������s:VV9<X�j��]�Fed]�������M�@��12�d��������/�H&����������_� @�� P=�/��/���<������h$����4Ax����-/�%G~5�[7Kf��� P@��'���[NN�4�I��ID�yB���%*#�#V�Io?�����iq��� <���������	�K������)���8 �3����G�|[�����:8�GF"�8�����S�;�� |[T��1v��g�<��Y��8 ���8 �7L&�j���JE�/��������[��� l��VAX���K"@��*��p @��R
� ,e��h @�@�T :�oN� �������w�z���8�U�������
5��yl����8�T���uA�q@�ZPA���mg������N�@T�] @`�T.v8? @��T�U�����N% Ax*!�N� pj	�S�
 @���
Hv�;Sg� �Twh�� �d�h @ g�9�0�%@��@@�p���z�~o���{-A�>b���������^��]_��������n��n|����/����E����dtcT�}���T@|_�/�W������(>�1>���b�'�������h$��B����q����-/�%G~5�[7G�������e���#~��Y����68u�0��3<P�-�����0�����|��E�(w|���4Ax ����x:M��S�BE-A�!*�^j}���X?���O����& �����x8YA�&�'��Z������'}���2��/�
�
�
�-�M�����j	��Q]�zbyME�/�����������+�����-x�<� ��m@t @�@��b��C @ s�� �\��� 	�d�sK^@�@�$K��.� �	��:$ @���$���K������nHvC�9 @�E� ,z�> @�@�J� �?��~�T����� �2�����7�9yOo���(�|�?�pk���AX����4<���0?�8?�{����O�q`�q�z���,��?�?�p$��������|�|��Xr�mQ�Z��f��o��o~O�/�q@f��|����q3��Uc��|r���k����k1�PA�mq�#@�Q@a{�5 @��-�����fpU�&E�0���u�����
��9O����2��yl����8��8���������/��>F�q`>q@a��j�
��y1�PAX�nv� @��� �0�� @��Y�D�s�)���Fedm��-����z] �.�� �?��/����dtcT�M��� @�@k����{$}��i/9v0�>��X��e
� �~��"@�h�@I��c?ST8��
�S	�w @��S� <��� @���@I�$��+���l�F�@T����� �5�Y��!@�(I�Pa����P @���T�J�� @��Z@���� @�@wJ� TA�<A����n�F�@�T����� �=���-"@��]�D	�$"�;�6�Y�����������|��c2N{�O.~�����J������O��xFDO�������dtCT����b}����j��������}e9�_�~}5>����?�L�[T���H$�a��1r�@}��X�e[,9���n=3����_�/�W����|���o���[�;�������b���4<��4����/�����
��g�]? @� @� @�@y���7L�?���;�:�����j�� ���k�j��G�}"z����"��o����^�ko���Yh<�D�Z��Z��r4�=k����+VF2^A�~>z�l��W]4�������.��g�x���j����(�|�PE����l�.^�����G���wX���_�jT~z(���mQ}�h��%1rxoT-���%��������/����%k���wE$��O�?�O���l���~��>���~�����,|����)��?�����g�h���+�O�;q���\6��0?��8��RA8:�\�=K7��|�?q�/]q��HN�eI>�����@5zFD�okT�G�aK$=�����>}�2*�����9�t�.I?�i��E�[��P`x(z�~;F/�$���d��@� }�����D��S��_�?��Xz����Xr��u���=|g,;mE,}�{�w�����l��� @��$���'c��K������P�X7����I���a�-�$����_T{�>� |��7��{��.hO{�����"N?�!A������x�o$e<�/��|m�*sHD��u
	B�O���-����������x!^����E|?r��_��G��� <=���mH^8������	B����za��^q��y����f}���6� �M&��>�!A������E�Wa�3�ZO�@7TvC�9�N@a��� PN���wWM���@	+������>�p���X��^A���L~�d��~��
��-�����g;��O�����������`T��4�b�o,^N�b������W�� 4���<�{����w��p�x!^6�qC��~����w���|�� ���:��V���i^AX����tgr�,~����8 �S���
���5�Z���e�
��TA��0�	{�#@ �*3�5F �*��{�N��Y� �5�_$@���@	+;�;���:{�6�-F���PT@�����,�����;;�$ A�%h�!@�������N����6����a��L�	�@@�0���X����
� @���m6� l3�� @ ���� P4	�����!@�@S	B� @ �%K>Y���XG������t�+s����'��KWE�~v$���i���z�j����{x�d��D�����m����l�IO�X��|��F�E�E��K��>r| z����g�x:�Pc�Z�O[���������`�x!^�����x/�7��'���}�����D���H���?��Xz����Xr�������x��;c�i+b����[_:����}�x+����8 ��@;�@�w����=}�c��X�~b_Z}�P$}K#��*���dp�@9:������N��EPA�H�NK��*������*
 @��,
������o����
���t�y
�l��k�h�����?���l� l����^��?5����N�/��(��^A�5*�*#*�XCa����*�r���������c	�z�xa}������u��]����o]Ah��^X/��������&��o��o�
�K�����
��h4QA�z|���QA�"E=� �Ioz�<? p��
B#��� ��C @ {�K����	�m���f���� @ �%� ��=�=����q�_��6��������/� ��[��Q{�c��� ��E�^,��,�����Z_=�0c�������S�[�n��'��8 ����A����g��� �	���� P4�E�Q�C���*
 @��,
� l�7�T�f�oV���AU�y����/���;>�U��P������G;��w^qK�}��	�+�gq@�q �q@a���*��p���
�Lt�F(��
�����!@��
Bc� @ 7%� �l�Hv���	 �8�����
. AX�vy�	LV�+*���d��x�����#�D�F,9��Fu�����3���"������u[$=�H	 @��M�d	�i-sG$;���X$	�E�wZ�� ,v��:�x��@� �E��%�� l2
���?����8}{$�}Y��D��E� \�pz�� ,f��*LPAhH @�dQ�d	B���a��6 �-	�l���(��aA:�e @`f�F @�@J� TA(A��i�Md_@�0�}��r( A��N�d�]@����� @��%L&��i�3����b��Wv�<�l�������"N?;���������N_���|�O��c\��V�g�`T��De��H6l��'�%s5*���w�yQ9�������%�"�Y"�vp�5N��B�������[1z��B�����R�,D�����n����5Q9t0�����O~6�yy�h5����u��7�9�{��Xv��X���D���N�?��n�?��g���8 ����?��g�B��
���b���}i��C��-�X���W����Wv>�� @� @� @��F`<A���oe�hT��D���r�M����#Y�,���#��U��f@g�	_��Y��L�o_T�l����"V��� �;��O����ylj�>�dT�����5����z���������;��^&^���x!1T#*��������_x[$'G�wET�A����eQ9�5'�u�����8�������5o~WT������G��}T��S��G����6��?�R8����%�xU������5���n��������KVL���G5�Lc�d�a��i]� �=�G���
��M�2�3����]{z�l�I_o&����@��_����G��@$g�IO-^�����_���n���$��=��������v	�C�@�*������Yk�� @�@;��������������������������U�3�,*�\t�����q�/~����d8�x�_D�gcD��z;��1 @�jG�����o�<zz�Fl��ik'h����=�n�i��N�9�|cn(%[t�an��� ��������* AX��u]�	H	 @�� ls�� l3�� @ #��� P$	�"��k!@��t	B�� @ ���S&Z������N�����J�^_� �=���p�����v��_�G������k.`~�c~�N���_~�����M|+d|���x��3�bT�?���?���e������f��h%��33�b��`}�>X��^|��B��f}��u{}�L>���3�bt���v����_�m1��b4�)|M#@ 3*3�B�8*������T @�PA���q��6�:2" A����E� ,Ro�L� 4* @�2, A���� l3�� @ #��� P$	�"��k!@���1@� �+	�6w�a�A�� �HGh�"	H�7]$� @��\	�,A�D5������Y:�G���
?���v�<�<����F��
��yK$}}�iw^|�3�@?��S���W���E���H��IO-^���?F������mc�����=gkT��Os����������r"���#6�^�/r��A�/;s��y^��1��E�#�?����������������gdiT.9y��V��>/~ywT^2g��/���!"�~}^�P�����������q@���-����EO��_�i�&�U���Hz�D,Y1����#�"�o��<L�L��u�GaGy��&��p����@qT�o]R� 4 @�2,��p�����TA���I�8��g�|6O�T7�7��q3��QA��o������x>�8��P�W������;Z��G�Z��C����z�"� ls�Va�A�PA����EPAX��t-�.���� @���@	+[�F�(���	����X��������������k���7|6���_s����u����_�H�����1z�YQ]�z���O7�G|s'*/��j* ~��������o-��g����eQ���u`�3�sTz6F$������(>��9��>������|" IDATo"`}��w}�L��A�vblV�=Io�����w&�A�17�za���� <=z6o���U�0�inM#@�@Tv�)�M@a�z�� P6�e�q�K���@�+�f�����
����q}<������}�k^A������x'���o�y�	�C���)���*F~8����1z��*�w������~`�~hZ�� >�����h=��z6� <�������yq_�.�ZA���>?�^A�Q��0C����a��~X?�OsX�&&����a�(�����0����� �^|QA�"w��0WI}�%@`�T.��(��
�"��k#@�@�
B�� @ �%� �L��~ag����J�C�1(����AXt�G�����Dll��y�	 @ 3&�����A8���w���wG�%�q���>�0�
 @�y������c��� ��D28�Du��N�����OZR�����>�����OD���-F�{������'�["����Ig|:��v���-l���W�5C1�G��_�����r�~��4^���9����-o���=�[��i��b~�_E�_��'U7~���V�����'���9������(�^��b���[��$F�vo�������Q��~������4A�����7�s�*����u�z��=[�b3����&!$/K 	���
!�
�$����{�I �I0KV�
��8	���xl�/c��x��v����*���GR��I�����*}�?���:u��y�sN�S�zJ��W��������H|$>8>.��+�����g�����r����su��
�����a����>3����O��?������Og���d� l._Z� �*([E��B �P���
�@���+��g�8"|��a6A��� @h-�6U�WS6������b+//y�������_� l���=��~y���������7� |���e.Z-cURExL������ ���������o��l}�X����xD<"^/C���)	��H��� ��s}�U���_��	��,���+�����"��q����[����
������PR���+F+)���� ,I5��/5�����J� � �	� �)
B���2�� @�O�M�������W�A��v+�{��vS�<�H�JYk�� ��<�k$�~3�w����n����r
BW��-P����`/o�G8b�s���K�JkO7����@����������o���(��f������@
B�?c�`� ���q��8�W�^�5�@
�.���W�0��,
B2�� @� @� @��?�&���V�P��y�������@�3��=.+[/��,�Wf���TBJJf*�w��U�y"��~_�����q�92"3�Z��I��.��r��yY�(M��y������Cr{{e����s�t�K"�eJ�e���]��xA�("���y�y��d����D��g���7�LLHr��T�:.����j�9'W��:�����h��w*r�V�s�R������Xw9�3����X��M��
���y�<&��"����s�L<#Y�RdP��]rN�5�J�X\&�,������-������ t�s��a���������������~�N?��L��r�d&K���� �R��?V����s�����r�U�M>��r����|�d���Q��e��Z�_N�@*%��Cr���t�.B�@�����5u����{���[d��V�T���+�#��S�Y�����Mc�$7���_���}\}/��+����;���J���n8,
���E�@� @�F�=����W�DOH�-Rd@�����!k�o��'d���.�LNH�i�E�gj��H� l��@ �H�tt�%@�����g�� �A����+��Y$��m@� �#@��c�@A�1P�� �$}b��0 A&k2@��� ��%�����w����!A�A� xN���HIz�� ��	B��n@ LH�����	B|� @ P�,Ax�U�jT���W����}�����7��|
�N��,��z�L�� �O����?��~��g��#�sWI
��2���� � ��-������o��
_��d�����>�����,���W�3�YK��k��W��5��D�)�Ax\��j����N�x[��0!
���aI��� \+�������x��3�z~���%�K������s[I
��Rdp��%��\
�)�e`��� �a���t������@(� � ��x�(�@� �cm� <�Mq6��+F����!��	�����/���-#� �%@�?� @�1����@i��O� ��!��D�a���X � A�W@� ��@�%5�J��g��!��+J����y��~��]'}�����R�w)63 �,� td"}u�_o�~}�������Ks�%_���r�5\�AxT��/��{��y��w��P�AX{��n��\�����J�� $^0_�����u^���s��s5���iz���{�-2�F����~�Z
���:�^��{��T��q�HI
B������8@ �����@��+e�'�X��]��O����O������������7jR����z���
B�[��A �P�ht�@P���B� ,7�?	�����4|8�$O0���NE;� $n1�[��_P���:����y|D�X'Y'+��(����r����y�� ���(=N�R��c�4@�'P��ta"��0L�d,��	� �+ @�|L�
���	����u@�"�p�p���� #�~T��a��l-W>� @��� n��;�����;&�\��c_W�G���5��f��	����b�����/����)�=��W�F�����_+�z��8,
���E�@� @�FE�	)�E�����a\&���}�grBJMK�+j<S��F
Bj��� X(k::� A�_��3@^@A�E�� @h��EA�1P�� �(}b��0 A&k2@�P�� @>&���c�� �(�A�	�>1��@�� �5 �E�����Tx�hJ3+�R���,y�h��� @�<%���S�	B��� �@A�C�
��	�0Y��@X$AH
B\� @�_�LAx���E�]5��k����t_�����'}ZV�S�:���:�K��i�z�LIrd"}��������s����q������K��>[�����W�d�;rT��/��{������-w���~�h�[��	�">�Si�Gv�}�����]���Ku��&_/��?�)���4uD��WP�J+W)�����1����%k@��J���}_��o+Q&���I���xq��4�V2�	�	�	q�8@ �����@1AxR�m�"�r��R��}�6���xB&�,oorRJMI�+�ofl,� l�as��: �V@A�*��!&��0��eh���Jfd&���W��P�� @�"����'
P��.O�6O��vO{�������pi��?��>VXQ�IC�����XCq`	
���_���}\���P��/U�/� d2��\W��BIT���8K�%���u
B��(=Js�|B�OA7 &(�dM�@�������K�������4� |��)?�����:�dol�� @�@m�PAXSx"�������_4k}�/� ��m�x|��C%5'sOk����'X�O�@}�����?��Y��)&5%sd����7e�jfk�2_�?����wNA�������Y���P��������9v����Okf�[�����2V,W��� ����YqA�����\
�B��������e=	�z�����^�z�I�K�
�9�w�� ���Kk`�N���+3?��amyY�@� @� @� 4��	�j�.t�����g���2C���{i�!��?���XL�H�6Q<>2��2V���r�S��,��W���@��Ck���jO�b�����?���_~�_��A���2�))�%�FJ�+u���FR�_�_�x\�g������ �b� �����/�K�}:����3�2+7���X?Jk?��~U��d�������q�NIg�)3vH��C���
r���"����O���L�G��Q�|Z�X/Yq)�J��e������2[_���]r���Y����;/3�-����Zc����{����������u�:�d�~��W����
���~�G�
���2�k�D������Q����N��.E���3 �s6�42*��r�e�g��gY����A�K�jv������c2]�%	�����(��B���l���M�u
�@ ����S���L�����k�����Y��?��
��%�=���M���E>� `�����"�z>� @ ������qf���S�_x���E�V+v��JG���I��E���e^ Y	)�H�v����>�������e~�V���g�Y��R�0�>�� @��\��a�h�AOv�.A�F�����/�^��Yq�\�������;��.����{Qf��%	�)����B�`|H� ���K@��H��,t
�&@�0�����#�����?���_���l�R�W�%A�o�@X$�3'Y$=����c�4@�'H��ta"@�0L�d,��	,%A�W���_�����"���� �� @� @��f�� A�	�b#$=Js�|B��OA7 &$�dM�@�!>@h$���m2�6Kp���g���������������i����c�e��RGwY��b
�
r��������Ym�8?�a���R����s����^#3qRJ��\#�?������ �)����l�(��8Z�X5���W���:n�����"���xA�Ph����-8��j��r<,�I�� <�H�E�����s%5��x���1�qe�*ce���wT��5��XOXOXOXw���@������d���7�[������V��^'c��Yq��������Tw��RgR��K���*�n������������7fP���m��g��@3� lU��@�@A����!�����71� �v$�����1� �=QP�T{����WV�~|�'b�~�;vl	
���?����6���>�'�s��RYAx\#7/� $~_�����k.AXMAH� ~?���������#�t�^@A��2�
���Q���
B�������n�����J
B��Uz#G��/� �^�[@A�Tt�#(��!
��&� �P.dN�v#����,�x!�v#����,�x!@�-� l3�� �PAX�����������F�\r�
y�~u�\��b
�dY��;��\!w8[�pJr]YV���_|�gi�i��o�����'@[;��
�Rru�K��
�B
�[�x��a�l�����j��������?W���U�H� ~�~�����{���R����c^��A%~����g�� �Y���e^ YYa�����Qx��;�����V2�7$a_����/�o���J���G/��������-��ULj�i��NI
�.�����P�p6�42"�S������DA���M��h���J�hs(��> z(Cob@�Ha;Z=�cnc��}o�]T^5[s��:��_t���+W��� ��U��d�����5�	x���������a�
����h�'����]��n�|+S���o���L������o�z2� L)�g�
���=u��Gem^�������d~�l��z���yoD"�#���7�]�7��n\A8*�gP��
B�aM�3.@`� \���Fa�Y��B�Fa�Y��B�@[@A�fn�A����9�^��9���Uw��L�a������j�z&�@ ��AN�
��	B � �Z�p�b��t��������5������Bnp��-�^�2�X�A8��5}���A�O�a�M���� |D�u���V���P���7���j��+��ww��4zP���+F/�,��F��O&����^Ev?�L�
���F�%��e����������?����'_ 3qBJ��������Y��?��
���q�zH�M�d����Q�5�~���������<����O�K���~��~��B�_vZ��G�83��}���E��	���$���������+$o�k�g]����� d���
��Z��;��`}o�����/���D7p��G��5v�h���2)��)�;��v�;���K�3���l�p���������N��b����w���_g���d� l._Z� �*([E��B �P���
��$�� @ �P���m;�6S,��������M9eM�.������W���/AmI��miv
�� A�\��@���A�jp~@�@� lT�ljz��@i��O� ��!��D�a���X �@A�W@�BH�a���Cj3a�a�]����_������p��]w�xI
����� tdY}
����q\cv����Mc~SL����$�H��ib�-%5cJ?��"�6�� �6�^����'���N�� ���j����{��`��� |X��*�AxT���*� �Kf��R��`�]�aL�g�QY��H�b�����?X?X?X?�~�_��z��AxH&:��~�����sw���/�N�>-��P��?dO=�������Lg��{-�A�J#�RzF��y@
��n�#Y�
����L���a^1���
0p@`Q(E� �z	� ���� ,(�e/z@��
��0��@@A��(y2�'2�'�'A�'(�� �-���~��00O2���O}O=�����KPZ��M������NFd���*�����Pzf��
�b�%,q��q!�G��>BA�q���9@>!���'���a�&c� PN`)
�o�.�w.���s��)r�Q�? 7�
�>��#�6��L6��� ��@A�\�����PA�\�$����!��	�V���1�!6.C�  i		B:.�k��=����Q��;I�T� _ A�3�	O� �#�@� @� @� @~#`��d_��O��L�C��-�?'�[���4��9���X��\[�B��~��I)�B������`O�g�0@�#�:Y������9FV�G�L�M�T��������N��5����~�����A�p��2c���D&��m� ��0���S'�9u@���e]��r�x��}i��}r���f�����V���+D#�����r:nP����7������9Y��5<�m��� ���k����Lo_qX�+��9�����i���������C�t��v��d�"��.3sD����h�\��3uX&6 M������2�D��[�xfl�w���PL�p��=5"���;����.�VRL���sM4�?s�~����d����E�����E2�u2g��:����%�/?��������?M��
�'��e���t�[e�Td�Z)3#g�I�^yS�E���r?��;��-r�7��I��O�$���~��l���w��o(Ib����(�Q���R��I��<��fR��;��]r��������vM�}\Z�J�g�]��?V"y�\��J�D����8���x����
�������v IDATSG���>���s���T�����;��?�%��H	��p����'e
e�����+9���y������|8��[�9t�2S3�������5~�����"���}v�"�M2���������<h}������*s	����p.A�P�ry��O��� ,&���������A�+�������������z��b�~5&���������R�>�]� �������.%�W�%��?������b������b��	����Pze;�� d	d���%	��/��!� lg�������)�	�[�H�$A�y��PnZ���� AH�00�JG! A�/{���	��X�q@�L`)	���*2ug1A�w����,	B�
� ��H.#lN�$�����+��@�wH��$t� @�0fd�� A�s@��M�a��G�'�f	�B
����=�95ZR���t��%� ��<�W����9�@I
���G�����PI
����l������	��?`�+k����C%5�P��o����������� �{��y���n��a�n����<���m�@k�@��c��TI
�s���-P�0�[�+���N-�A������{����q8Lq�X�p�x���AxP����'���|
����J�� ����%5�d�}���+�>�b
�`�=^1�+FO��
@� �- �f@A���	@�?P���� ���P�� @A����(yb�'&��D��p��=o�\AA�-O�������(y��xH<$�;� �}����u�u�8�8���y�y����t.5�"I;�|G��LB� 
(CaF@�*�8 @ �P�~�~qm� \JC� A�6� ������5*�z����3�dI
�'sC1���/���)C���@	� l!|N
@`����-�9/�o����M�g���R��o���.%�W��x�g��Rd���^��[4u�g*��NQ4�v��.�00N@h$���m<�b��P��_�s_�5~����H��\��}v�"��2�d��m�JnZ������!5��t������@X� �% ��P�� �` Al����	� \�Qm�@AX'�@ �P�ht @�0F����@��q( @�H��t��Pz���W$i���� ��I�BA�a(��  T%���� �	�`���/N���j�	��8�-@$��0�F���0]� �(��C!@> @��F�M%�f
�g\�Hrg�w{�sjT&�'w<#9"]vis��`����v�������s�R�C2�u2g������W�o���I�4�������r[���K�����C��^+ef�L>��+��[��{�������4�En�f��S{6e����/�e����e��[��7�D-�������}����_��{��y�1�fJ��;��]r�s������vM�}\Z�J�g�]��?V"y��������*2u��
!�w����l����:�h|���Z�?��1N�O�;��8��8��i�'O�*^�����AY�7�����u��/�������J���W�J(r�k��w�^�)�-�t�>��"�M2������1�M)�<0cc�a�>(��f!��
����@ �P����	�@�
B�� l(�m?z�8�6U��4����aAAxT�l[I����sM4�_s�����D�����E%
BGfE����?C���x��?����p�����8� |����%
�'{�M�N����wg����g>1��4����?��ad6`�����?�+�������W�D��,Q����va<�TW��/���!�O�'p?������ki�����
��R6����dm.UN����s��+�Wfj�DA�uY���(����.QJ�=Z� ��� �^/%���p��o1A�^���\�� ,�*�� ��P���4�Na�s@-%�����99 @`�P.!
��@*)������ ��#Ya��G����i��b
�B��8�|��P��� ,� l�|�w`s|����F{�
U��Z��U��������P����l��A�i�A���'x�������A��b
���D�
B�E�����W���}��M�a��W�����Z
��T�_k�O�'�s�����>�����Z��y�	YCY������
�B
B��)���z��P�0)�d�������57��'
B�%7��_@�@AX;+�	�Na���& � @AD��g@�@�
B�!�P�=�]�	����{Ua��W}b����+��{�[@A����?�A��w�Wz�>�)��?����b�����[YA8#g����0���=*7��b�~��_�'���������
B��l��o����)�������8�d�US^)cb9�B��������B�g�5�����,*-���7W0�/��/\_,���W{��b
���\�q���G��u�L�fk�+r.[��� ��k�P����w|@A��0�	�4�
�&��Y�9�m�=��(���T���r������,M��o+2�SG�g��^1z�� �,P.fN�Bm� �����L�_�x��6�O�������� \D����	��5=�@S	� l*^� �rKI�M)v�#2�}�t����/j���H���t���	�v�v{��a{��QC� @� @� @ ����Q��9����������u�j=����^�6���:��RVJ��:Y�t�fA���:+E:�H�Wc�3�����Q��Od��J���2�nE��sZ���\w�����O��n��O�`y�]�~sB@�����^�C�d"Q���A�@�2)9'�zbD3��R���^��g�DbJ=���u4s�[�< khH����O}K��C��*��z�����S�'uNV<�{^1Z���Df���HT��+��e��<&�U���R����3��e��:�c�4���S_V|�R"}^��_��^9�o���'3����n��HJ���V��z�{QszT����Wl!�fll�[,V]��mc�wFN�����H�q+��������U������q6AXZ4�(��^M&#�
_.+eK�#�
J�#RbP��K�|�v^���8�x.<�1?.��pl%���f�}�����m��N��=:������U;��������Vl�M.�n�����~�?h�y`�����t���D"��!��!q lq �V��ne;������U||�2���Qu$��cz��_�v�����s�+�����C�5��?��N���7��n|��,�����b�!Iq�8R����e�2�I��`�	�:�x��\;-klT�������C;����\�WW�L��:�sr��+��d��&������[�&���xP��[�����3��I��+76}zu���
Bg��������q�(�l��������$A8���];4���"� �K�J����$��ZB��(q��h����>���UkMt�M������$A(�{�.I����
{�$A��0(f���j&�I-� �����
	���p�{��S���*��UJ\��RrE���GK�AAX�a�����c��(������@VAh���]=�-Q����UW� ��3������?-I��a6Ax�����)�;e]�m��P�������|�g^@AXh���c���������F��_���	���a5a�'���b����������'�UUAxa��Ba��(�7q}�����o^��B!�����l��l����&����0�=��7*s����V���^���a��|������5~�W��G�����:������
�B�0���+�/��~�*^���jS�������W�/��BA8����k���� ���A(#�����Aa3��& �X��p�aq�(�cr?���B� ��@AK��F	������Z����^�AXx�h����_���������%g?����A�}���@
������s|�^����?Y���o��GNAXx�h������^���?���9�%�H���A���W�x�?e���)�����_���K>�P
��	���y:�k�?����w������I��Q����
���r
B)���j5�������ra����rf�� ����z
B��?
B�%�@�(���W!��	� �_� H(i��ua�,�x ��$��p9is�VhCa�'������0%�9�h���pi���d������%_X��^M&#�
W�A��8�^��;^�}�v�������,�U�A�G'^�����j��r����������������e"� �_��
�
�@��@C5����)�q����8BqU]A���3�_g*�Z��^�����R
B�{����|�T[
�.9;�uy�������
B��H�sN@ P�����#���6�G��$���K��� �
��0@��pl[�����Y�M�������"@��hCas�U^�������V�k����������R�������"���a�BB`�aAAxa
�B���0`���r	�;��
5�?-B�@	�� �E��������}�������U�~�����>���SR<�=����9��\���au��sR2[�p��� �*��@`>�x ��	,� ,� l�}��@�	,^��Yr�a����I�]9��!�|~36���K�}
R���������-'�}��E�������Q��[���]TAx�+F/L6~������f��o)���4�����>���UkMt�M�����/�(��Q��s���d
�>]F�l�����jR?���~!~??�x}�l���+F	�7VI���aQ\�GK��-
R�������_?*'����E3�/��������|X� ����
B�+�5X�u����IV������l�9�������W�J�t����6A�@~p	BA�x
@��P��B��$��0�v���j%���VR|��P6.MC�'��0�&n������l
�)����GNbfaSN]S��)�u��A^1ZY��j���$��@M
�e�'� �P��+���$��C!��'@
��w����G&FA�H�� �G(�h���@A|2@@A���	B�.@�%��0����5h3a�a�]���,&�5+���f�w������������ �|d���LF������"��Z�����r\cv����M
~3W���JzB��!E�rS{t�%��-�����=�Kj7�����u�!�j�����t���D"����2B2�C�y������������T�A8X�^�'FKI��#�Ue��9{ZV<!���?����7�u��N���z���3��%�z>�qu�������'Kk�Q���r]rv<XR���9}L���]�10q��7���
f�?�];4��.�b���x�h�Yv���m`d��@A�����2@A���9U5(�
@�@A�8;��=9����Onzdg����<���']�e^� �IS���<�8�����[���p����o@�/
B�cj�y�,�!�'�b����M����CA�Q"����@~$����V�O>���!#� ����� ��� X(k::^#�6T�H���� �_� $$�d-�
�� A[�S@� A�5���	B��� �VH����r�$=2;	B�@� ? A�G��'�	����@X�	B��H��t,��5��	�A-�5�����L`�a�������
���]��M��x�tX$�3'� �2KHN��+����u���*u��_I�����'FK��jj�h9�O	� ��a� $a&:�$���P���@~$�H�0[�y5	B?Z�>A��H��>���R	,!A�z�6���������?��$�j�6<�a�!C� A�J�)���G i��	��Q?Z�>A �H�����B�� TjB���L�_Wd�K������&A���M�a��8���8C�	� �� @� @� @������}\s>��K�+��+�9/7�N�8-u�JV�9����'v���Y������A�gF�Bf����2��2�y���d�����w�S�s^�o��=Z� �L�LF�C������8�$%�t��s�wr�����s?�����~�!j.�y8
K���S&�������h`�A�!@�
���}L3G�V��!�^�z�v����(#�����U��o�s��"�^��+��������>rH�-�(~�U����ZWv2�>+��!�X��9sF���v'!@u��:u\��!����������e����9&��R���u�N��*v�/��w����%{Ff���7��\�T��-�Z)ce�^Kn���2RlC`�65A�<�S��m��RG��K�S����H�;�/m��b�0u~���{����|��qe��eR'��z�M�#R���q:
@ ��e2J����[�q~"i(� ���U�C�������Og�v�5�H�:����	���SW��I.J�/@ �S����if���|�cZs�����W]=�����n��h�%J����Tl����f����y�?u�C�s�Tn�=��u��KY��Df��"�l��x�2���r�X'O��� A�$�4��@�~���'�m�r^�p�����{	�p��-Fw��W�/�J��%���\�0���Q���T�c�Q��7��v��D/�� ���	��!	������	B���"  � � ����g�����������g����&Ar�i��H6�*mB�B�a�X�}�Y)Ah��'r*��_���o�1L	�}n�����F���<���
[���8SG���J���������H������s��S��s�i%��Zf:#�qd�dR'���$����I��:����&��^�|��yOx6#>{�Wn�V��>���M�qI)�I*3�G��3���+]9���Y�Qf(+��~��b<���G���N���&nWW�f�<�G����:�:A�(��/7��fFGP^���~S��[�UnjZ�H�~�W�z2���"k�k�G?����5���ef�(�X+){������urLnGBnw7����~�����7��>�K�~���q��]!��:;u�����^&���s`�������%
�w������#�?��J<������5]�S?T$z���&��p�#�;#���"|P� l�G9 �$(����!�*P��BN`	
�S��f�������!�
��x><��#�A@��� l#c��P+*o������ah^1��!Ot%�O?���E��	!�������w(��f��
���|�oAA�'��<oP�.���������o�>�W��l��
�G�a����6l����:*u^Z����Ggi�w��%���H@��	� lk�3x4�
����a@� ���vh�^� ls`����� \>�6T6�*����u�E�0G�>%�#R���1�Bh��	�B
�.eF��� �+wvg�v�5�d��7�74��	��X�q@�B`.AX�A�mM|�k������3�n��hg��;d����g�M�H�����5?��^1���G�a}��6 �R$���(*+y����w���b�P�p�\�}�h��P��>*�)�x�>v0>� DAO������ ��I�BA�a(��  T'���o�$ IDAT���>0]�K�a`MG�k$���FP�}
�b�P�@��@>%������[8�7 �� ������@��2@��@h	� �i�,��5k�+Fk��W ?@A�G��'�	����@X�
B�H��t,��5��
�A-�5��BAX!���
B��nA �H��t��bP.F��/���S@�%@�0��e`��LA���L������<���
[������2��i`�dY������;~ m�"�.[4r��S��s�i%��Zf:#�qd�dR'���$����I��:����&��^�|�9�	�pmF�^�_�[�����o�5e��%]�H&���e>�������\|uv�#�f������7K������#�G����9�������a��}:�"X�{a�P�N���~Z3�#:���i���k��_Ut����N��}���+����}��Vl����.����U����;�����Z���+�&{M?�O23GI��������82�?��cr;r�����M����:�@�~���q��]!��:;u�����^&�;�o�?���*&�(y��d�{d�����G%�GY����.��*�T�Z��#nzDrgd��W��X6A��
����� �@��P��*�	�'��0�6d�$����>0]�Ka`MG�k$��
��Bfw���'
��R��%
�����w���=w��K����:�S���%
BW���DA��Ez��9�����B������O�,}�a�'���?�oU��I��p�2�������k{���f~2?[�������a����M�3�W�'�Y����9�>��]@Ax�Ro{G��pFX���0�����Ef�p���o��1���!��p}��I��Oq��WJ���:�#u�^W� $����W��!���P� L�N��DA(���%
�`�?
�
���Xr����b��52��\�� <)7�+�B�����~���>^|�
��1�@�� �9 �r(�
@A�#�@ �P�tt�Fm� ,�)d���w��+��
�B
�u�UZS����m\[{���aq��a�a!AX�AXHz�+� �:�
B��=!^��9��O��/y�C���W~N��g���]��d���w��k��u8x������%^k�^�d���?��]�?��M�]�a������*�� ��"k�
����5
�6���]M������O|b��c?�~�x�0� ��b��
�B
B)� ,� �~#��{��\A�-�����P�p�\�Uj��]�A������ DAX� �!�Na���& P;������Ia ��N� �E ��P.'m��
(������P�����������p�de�.XZ�K=����(u���}��y���
���.u��Z��?���A���
��2#{Pn(�=[�	D�_�]��)n/��G����������*���Bm�#��@��a~\��X� �]������)��MM/� ��"k�OiWS�
�37���f�wQA����\�b�W�W��<��b��X����\�q�������������[c�(Q�"	�9!�@A#2��
B�.A��
B/i�V�P6�� d��L�+F��.�b�:�--��%��
Bo��<�S��mR�P��R��Ai�P����������	�z��}@9%��3��L��B
��2k6�j�@e$�@!'����7�R�c�s�b[/�����l
���23�I��U���)���	���q �9$q���� ������O+�������X��Q�M��5�O
!	B�{)=�|I�/�B� x$oB@`aKH���Ok��o�k?��7*��I�su AX72� @�h������S����}	���a
��S�="u\Y���
 � A�[���	�0Y��@�@`		BwrBg?�M��7�����������!�V7�u#�@$���!P1A���d���(���S��7�IA��[��j�����r�V�����(��?��/��q�8@ ���q�8@ ���q�8��8��W�:�Y�=����rG(�L)��9c�e��d"-,V:sF���:�[n|@n�pY�;r�Y������g���r;r��Sd�j)��M�|��43)
���=Z� �L\7#M���~���)�%�+c�~��\/��x�����>uH��EW���<��%��8-{�Q���X��vt�$��wJ'��(�V�z�
������sg9wL���9��RFq)a+1��Ld�2[]�gFe}�9��D���k&s�����x�b�l�R����:��=#��Q�G�H�q������� �I���r���\�LzT���zk����5�����u���C���}��d�UT)������(����9sJ��
�<�:��n����z����v]$��i�D\�����5cc���1T�l/��l�p��;����r���9��+_�Lv��aUI�������?����\+s�n�o��������{��C�(��k����*��S��QE7l*I��|�|�3yZ:J�����B{K��>��w�"'��'�D�s�~��i���-g����y�������X�f�vK������e�(5�_��]��d�d�T~r��J|m��%� �|�+�~����.,K��c~0?�{~`�����	e~���>���s�VE>�G�����P�w�F3���IR��US��> �Y!�9�V��G��we���\�����J��'��gr������\�\9�������r����C������}��9#c�eL��?���O����\����I+��.E.~�f�?���������eV_-Y�����7�?��9������<�a�]�I��r�W&����3���U%��>����\�0}�w��r���r.Sz�1��E�]��ES�����9�tI����
+[� 4��J�[*+��o��O9���vt�9qL�
�U�?)]T�4���� _�*��Vd��rN��zeb�
	�Q���)�v
B_Z�NA�_�	����EoEA�/��@5p>��r��_+~�z������	����������"_�c.Ah���*��������jE�M%	��)-��k
����^_t2�e�
��2;��<"�Uf��qI��%���	B����8����$��SN��w�~�%n�E��^Y� �B1�W���J����Td]���
����*GT{j�_\AXx� �1����ON�w|^A�F���Ya����/� L�d��;��'�r
�B��;�K�S�hY��rO�p|�����3�T��MAx�2��	�Ra����?����g����_�2gf��!�'�g%�����}�)��������������&�W�H�{P>#��&K�?��
W+��[�<��\��m����rVo)�>�� d���y��� �?������������Wi���g���(����`����p���'g�=���-�w�������W\-��T����*	�Ra0�a
��Wm���"P�"�@3� lU�� ���?��E�}
)�'���FA��6��P�b���~�}i��
�����a���PV{2����� l�<�k0T{���ra#5K�K�����_�I�%���������F�L;�Mm
�J5�+�����Q?/*+� �k�\9��q����q`��B
�J5�Q��{u!��x�.� �?����\/��`�[ea���~������*K�*+� G�(��d�M@�
� l#3D��
�@���<&�x����i�N(���!�@A�;��@*�7�����c�S����sx���iu\y�2���a�L�u�J��!���2����&�Wt�a���*=p����r;:��8����R��h�gJjz�$�@X��Y�f9'�I��2��f��>�;�.t|�P�p�bk7)�j�rt�s@&@�0�����f	xS��w��QE�p���^��
�S�m��P��}rV��Q��>$����!��� �� |�d���?���*'Kk>O�7t���� �����_1za����������	��r{�������O��G�������*	���?�Ls���
�%�?���������D���f�e�G�������/H�.If_1:��X�!������ ��*h|~��_Q������?�H�q���8������ys���)���r
B��/�*'���������cX��'_�l�pG��Y}u�!�����:?A��2�J�-$/W�d_1z���Ge�:YW��������=�����5"EAX#(���
B�Y��@ P����ho(���A=
� X�>B~%������_^h3��&%�j��;g_1���+F_�W���W�����j
�%�	�%�p@�U�A�*���&@�0��et�@{h��������=�3��	� \��� P�5�����FAX#D�5��k���� ��E��A�a8��( ��&�������� ���# �W(�j���6Sf_1�}l�]����^�pT��U2+�nb��[K{f���������B
���.�A�UnG����n�$%���a������y�^���O����X���rN��zdb������Rc�[�Q�U�Z��_��8���n98f&��� �����;��3�,8q2�uqa�,%�������!��_��#{�����rV�~����E��]�7��27\���n����s�~"�������R�^X�!9�zI�{�7~��vr2gd���I�������u�7�V��]�\|�f��X�� l�}�;�k��O�y����h���'�W�A�T���)rE��eJ�?&�����KW�A��En�pYr9��~E�V�;���{�g�rNSt�F)��MB�7iXv(�9'�@[@A�ff��@�	� ��C0<�!0"C�ZFa��s�e"����'GP���"
B���`�y�dO���M�~����8_��x����c���o��G�5�Z��Z;�!���~�����
������������g��"�� � K
�!� �_��F��,B (�aGF�7��m� �a�D!�@A�W��/�����+t�vH���a��� �7$�f��p A;2
@��	� lo�a�$�`%���	B�Z�~yE��$I��a��� �7$�f��p A;2
@��	� lo�a�$�`%���	B�Z�~yE��$I��a��� �7$�f��p A;2
@��	� lo�a�$�`%���	B�Z�~yE��$I��a��� �7$�f��p A;2
@��	� lo�a�$�`%���	B�Z�~yE��$I��a��� �7$�f��p A;2
@��	dn�����gE�kn��SqYJ��;��~C���$�G��)uM?#�Y)�9���=�*��;d��renx�R���c'���\�A����rV_\v.�9(�� ���1}E$q@� A�8;��J	���]�=�(��[������V���*r����r.Wz��L_�"����~�{�mF�H�H�a��� �7$�f��p A;2
@��	�?�_�?�hE��W���J��ee���s�����S��� A������I6��� �� ��N�R�p������;�x�{{�/� \�	H.Fh��$k�� � A�7����	�p��Q@p�����m�#'r0��!Yoy��/�D��}���&��������NY/x�bo}���V� ���&@��nd@`�	B�!�*%g���J��g������57�)A����r#+$�'��q�8@ ���q�8@ ���q�8@ ���0����bt����{�h������	Y��Z��1�X\�-K4GPf�F�O���r����e}�B���x����rc+eww�=?�Hr@�'��LO�u\����S����Y���Ul��Lf\n�������j!���2{�y�O)�S��U��Q�����~L���2����'5e��;qV��?+����I#�cP���x[��� NfT���u��
�X��w�Lf�K�����<[���g�I��;e�Z�+����^���}@`�X�������������srS)�O�P�������P�#�}F�����\����L4���?�������U<6�D��s�L��-������%�Rd�_���
��:#��O2� \.{�<N�����X]A�6}� ����;yPV�:Y�I9&�'��'2���������0�h�@z��+5��S��7��p��e9�NJ6�z�
��z�>������I�(�����������@� l����8�����m�i9{H#�e��eJ��+��W��U-S,�v���������Rl�����)(��~���W�E�U3O)��,�==rN�Rtp����?=.���t����M���St�E�+3�'ef�����e<���	�����y�����:������z�o(��7�umM����oU���*��2}]��o���'�����OK�iM2�
nR��<�2O���So�i8��w����Jn��&�����?�>6������,�G>x���N���W��@�n}�Y���������������h�>cr\�?~���}�����2���'J���}P�����W�)=���
��}�K+����4=�+J�����.uu�D���(��v����+s�X��������~A&��l�e�(20,��Z�"���q0��Q�����~�[?
�����������������d�O��f�1���(r�&�Wl�������H�����z���h��������?V�_���(�3���������zZ��V��/����+��Z�/���ES���w+���r�<#�����������E�V7���������~�Ok��o*�����}wib���s���c�Ie:������M�
�W��Lw�\��TZ���][��
��Q��Wx14�� �������\���_��O�?� �<�OJ��"�������u�%�)W}���Q6�:4
��8w���g^��������J��}5
B��6��?���g�7���E��t�OzG7 ?�|���/W�Z�W����)�>P5A�Z�ZM�}SI�������g~OS;�1�n�5���m��D7��+3��"Q��A|�''3&c:P��t����>�]��ke����>'7��}G�+7*�"�AA<������O��������n������N9��H�����+~1� �9��$A�M���J���i���
�j�������8_� �� l��|�;�� ,(�_y�O���a�aAA�����Z��������'5��������0�_��_U��?�3��y�����Ya���J���:�a!��a���(��;��8�s�?gf��,[�,w�lc��`���6����PBH(�	)��\ ���KH�H�S�1������*K�d�-3s~�m�JZ�������G����S��;gf�����/���{d����%�S��(�uS������r/yM���-n�_���v}7qd|R��� ��������!�~u��N��q�;8�~^4����#(�o�V6��n������;.�w��m���T��$$v���!�^��GW��������������V~v���B�/��Z����SW!����0V-�z��P&��s�]����R�/�AHa���G ��?�L�0�����t2�H �	�;'A��f:-�,A�����������	��@P � �@8���0�@xcat��e:����� IDAT�������GN$�~t�?Ar	Du�����@�@�u���~���A�A�b���GW`k�{a�������-F;;���o1��Ae|����{��w�Fy�G�7T�}����p
B���q2>}�OWG=y��5���9����g��gc8�t���[s����j6���� ��5{� � oq����E��0\�0?t�1?���d��'������ax����p
B�S������sZ�y�B:�+��u �� @aV��� ����0�!��� �6�D9M���FL���I$`QtZ40�ir�AhC$V����z��y5��!��jV���6F
���� H���G!ew[�&� L���	�����Ah�Y��xzV�0��p�$@]	��Ax���1jN@���A�q]k2R$�N�!��H�zO�a����� �� \������������y?�7��/�c�48��kZ���n�l��,lq���������1�`l���rfx��b�*���~��������|����������lp`k���|��F�g)�~.�f������8���M��QX�v��j�k�`�
o1c<�-FA��;�x��������9�s.�I�[������Ax��|`���W~��5G�V�u��E�\G���j�'� �	�i ��Gq�kS��D�b��]��s5�9���_��S��Zq��{���~"��6����n�x+&�C������ ��~������K����I<=��h����7-���u1��bO���[�����4(Zx�Q���_�:.��(��������xu�bT����`�Z����L:J~!<�WB��E �1�!��|%��#�U�Ah~f�E �t��0��'	d:33�tff�8jH':�I�}'B��D(� �N�BfF��Qa8�ae�o�
��D8C9s6�[�B)�;���9��'e����Y���b����|}�W���r���xhE�0jja+�;����%�$���@g<m���~c�o�y>�	���o��Y/�� ����� ���A?���D��>y}r}�����B��G��w� �z����y������W����'���0@�\��^u\���B�;����x?��g=�|��A8
��������W��G������������@x�����;���[�"xv��
;�A���C[����1:[�VE,��A�0���&���@X[Ye�@��]��$@YG���)'Di!@aZ���S:���
�@���0�B�q��0�B��	X��
��9� 4wOX� d
���]�=������<���X��5���p]1c]����5������5y�[!���<?�y�������9o� ��
���_7|�u�nX���O���Ah�����AhB�	 ����"��0H �	�A����03��Q�@:	�A�N��;t&B���	�@tt23����x}S�� ��0��I���}���:����N��������G�Kw�Ba��m3�C:33n\�73�������t�_"�J!�W���1��<NW�A��|��?t� �AH�	i�&H�,B�B��� �'@af����GM�$@a:���D�A�%C$@t2r�@:�4B
��g[$H
��"�~I �P ��xR ���q�$�N�I�}'B�a"�x	�	P d�&
�&��!B��M�	X�B��� �'@�03H�03��Q�@:	P L'}��
��P�1$@$@��9��(�w
�MH#6A$`-�2��� ���GM�$@�0���w"(&B���	�B�@n�1�p�����l�����L[vC9s6�[�B)*���"��9��d�������Y��������
�2��h�,E�������u<��B5���
��9�&HM�(,i���j�k�a���j <� OL�d��'�`��_���������.jh��O���A?���|`�b�+�Z%�+��ip���
����zK��� ��?�����O����O[��|>����^�,�~w�r���o �k��������d���<���hR
��?N���q�;?m[.�b*�]a��~�����o �W&��
�uP tB(���7-������z���z%����h_��M������W��G�����
��\}>��r+|��A�X������g7�X��]�C�t
��"xv��g��C�O����K�tcGg�������_�L����B���
�	������`��/��������o!���=��O�(�k��#���V�m��H �������9!�P����O�!���<��7q����!���F�_qM.#��I���=�(�VD=J;c��
m7~��������@;T����
\��AW�W� ���6���'����mk�>x ��������@8�_�a�H H�Bf	�	���4th��F����~q�������&�Ax�Wa|���~��"�����5B ��X�H�?��������;�!��w� ���(��0������������ ��������B@z}1�����A���w����x~$��}�'�l�c{a9�7�\���;n����B�"Q2(�@����������2�]_[�����b>%�\�`�<�����G�|��}�)������a��v8��R�8�����!�G.����*���+�:���/�=�I7a>K�x1��.�"6.~�������s/E�%��pv��������] d~0?x�J����_4��~t;;;����k�p��v�(����@�e(�����c8;���?���@)��hi��gS����fHM��b��@G)��BxFl1��2GI$�[��p��(�6�{���?#�����t�V�p�$�@K��4�)�: D!���#�~5|�����b	���?���3p������e@��F���	X�����/=q�Pph���_x��g���?c:=U_��b~�PzZp������Sp����n���c� �Z�3`<tf@�8D ��������L"�A���a��=���C9cN�9�����	+�������������u�����G�x����G�;�|>����|��>����|>������G|>�����|d����9uO|�������@�i0�W;��L�s{�L�n�b�[���#!G����%7o�h�p��j��<n4� (G� 
@���t��-yyP���E�-Fe�JiQ��i
��8���@��!��c���H%����`���Tv��H m<�WA���q�`;v`4��o�f?���P�������mZVC���s�h@sB�]Q�j������	X��^
��`�v���?
z��m���r�b���9��J`�.�)'3&	�	$D���+��j��E�p�x.��^�w�z�W�� ��Mp������0�n�RX��W����-���� �N��y�eh��C���m�D��9��Xt������P�r� �6@)���s�$@$�'��w'������>'�#�@�������'�'�<9������md!T�z������Gaw��P�����
�C�B�k�s�;|��A,Dii���P�5�)��)���{����]���h���5��B��
��J�[���DW��v��d%����p���#����4V��8�d�d��9��y������(�r�����Oa+)�Z:���W�QS���Gf���zH�6�y�1���k����o�������������7�B�_*���'�d�������OP�_��X
�r�/�0��r/�'�bx���R�6G������N�<c��0�y �a��V
��.Fu�3����y������T�?�����{�U���~�o�E�e�^*�5�d����7|�� Wn�M���!������+>�����u@z�P����Uo�������������p�*Q�����O���[��r|�y7��������Q��r���@�y}	���F���yy���������5���A�r2��y=O�@����ns?c{���">�g�: ��5/����o��pM>.x���u(�����=�p=������k���:��������0��~���]E�O���]4���CG�d�WJ���\`�(`�������Ta��w�7�d@�5�(�}�����TU$�`&� ����g@����YgEu�VA^Y>v|9q>4ge�@�� �n���b���!K��v�0��H_
D������pLO������_���?����A=�[fS$`=�������?00���(��/B��Ex��[�P
.�������P��Is�9���p��-GD)%`T�
x>����z<Z>:��]HaJ��}g���!�����/�������� �"��U��e���-��i��f�@��7��%��1����@������Ghj|%�k������������(��~�&L����A��#�S�Z�d�a@��Ah�Xp$@�G@6Ck~�2��o�5y^�A�.~����*GA��G��h���W�p��)���	��3�i�Ah�X�[���a�U����7����[�F��V
�6�@v�?�G��@����E��?��b~H��H��ThY�o4��/�p
A��$��.x�����PG��g����P�Q LE��	X����+����h��G]h��
�
�|��[j������B#�PH��F�������A�a9+GC9���4�����c

��l<���s��wwH ;������-��-��!8g���/
�VK	�����b��	d�n�%B5s����@']�Eh����Q��wC�����5wA�� <�1�V������!�������KU�-Sg���B�0U��O��?�
��w� ���� L[��1	X������E-��@�`���a����! �h�.����a��������S d����3�$@$&@!s!�	tv��m�����s��fs��[�f�@�%!��^3tf�rBa����:{���	t!@af&��7���I��t�g�	�@�&D$@Q	�A���rt�`:k�VU�'Ia|F=�B���J
:�����@���03CNaf���&�t��0���wB(&���	�@���B[��!3'�	�AhB�X��5MH#K7A������L�B���9�Qtff�� ���q�$�Nt��>�N���0�  ��@����� �cB����Q���{����]���h�-F���qDq��rS�����'+!�O��������qL���VB~�|2��r�<h�AP�A��t9��oj�w����A-Y�����	;�p��!}�y�1����Z�qz����=�K���.�k.`�L\?�9o2-���mY�L��Mp���1��OB)X�#���C9�p����V
5�~�|1��M����u*q�%(��"�_����S�w��[>��������k��=���x]g���w��w�D��Z�Q� �NG�5gw�_�����O9���G���5�pJ�OL�<��y��y����<d���P���m|���C������
�����k�>�u#k�
��J�aUF�_����1���m�Zqxd���������P�(��t9��4�w�'��C-YR������0;�/��,� ��s~�� d>�	�A�B3(��
:S��=�@���0�#����0��)�	$�k&
-�:M���tf�7�� ��oD��>m�t�����o���f��� ��<��03��u�q3c��m�A��Kg�%��t������p�$t=��96���}�B^?Y~��Ah�P��t��F�n�BK���3��&es$��� ����A��q��I �� L'}��:���H�H *:�YN ����}�wP ��a!�������[Hl�]k��m??��h�ax��p
������-F�5�[�V���c~��5y�b� L-���;1��z|W������b��d=�^�;_�'aD
B�K_��A���e�@���5���������L��}�Jd>��^�{��� ���y�?�>�������|��B��#&Z��!8g����]��A��^?Q��6a!�g��%�y��W^��u?��x�oAQfE� t��	<���B
B=y������>o�@h����Yg�5#4�o�� L�tfA9���	�x���ON�zA���n45>�������AVMC��S![�;�����@���@��)S{1z��� ��0s�$@I"@a���Y�0O ����G'��<E�B��t������B���������}�K!�������������	���G���t����:�������sB:�o��[���t����;E|�����$�G��y�\�d��h�@h}>t�Ah}��#��������B���`e�$�sz'�&�M�B���"�0G ��������A�����I��M��tG��'��ya�jB�)��F�����{���a�F��&s	P 4�g�Z�@�*��������e���a���#H
�)��.�I��	��[�����'�v�y�@���CB��0���5!?Y	1~:���
P �D�
�L�\"@�0�����@�P L�d�L�0�t�6	d'
�������
''C$�bS����B�S �� �������:�x������#�J�aZ����@�b�������,���GM�$@�0���wB(&���	�@T�YN ���m�P�E��_�S����"o�@�� �Q\����7X47���'� �����E8;�g�Zqxd���������P�A��t9��45���S�J����,���`��B��}�1��W�wLh�n����w�����=�c�S��Y������v��+	�H"�3�<�y����4��/��W
F��&����������\�#��/@9�0�������
e�@���C�]L`g�Swr&g������(��"E������������9�W�>-p}��N��^|���iA���m/y�8�7>��(�/����U������-�>�*��!8g�����u�:�SNf�2O����@����3���Np��:��u@6Ak^E�	��7��|>$\��	������!���+���zl���@�����e�������
�����d��V�e�]P:�;'C�R
�����TYf>�>�c
Bn1��z�����
�]{���^�������2	X�@������?o�c�����F3\���_o��P
��p���R(�u������������P��X`6	�@���Gbv�-A�'�Q|�����t����x�]7�w�?���
q�eD$@�8��7�l~;�����A�Q }�h<�N�1������s���w���*���c45>�������AVM��ic������q'u���C]��)S%�N�Bf	�	����g�33�@g�t���?�/����@/{�T���E����v���������::���]��@a|'f�;K<��	�w����G��8&#�`�7��N�]�w����;7��/]��k����������I��s�x:�
0���Fn�s��������>����Ah����6B~����C�������3��`��\�-t�Z����u%�.�+�z_8�"��o�"\���8p�s�d8V�����]t\����|/B :��������v'�����u��Kh�:y}��>� ��{} IDAT}��q���	���u�B^?Y~}tv�}�>�HC���[�hsv� <��!kf�D��Azxr�>8M�C�L@?������_����y������3z4����	�����;d���l�x���(<�q	�^{��^�M�)�wO@>����!�0�2�c�$@Y@�u�Kh��?��Vf��V!��Ka������+��cx�}��sn���-g����\7^��A(���O�����(G�w�9�w�EadP��@a��y ��&@aV����� ���S
�cg@W�m��[�f����������G'@>�o�2?����������#��?�������?����#���?����cg|>����|��>�����>HZ
B�����P��i��"�g�8rP[�������b�n��a���@�
J�������`�)��{�G�
����2@u�.��{6AH'�0�^i��
����!� e3Teh�=t� T����a��p�}B;
��?�G�@&���6@�_�:w~9y4��=�L����g?����z�(��+�r���jIlC�e2��H������ ���	5��gA��`�J�)���=�@@y�
�-[�I'A����V�	�@A��%@�
�>(�����xw�;�f��[��0�������3`4��H�-��!c�9%p�[{�<E�]�����,X�Z�!���a@u���z��Xd*	��F��B�s�U�r��H�2��t�@���!�[��N.�������1�dE���q�$x.x������1P���,���SH]��1��^(F����@�p���@�&��B$�c�e����Ah�o,���~�P_����BY�6��2c�}��B/��J�Q������-F�X����0��e�T��*4�������7�A�a�u���t���ZJ� �y>�b@�
�t�
�z-�}P����Q
��a'��z������{������l��7�z�#����~h1�N_	i��r��������Ku�H}5��Jp������@���Ka\>�7VA�{�^�V�����@��y��V�P���>|L�@��gR�S�o�/{�_�Z4}�}@z�?%
����j��!�0{yd����y���&(�����AN:���s����o�C���!I�`4�~��t\����Vp����C8>~���@���u�u0�;�k��yU1\'��s��h��F����s�l��x���G�xSZ���m�k����>������Ju=d�#B �B7aS�[h3_�/IZc|^���R��0y���t7��f
O9��e��4 z�y����!r=�z�y�����p���2�=������&Z����T�Uv#D�
�Q���9����{�u���r�o�����3�f�^�T���M�f�����a|�]24�v����@�A����L�VF��"���P�l�D��q��w�!����JO�@�t�F]	��uh�����i	�&������`G���5^�j�J(s����] <el��Ca��O��0�b$`e��~���1�h+���YP�����"�T���w��6��?�~��A$`I��u�uhJ����v��:���'hO���Q!��$ ��Kq��3�`[���������,�;�u�@�Z9���J�>���� �������H�H���B����e!�pyH �������@x�M �\5�-
��*�:����@�,[��`C�����C������K�Q���
%� ������XI �� l�ta�Z�	�a�`HGo�����y���J��t�z�����4��H#��
���_�w� T�Y��/�yCO�k�I�7vMV#P��Y�zK�a��-��6�Ah�Y��x�����h���[�o�V����I�'p��M��/�c�W�bg�[9��C�:qt��6�}�p��/��fi���
�*���aga�����O�a���3$H��@v�	���@�wn��@�0y��r*��G�y��]�W�CS����]��Qc��k����������Md�@����������] �o1�bd�A��b��b�� 4����_�Y�b4��08��@�wF�bthD
�h[�j�-F����k�b4� �o1�>���V��cF�{�L��d���i��7V/�k��s����_#���5�_	m1�w��(N��[�F�We�dk���'�=w�����0�A��"�����g��7������\�rw����>pk�p���n?�	
�3;8���.����n1Z�b�� �o1���7r.~&>dE~A����hA�M��bt�o9��y?��w}{����h<����-F#����?���R�<'o���
�\45�<p�
�2B �E � �v�� �P���9��n����:�
�D��\�6�J�#@a��&�e:�I�m�@���0K�=���0{b���	����g�SK ��["�D�F����A
.k��r)o(��0��`�$����kv� d
�������	��Ah�I����:o��9wN��D mB� �S�r�d
�9lN�H�t�Ah:R6h1A�0� �:��h�A��N[��sZlr��C!���P��B�C3���#e�$��� ����A��q��I �� L+~v����x	�	tG�B�F��1�f��5��9�5��@_����� 8JL�7�yk�c�P��.�P>\���<��3X�@vW���>(���k�	��A��`N<���<��~�
��_G�����Y���@��Axlo��Q�PA����� �������g���W��j�v��t�a�5�t�����?K�_\�N��E�~����y����U��E{
B��55�/���?�Jd}T��#jJHx#j2_�/�=�&���M���'�� �z��;3���jN@����X�������<�B:�]�O��� LZ6lAtZ0(	d :30h� ���q�$�Vt�?;�O����x	�	�A��Ut��(����l�����
�l��d��B�{�}�����75� �f\�]/tff����g\���h�����x.�t��&��?����O��g:�����&H�F]=�eo�6�4(k��Yc�\�{vBz�T�bz�yb�J���Gl1Z����5Q��[K?(��+x�l����Q�6�1��z-��2��Y���M�o1�o��w������K����g��%@ajy�7�Vtffd� ���q�$�Vt�?;�O����x	�	tG�5��N ����!B���Z-R �V<8���@�\�l�r����4���GMi%@�0���y|�3�$@$@��9��(�y
�MH#K7A������L&@��d�l�r���<���GMi%@�0���y|�3�$@$@��9��(�y
�MH#K7A������L&@��d�l�r���<���GMi%@�0���y|�3�$@$@��9��rL �$�E%�?�"�}�w�@x*�5+"j��(�E� 4����_�YQ��d�tp��w_�����5�y>��A���"j���!ka�#j������A�z>�����'S��.2^��`N2^��+�@����+0.������:�>���������c�Z4	��*Z�U�>���3�~������g��O���@��B��.q������X?�?�m�n[n����oq=7������������&���U%p�4��Q�����T�u��6�������0��[�h�� ��g�����z
����������2��7������K�yQ������5�����x�A��<x�y�����
�����~����{����
���P�������������������=��Q���Y8���������W��&F�~������������C!��'�����b�p�J����e�$�3��Zuy�@�3�,9Q:-��M�4��}4�*��@x��9qti#@a���c �,  �-p�Y!^8\�� 5f�)�*�wv{g�7��w� �	Q��� L����z>>�fea�������A8���pv�_W�^�������C������	�������L�>s^���B�G��G�F�\�"w�fI�ag!��t�=y����z����-������N(�-+2~	���O��_t������\#	dO���-��'���agag��|{��������+Z;�� 5(��r�%.6A�|��hg���'��Y����b��@�|�5}�7>�Lf���L�l�r�@�!�V����V=�B$���B��Z��
�D$�1� ��Pq��$`�@��A��4QW�AV2���Qw��e��� �c����	� 8J"�t6���=}�o/X��tk_�2*\�����k:��dw5k ������A8:�^��+�7oS�����w��������_�[���b�f��*���{��&1���Z�5�����b� T��npD
B�k\o{���W��J\ ;���d��3�����\����>3��N��M�5��}��5��_�� �a�]Q��������R]Q�PB�Q����|��-��	~����9�5�@=�,��r8�vD� �z��43��`
�(CV��A8�+��Q������� ��0EZt�uS��|��;�<����fd�9#�$���{wPT���x�����R�����UP����Z�=�=�����6���*Z�UE�DK$���W������������r� �P�����ni���oC���!�B$`9���:,%o<��7����Q:
-'N���� ������]�)���!�~�.����V����
��B��� �Htfd�8�8��@����x�����?Zp�\���Q���Y8�����m���6�:�3�����J!��d[��K��[�
4M~z�8r��#����@z�[	���]������C�y�Qx�y��^�P:�������f~�Zv>��'b>�:]
��j�*�V�C���o�9��}��i��}���}��s
�KgC��o�V�+�z�/���K0fC=�.�1�c�7�]�� l�>��v�9ve2J���K!���[t���g��m��E.t�ec�A������\��6�p�W_���?CJ�~��R������^���J������7
��L:�/{���AY�6��2c�B������pt���dj�b�J����X�:��:��vm��n��xV���#����K�������q��3@�ZH!S�H�����[�Ro��{��Q�y����1���B}�_��9��7@VTXl�	������?�����r�����:�86<
�����1���F���|*`��� t��]�D��qx�ePE
���������tZ.�< :���H��� �z�8���� ���&<���P�I5z��P<y�����s��m�`��Ls��'            �I�A(�4�X�����_@�7r�T����y������1�FS3��BHm�?��@L8��B5B������2��>G�0h{��6pD�J�%��������}�6�& �������B��wH��#��A�W	�V
��#R��=�����!���tE���I$�i���Gw�P���[BWj���c�8x������4��~x����Z���C6�@�U@qB�M��%0���p��W@z[:�,�p��y�m�
��� ��g3����zH�]����p�G�)���q��!��H�H�,r�X�	g���y(���`Z��>��)��4�;K�};��R�cO�3�����BQ�f/���������-���M��&@U!y���@c3�������`��	�	$����u��b��k!��D!�O��(�A9B��E��2�����8rh?4W)��
p�>
6��A�
��������a3cFA�t�VG�i@��u;l�gC�
- T���q�g��2o<����^>r���\�2(�H�@�;X
����Z?�]L���g�,<��}@�0��{P�h\U�@�����@�(Z#�}�C�/~���h��1�B�B�T��2.��@xd��#���B�v
l������K�����,^��O�)��;m�����m�����#����	��P�q|�C�kO���w@=�����������A���(����6
��s� �M$��c�>�3@�F�as�����q5
����E�F>Y�#x��FT6��p��x�!�A$�u�-'���JT|i��\�>R����]��������w7dq� I�J�����s���C�	��@������q�~'vG�@X[�?�z��$ �\!�7C�{6��uOC��! �����f��P ��\��y������q�G(�5��/����-�����~�����w�_P�����4��m(��EPWlC��od�@��_U��������g�Y <���
�C���@x�>����y
�����/����`L��y��I��-�������|���Q�_G�/��8���| ����@8���P �l#���w���I�i�����A��B�wW�@��������r�?���F���������s�@o��F�S���]�u'*��> ����U������[�_������	�s*[�l�S��v����W1�;)&��G�	�@G�� ��_	�P d�d4��w��={��Jv�F�e�a�� ��,h�E��o}��m�u�_���V�-�>S�M)� �vU�A��k�s���k�����2w"9	$@@��O(��8R�U���N�B����`�R��C?�:��CH W��3������.�yp�_I��BI�����7m#*t�@�S/Xh���R�Kb�*����?��#j���%�K1-<��<n��)��!l(���@MC��P>	��?�r�q����t[��t2�b�f&�����C+�H!��H�zK�����yB�� ��c;��;Qz`2�W��c�/�^:�A��A���r\���������$�_ �W�����W�����P���������7�S�J�!J�{���o��I�[�~��������L�CT(����m<y����E���
;�[��C���a^Bo���fH�
b� 1R� �j���;?�R��N(G@���Z�Z��O75)���xZ��v��f����x��~0_S�.��:R6>e������+1B���z�3����1�����'k�����2�_����S^���>[�>O���/�{K����
�� �����p��}�	���������?3#�@��j��/�QY����q����u �:�7A�[�g|��Ep/	;�[����������[������6al��_����d
B� ��g/	�A�Kp<-#	�A��a��I�r� �\H�	a���� ��p�m�t�
}�vLa���#H5:SM����@|�0+�a�aE�o�n�A������ BA�[�9���������@h�~�D�|&�A8Bi�fC�5��n��aw5�B;���a-�����kw��b�� ����cN<��i;����?�c�n�;�[��_�����u}�D��-F_��b����^x�$��r����"V�z� �?�e��y���|{� ���-�,�P,���X`Vhk�gVJ\�uS��7K����n=O��eA
u�o��xi7������$��8�1�<�N��Z��Y� IDAT`EC��%��
��8�"����T��h�/#������H<�#�Vp�(�;NJ"�L�[��^�X:!����~!�����3T��q�,WD/!��s~8 �����X�io�?�K'�1$�QF?�,�,���A�v�&p�q�g��h��Mw�6��u3�X���&�q:P�x�S�G��$��W�>��C��1�#F��w=s���K
���0<>���g����J'���;��B���G����\^�,��m�	���5oD4!�S��~��~�q�kZ{�t�A��7�m��
|�N��
<�M&���d�)j�'�g���~g��E�>����+��
�`�vI��*$2uj*RDD'.����>89(��5�woI���:�T�X1-B�J�y���1��G�X����'�@8���/<�bb��
�Ed�ha ���t��>_`NH\��@�\���3�(�vxM�aO��x�	�Ah:��o���O 0��f�d;%_ �:a8��������0\��}���A�
�N
��@��A�A����-��AXr~r�k*P�~a�!tvW�p�C�G� ���;�5}�M~a�a��:w@�w�k���<�VB
�����w����y�O�|f<b_��c:��kn��S��pi���FC?`�(�#O-h�^�������.��K��g��^�{�H�A�k�������;.�����Z�����'����4k���'vr�5_~Ib�`�G��BBY���5��85��Oo���x�b�!7^,Qj���>�;]��!@������$q���sn���7��P����>���^������!g���%~�
X=H`�l px�����c��u�S
����I�'P7�w�
��3Q�������������'�Mt�Q�����s��#��z"��&1�C�7�	\r�z����[�����%�2�@�������5�������V��y!a�a�����t?��/�}�������-�<~��!a!�K^�mh3�������z��|�'��	������� ����W��l����{����\�<�M���@h��Z�	$� |�-��x��&��& �f�D��c�����g�?��)1�=��K�8B����{�J|��nC���#B��V���_�K������������������9��Q���/p��}1�',t=r��"�������v4�F �N[t&:�����#���k��J�t@k���"�H ���Yw�f"��a��x��� 4i�7Ha���	�E�B�H��0O �����I 4�Rt�A���.����e���� L-�T���u�/��w~����m�����;������� ��:���k��������7#����������G�~@�V���R1�)m�2r+����E�sx�r`j����l{�Xl�=<�i���}t�<�J���9/�aOB]���C��Y����TB5-cm5kJ����"@!�f?�A��Y3�g�|����������-���A����u��/;r�'�Ae�x�{�[����e[���k$��"���[�>�����.� ���rV�	�A�� 0�@�B3zf}!���0���%�s��T�����
P�wF�(&�}#����8`�p9�	����#��?l	��8]Q-��a��C���W�x`H�Ksq�)�E�MI:��5�[��0�����	�J�BSq�1�0O �8�y�:��@�l9Q��`x���k��d>
��3e��%@������H �P ��h��5Q���W$~��E�m0��o>���p��n�u+H�/X�������"���$�'����'p]q�>���h��^���H<��-F���W/]\�]��cVv�6L!p��8[�z�;�I�.�k��a��M&F�ab�xT�(&��G�	�@L� YN��	�@��b�2.I
�qY�
��

���M�@�@��AOT �x��������;����I`W-��n����
1E�F��/Il&p�I��At`�Q��-�)�k��9�;�������Mw����hH�Zp��#����# �mn�,���@�-��;�V[�?�;�z�E Dp���<M`��L	|�[���������gL��^_#��-���
T�EZ=����x<T��������7��%���!~���w~��t��c^��<A�_�
x��~&��m@u�@�������u'5��$���.F'5���;�1�BS0���$@$`
�&�d3V%@����P �@hBY�	
��g2
�&es$��(ff�;���<��'��m�.��V�����^�/,Hv>f�`uMG�!�������Ud��(=.8]�����]4�@��]�#�F�S,���5�g��u���� <�������Sk�����|�u�_�t:�������_4]��a��%3��=O�;
�}g�:�@��  �P 4	$��*�7��b����}��<R���cl1ZQ����_$����mG�<��
h��.&B��������P��!{�x���u�6�U�����
���C��B���>��6* �&m�t�!�WB����T�tP���;G@���Z�Z��OO
�T���~�c/�a�m7A��\�+�q�QJ�?�)�������yUp;������w��h|�i|�v�G�����8�������5��FC0��#��]���&�'S�s�)x>`>����s��o_b�{]LX �$�[$�
����r��W������B�1

��%�������Xp0'b[��7K�x3��8~0p�h��T��u�[��p@�+g�a��%��SE��o+�_w�C������tH�I����nBS��~��3T��q���@w��}z���%��ZQ\p��M#�]|]B���_�
�'|���������kz���J�qP��������`�v�g���e�0��c�9�����&�<��� O������m��eh7�W�~�gDee���������>����U �C8������z(�KaT��}���\7�np���M�������_�oD!�K^�mh���!�[�}��~�n��n��0v��w�N�������@J����Ta6��z�
 �}����T/��=�^i+��u ��6��T�� ������X�tZ, NR	�A�T�l�r�@���^�9C����@h�)�oT1��
�=�@f��03��I���0�����	X����wt����AHa���B::r��Pt2�s)��������\ ��0��
_�{���y��ik��a��9�I9E�B>���I!�<Vx�2;���:-yM!{&�GV^�� 4���B:MH#K7A������L�B���9�Q=� �B�P �wI�Y��h����������<�gcn�����x	�N�B���|�t�|
	��Y� 4�$��(�t�#��Z���5���|<��3`��>ePRQ����[�1~)c� T��D� ������k�
�Q�p�C�G� ��tD� �A6��p
����A����1j��O<~�{����A�x���O��+����p4DyED
�C�y��[����)��������'���g��%�+����Q~%
�Y��B��oV����a��Z��I\�a�v�����NXQ7�g��h�]�;/�v�m5]0�����[?��~�������<�`MD
�p�Z>0���E��������p��gBD
����Q���?���lf�O����������� t#o�7"jZ�y�5�}HX l��z�5���K����h���@��G~���!��@X�m.(�	����v
l��b���2��7@���_~c�+�c#�>P7�/�G��)9��*x�m(���������P��;����o�@�b���(�E!��0l��a���Cb$@H���E�Mu1G.�J��w)
��4$f�D�l����pd��k�Ua�9(|��,�%�C$`5���5���4/��b,�S�v�1�Jh����=;!*��{pN�a�iq<i$P�T���s����8�; ~��pH ��2,�q���H��N�B�G���#��@����(�y<�.�{�_�a�����`6���\��AN����C]�{��@X���$�tQW�Q&c�b� d
�d����&���k(o�����B�?�{Og����������z���2z�<x[f�klE����]|>_{������Y(����q��uo��;k�0_������_��*������2$���!_���������JO�p��^�n��V�	�@n(*C���@}MH �b�d;���bIYQ���B�>'�A�C� ���g��3m��;�0
��!�a)��r��v.�u���y�� �:��\'��>��A���!4��%�F#�����=:����*�}�8���}w<�����|�g���:�-�.�Ah��*�t���	�V����{�g&��&H��D�^(��~��o�w�_\a?�\jv:�Z��RN�����^�����o�,���/�5v!Z^}���G&v�M�A��g��8����r������d�P8�C$�Q��#'�-G�A}ge�����~�p�O=���
������k�s��0�����:?�l;^�����+Ca���H�����K�$*���P����D���p]�v�R����T���� ,�����}���p_�;x��Ls2H�H�H�H�H�H�H�H�H�H�H�H�H�r�@�����-�}������7��o��`=�w�����e������_{���/=����hD���FK#T����J��������p)��@:as���� �
�z��W`�?
��m�������@��{�����
�'��44�h���������Q>��N��x����m������G4>������=��#��}������'�_�<��z������#�oa!��g\?��h����y�������3��.�A�{qxw
���2/��6�F���j�qzpt`4�z�z�����_��J>P4������y|T�����}�LV���E6YU��V��X7��~�z�jo�����������V�x���U�R[��*`+��eQ ���%�=����{�Y2�d��L&��s�Q�s>��������y��(�?C:�+(>����?e��B�/��B����O����[�D�0&�D��K�� t����L��J�M�84�IH
a��dW���(��������2GS4S�}���'�Wj�'��eHn'���8B�CMh��!iF���y#>oN���^����g���z\�NT��	���Tr&���WG2�QOF�P����������o��T����=�j�R$�������	w�?nBa0����P�����B�����"0����� L��9�_�7���0O�SLy���u1A�fT-E�UZTC��������PrV�,����*���h����LG����b��ZE&��](�j4�"?y��)��b4�B��mH�j�[7��{��3"�*�-��C�$\a@}��H�R��nh��s������ih&MA�����gw�8��)��@��y4�����:BO<Hx��O�xD�B����5�?B3�>|��������v�F
��34R%��F�l��>��qA�w���p
G�.��� �<iC	��H�������~F�j@���~4x1T��P@( �
����������xh��H�P�+��YX�|;JG:B�
P�Ci*B���r���RN�t����b�0�M���)P�����S�_�?����R�Cr*	zj��0�0L�	�B�@w
x���f�B��#Pz!������pI��KP(�z��
�O<��AW�F{�_4UE��	IO��N��r�0��
4��C��^������zT�QX����##�;��a����j&a	��g���C3k1A�� ��$@X�R~/% \��lq
@XH�-����)xf��G��wb�p��z�Dw���9�|t�����|��=�&_Kp��������~wNW��`'m�!��Y�Q��)F���~WG�3f��m;��W��p<	>�F��_�G�nF���x�P���BM������a����;CX*������K.:�B�;	��R���NB�~����+�<4�F��^��qq�{�0k8��a������_X�RC?
1���{&��b��	5�hs}��eK��c@�<��Yf�_Pu���E�7����)��ab�
�+�B��`S�'��y�_A;��1@�8z���-&�|=��F�s�9�	MU6t?���A���y��7�����o���m�
Xv��n�w|������8�Gw6\�G��S���P��e� �y���e���o��p��%�����T���'E�!C�=\����u�M�w���d�8���%��a�������u�����<b�vR#7��R�?m�zS#�=?����f>��~���L��[��Dr9�+>>�Mq�l�I��Da��%Y�m�G�h�7ow�R�Tm��������{�. ���U�s������=�W������j_��������ab���L��B� <Y�Z�����n
n��)�Ax����O� xk*f�F�A�W5��JS�(���K�wJ�W��59ix���
�?{W���]���8�?9e@(�L��"����&�������PN1:.��&�C�5#9��0��j��Eh����I)Fu(�����QXU��������P���� ��)!G�p��9�"5�@J]i���kn]Gh�T4��Fj&R��~]r����=�A(a_������JV�p*��U <)K�i'f��8�W�0���s}�@��zewVG���)����.�2�j0�	��C�����C�c��� ��s����7�bT.c�w����N�|���I�pj�f�R�Gr*z����;�:��>��%��fP������h&�wnJJ1*� q1��b�wG@xA�g�}~���� ������p�l���O�t���yY�h
�
{$���X�S������)@(�3D��lTK���R��:���<�y�) ���%�N��p��
�9R@8s$�h��* �9p
�p�7$2r~)�!5;�b�F�����S&�����Y�,W�;�a�+����<����J�����	�P�����V�gT��4`�1'�����A�7JL\9��y:���������#.V��9����F~0���vmJ!6M����7G�����kc�`��������R^����������n�x�����-����Qr���|�D��uZf��^� L�A��"l�7������/Y�������/�0�����y��I��V����l{���{���?
��_S�/�j�'O���sp��z7��`g���0s�%&��g�I4�Y>��2��5��;��O�����������q��Z�Y�����X�S�+�v����\�{S�z������<�n�������_���;��������n/`iY�����Q�~��5���t�@��^��/?w�Nu�^���y�4poE���^������5^��PVh��)��^��9OQ8�����}���G|TF����t#��mt��6�I<W��3�=Tb��EEL�o���~v��{D>��e����,���wGu�+9mgz�� �X74�^�!Eh0��/Zx���m.�Y\::���cK�z�
���lA�j�T�Y�E���������6��TP(��m���-j��p�3��>VT��}l.�q�(�fk�Dbz�V�� IDATw�����������,&n�b�.���t�H;��avq�D��U
A�����'������t ��M����5���d��,S��G�I���<�7:L:�ef������������)����:������|��p��<n.M��c���w�g:��i���F��sU�����H������I��i|�zr����Z'�p`����p�8�E	G��#G
�>��@�i�23
a��5�Q��v��������FNC3i
����b�����F���~�hC����S�g�6�e,1@�z6Z�+��1����N��z�\x��vs�heH!{���\G�
,������Z��U��y��gA������;�3riOa����X��/�
�� �b+
a�P�P��/��	��v
��_�W�a��������#X~^��s����U������K_�����~-����=E#���K���a���9��5�\t��������5Y!a��5���V��������v���
�lh"Q.<.���g���q�
&5e�`�%d�>�������2n�P�<��
�x�F�_XL��h}������l���0��H<�RW�0�c��vj���mdk�2Q�U���G�	*]��7D�Yj�`���v��n�V�����1F�y��J��'��M���m?�N�i�����FR� +v���h��N��l<���x�v��<�lkD��f?���a�xK^�����v\��� L[�������u?'����g������T���.�r�<wf��_#���
a�]�~50� �W�%+
�e���/��[�����	 �}T��3��
���e&��G�\��o�[�l^��	���-��h��|5:����F�������
x`��Q��"���v4���ug�1M~|����]-�8u�(�<�
�8�����*�9��9 �����Fv[�����������^0q`�!2�#Uu������b~W���S����ylkc����>b��:�Ym`����@xr6I/�y_��G���W�/������Ml����#�����<��|�LP����R5K~��@���W��.�b�B��&��vc�6*��f�c���6n�}��m	��#'O�x���0}Q�� /�q��)b�/,L�<��h�����T���S�o=zf�mf	��?�������:v�W��?����8y��Z�=�#�����O����H��4S���@�������
�)���V�Z����
���*nOv��<�������K����z��zW=y���e�#-�!��%S
yd��2�����|K
5XX{Y>�#59h��e�z<<���7����8�"]bF��b����y���0z}�!;�wYpz��'���g��Fn:�c���X1*�x���[xd�%�6�G�h���g
����8���*��������K����G�m��\����sG�|h���8���#�\���f'?��������
 �<R��O������f��f~���IsF�L������'Xr���K���|��83����ga�,+b�$R��$Wn�F�'��0Uh�B��_���F6.���t����+>>�Mq�l�6���]:TV�0}',�����c�!Av��<�����,�C�����g�ss��O��:Wmjb�D���h7�-�W/����L����k��h�B�>���}xq��r���9PQBsF}���!mSeFf$����\y���/[�%�(�b���iI�v���kY7���Q1�`���cqER�l�8X<������b�V�#'j9�\����m�����}*^=?������������f�����S�����8����yx�������9��|�cc�>��gU�F���l�T�� �M����~]��W�Y�� �p��zy�ZXrU�/�����Yq�QnR�p�s�!a�?j����/���e�����w��~OXY�d!�r%��t�fV�0���?
`���n1K��k�j�b��"�	@���pmt���<���1w]b���e7^"����	�����R��uc��:w;E�T����j�����-�>�eMc��`�
r�F�S����p����'�h�����	nW��K�V�`t�1xZ��2]�m�y�wU
�d���&�h{P][5�\\�����n�������)@i\��YP�R���tR��t�s���0�#���'�I���.�R��qq�{����2g?�`��!�,������/��:��k�]���)HT����c�����v� �z���O�p���;��>������;D���M*���B�S ��� aX1��P``( ��XG1�N�0�!��9�>�D�!1��;���k1U����!I)>��3k�V����z�3fW�'�GW����8I5�k�k��=N��Jr�BO��9��0���S@�)���G�� ��c��'xHNE:B���:fI��f�>$�^S�z?mb�M���ueS
L��c�l��b��F��~J����������aW��TQ8O���W�t;B�����.!��{�Zw�i��jWv��]��C��C�:wCe6�,"���\�V�r<j3�]���y�5��Tc���
�u���A���^��S���5]A��L�'���) ��n��--)��j�M;��0HN�j�1/_�l�����n��;D����2�]1P��p��������V��P@(0��p-��J* �A\@(a���6�c@q��x��B�� '�P�K���e]���}l���T�7���Fi�iG38� �x��Z�B�=�\Q���PbUA���i{�]�f����z�a������%,)��A�	 �g
S����>����mG;j�|�\��#�# ���`�6�wz�����/B0"�W~[��l
���f.a;������o/q���h�������Pd����t�����&�or��|���F����J��l ����I��,"L��h]�����}���k:���qw�b�>9up��|�������=��a�Y�����W�0��6��Y_�e[]�;���f�B�noNv+�8!
 LC$q�P@( HG�QI����0�'��9�>�D�a���OpcK�~`�a;�� �����5�i8��5�bT�[�����	1��>������������B���6�s�4�F�K�q-&��n��Cyah�zD���9=���0�����l|���a*AZ<<�_�,'�Wf�u�5����K��	�7��_��F�]��.nZ\Cy�>��.�����t�?�&��M1�e��B�u7�������}32S\�=@��>p����Z�|�
�N1�����p��;��wFaP�mMKc�0\��9�6i=3Ow:��}g)F)`�9yLL���)F���Pb[�������`�c�#�C�O�G�cvz<����w&�80���^=tf?�����=*���������,���J�S�Fg�!�h|)jO��@�l�t�b4�}�b�^�T�r�kQf�� �V,��H����!�8E( 
������$���
@����P��Q�m"}@����@�N�8��h#��2�4��p,���f��������F�0�*k��B�T�/Y=.����C�<9���ze��s��c��gO43
�^QIGz��b/�`U�����c�<v�GMr
B<���c�<^�a��2@�T����.6��a&nZ���FF��2�`���m��FmR�G� �`u�?��a���.�Gg���/��Cl�S7��M����6�r��R��!V�~IKY^l�x���1n?�+@����z�u3&���@��������2+@�n�~�/>�CEI�=A6���_�����T�0����XK�����B~��<f�*���a*+<�y+��{,���&&|���C��"�`qr��q��mL1�O12Q~o$bw��'>mb�+��2^�������
���%	����~��,,1�z�s���z&|�e�Y6~=R�#^�
�Zx���
Iu�d��j�q�;j���������0���g��\��3{w�yy~�[���g��Fn:�c���X1*�ROz�'>�����3�l���N�X]��~�0.UG�UR�������*���qk��t!T�e�'�L)���:�4�u������W�3c�� /t���:��R1?���#��������{<���z��H?'��ip0aC�,����L�-��N��`�7i�%����r�Eq���q��*���^77�[���E��i�L�Vv�����A/��}) �����T�*3�a��:6�Xj����/�����'	��%mR�������$�������GC��-��P@(����o��]������� ��l�BO�9X,��iaV�Ax.a$�f��f������S���?bo`�^75)g�T���������������h
_����jF��$�����o�kX�,�����'{�� t�����>����C��8;O��b�#�y��l(U{a���fUd��?�d�b4�6w�����}m35e#�TK��?��+��r��N�)W���[��-�������Y^�t��I�"Ov|��N�]B���a���?l��6��
�lhJ��Y�P89hW����Y�s�v��u����fN�A�Im?��)�O�����\��]�6���p94@�RA�������mz���x��3�Fs�1���Ljp��- �(�eP]�u�_|�����9{KK��e&�t��j6�Y�����;����*�5������]����G��GQ�d�HP����<?<���: l����L�:B�
P�Ci*B���r��,n|'���?�):M����� �M�A��b�B�@�( a��*Z�3
2@�O�������aw�
Da(��S�yhf-&�������J�j�����<�����@)������Q���O�cg8�u�n��(2���BaU�1�p���>L�_�C�����~+R������^D�T���@�o�B� �
�sf!��H�:�������#�u��S�L������h7�f�t=r�����p�a�O~e8�+o.�]�u����_�3IC�	7�9���p�p?i`|�����;���-��u�jP1�@�U��|�D����f��?u��ze�g����K
|���{����C����F���%&n�a��Zk!��#M�y��A��Y�F�3����D����� �5�X��������-j�`e���^�kC���v�&
�s?���oR�kG����Po���)`����`2	��!�
��^-�+[x�� iXze?����18�|���air
�Lj6�Y������*�/������W�s�YY<����c���.^^���m.�F�X2��-�ia^Qv���f�m����?9)�W3��<������NS�����C�.��So~��\^�:�� �%KL,(K�;��N~��77�i�wI!��f��}+;u�U}B�.��X
����_�P�U1q����[�Qw������x�<W���7�d�������#M,)��X�����CI@&�E*���v#m������7��M:���w�j���7�����6��
gde�F��Y����H��g+��X�EO���f1p��<~4F�-��8�����5:yG^�H,����<f�I��. ��#Ly��_���H;ym�\:���j`bR��������
���CC�pM=�����dk4s�aWb��fab�q�t�����w��5��^=��o#����a��U{y(����e����Lw40}{[�g����=�E}�t�����JoTyX��1���c,�\��w��9u� �zB��P�Gi*D����������/$��>�����>�n�T�~/J��� ��C3��� '������BOO"�r�����+�����h&
'��y�_A;���vo&\�8z1
��QD��������?A�jDc�����������n����B�
jr�����������]���8
�7��������a��Ss~�p�<��P��8��pO�PR� <�#��P�^p�pD�r��P` )� L9� +���&e{�6��9Es�\�����n�]*����Z(��@U�{@(�u�s9/� ����-��P`P+ ��z�����0�B� ����D:��p�Lo���S?:���h�b�B�@o+�) ���m���.l���q\+Wo�v`�/�X����C�w�p�����rv
@8���/L�{@(����\9P��P8y�>��N�n}p�����g(��g�F8s��p
a���6�?�1P����O/�[�i���!a�������0��tU��r��c�kj�''�M��I���%����+q�P��(�
 ���Wa>k��0[�d#�
t^7-�Xr�RG����8�����kFc����~c�M�\�%=MK���p&��qq�{���b�(�*Ls��i�B� �������0m���B��P�k��PD�W`:s��Q@������Q�ViQ
����Z�|MH�J�E3@��U�.,N�A�7�en��(2'� ,���S� T���	���.=#�����nB��
=�s�F
���x
�)H>?��v����^�F4�3�����=�a ]-�@Z��\�8OP��,jwl��;?C#U�:�"��N#1!��P {���G�n���X�6Zk�����[qeB����\��G�\�u��G�Z����j�@x��E�S��`��{@(;u���H��T�T���y}^"��?�)��F~n!�^U������n
n�kN!���T���J��Z4#�(zu�q��P@(0 ���5/�������0��$�������E
BS����A�
M1@�Fc(���?��hO/D��H_�
�X�Q��(	��!G�p�!K@����ih&	@��wLl�2 ���c�u��J��a��P��
�*Cw�Q|�����# �7����M�~����b�H�N�4��(�{>��P@(0`�Z
P��������bbB��P Ct��8�����7:\i�z1y������,4E33TE���U7]�����1�����
(�
�q��P��W � T����KS�
�Oo!(�&
��ho������J���[��>��Q(�X�~��I�cG<7k����A���sS��p�\�=�/�B��K�f$gE���uaQ��p5���H� l�����m+��,B���H_��X�q��_�q�g%9K���������p*���� ��z����^����-B��_�9��K������mQLO(�P@y��h��o��I�p���?� :��r������h������5;P6�*�
��*�.F�YI����:"�
�B�����	>����\��x?�����\��K�Bo�=���$��(���k�r��s�A(��{>��������1�8 ���^���M��Mr��aHr��|@<o��<�d���/�~���G�>~S� ��A(���~�������@S�%u?����w(�a$�%�=�V@��u:������
v~�&�;n�����N�-��������K�lzG8����/B�Go�v�������#��kp\<��2pPX�Q.\_��rY�B��!�>kD�5�g;����V��t��?�h��Z�XbfB�@�
(M�����U&��z�����=q�P@( 
�o�*����6�w,�u�E�2�'o�9�
�
��e��]5
�A��W��������|�E?\�f�x� <)��N�B���p�e�J(q��������R�?�9a�*�Z
�c%|�V@h���	^s���1nr��pu�0
�B��P@( 
�B��P@( 
�B��P@( 
���9�h������=lA��&��CQv��&8���4�0|*(4I�As��l��NC���E�<79Q����(��^����g��B���T�E2�A��R�[�s_�K�E�o�_����P���Q(�|�vj�B[8���Cu(�*ycb� IDAT���%5����6���	��z����\�����/r���	��LI~�{����h�	>=��qA��Q�������������(G�!m\���t)���Q��"��#��P@( 
�B�@W
�G!�59Uh��j��PV�E2<�2��	j@�@2�I��3V������
�z����������1����e�}n2��SU�O-�I�Bj.G���h.F��o�9H�	bD��}&�3�>��}@�P:jQ	�@y��!�P�(*��F�(�J��Q�O���Tjl���B��@�w6�W�a��`&���(~��T��(�&<���=���������������o����a�� 	{Z��~��Z�
��_�j�xw�Ck��Ru����B���c-J'�"�@9�|�$�UhF���
H���}i%�+��/Q�T[���a����C�R��������PZ��7����(F�������=��P�!��	hG����Ei���N�vnRC-�Mk��>��
��a���+��z��u�^/N
��B�����b�+�?S2��b�
���#������ K�T���B�^[����{�TY�)
�B��P@(�O����?E����l_�:�p��W>I� ���x�����LE���3���3v=�*�Y3��1�����P�v)
����G��y�@�X���
g{	[��E��
�B���|�H�xM�^��Z4�XI����� ��'�����{(UBD�@�S@jl���o/Y�r��h�k5�����g�ax����/��F����7�����y��	*�p��x�����'9�������'�&��k�ZOG������=�/�vb�Q@x��yI��8���%�KT(�p�E}��4������pY�S�����e1@��������O��h���$@�������i��7"���e�U?pP��^��������\m�{�u���N~�����B`Ps��b�g��p�_�(F��
%�~�G���!M�q7�D��_�(����eW���(,Jt�~������
�x�� ���k6��B�\[S	�Sx� gZ���s��&xq������B�@
�w>�c��pe������.�HC���������m���O\���f��v��;�����O���D���?%dE��9���(�_O�����r��y}��{~?���o'3����2uh��� ��s������g��O�������"�����p�^�f��?�u��|,�\>�,�#
P�B���*�|?5���Q3��g�������p�r��A��c@����a�����
�"�S��u�9�D���u-��sp���������v'�6:����lM����������g����O������j��q*��'����^;�7LFv���A[�	�z�g,����l�*���0����P[�;�����^�W10G)�Km���N���@�J}��PB ��w�g�J�����
�B���@��!���;L5<�\W�/��!z8G��Et��a]�p�POq�P@(08�����e��B��c��8����?���D��k<�%RU_���wS��K(;���� �m.{����F79� �S���wGg����A�p�	VT�w�)F�&tE
a�X��>��po�;����5��/�&�wjQZO�6�r��p�? Lq����p�g���S��,���|�`��&�)��Xs{����`_?��^����A(avJ���B���U�{@�A��;����3�������}��}w�����hG�za ,��A(�?�#jl��q�?j��5�����p�i����"�E\������\�0� l����g�����P8����	%�����|q���������>S}F�0{����t���z�b�7�=��b������B�@_W�{@(j��5<���S����P` ( �a�:S@8#��sC������R��Zx�s/�5�����f���-��4��4m��n�w���.�9�����3-\m����8O�=1*{��uy��'/��Q����Fn9��%�tv��_p�&6�9��\3!��m*��� �8��O>��J��|��3���&����[����~u��� O�
#M\=��|c<7q���.�������g��1f���#��f�77Vs��vW$�������5��if�{�[R��V8zY>3\�b��7�5�9�/�d��/��>'ob�������a�kk��G�	-j�0s�4�
�s�����<(��iRd��>��#�=[�;��������w����qqr��o~X��MF�_`a��_P���������~<,)l��!��{[����)��s��B!������1W�z�+�,2�p�t���SKzC�u������0��q�ewj���&�N�>���^t����]�S+��m�L��%�m��)��

x�V�L�1�v{��s��QZ������������Q�.�i;�O�w(v�uU�tH*yY���IO=[�`S[��8�{L�6��
�$~6��^;O��y7�����a���6�r*(����<%+�k��EFX��)�x����bs��c8�j�>�bz<P4���u<���������,���K�LM;��!v��<�nbe�����x^����{y�y��=��X^fP�����,S��8����k���.2�&����l��:�����mb*���-��;����|����\��z=�I��1���{w{�sx��agd??���E4��Xf�2s&���"N
�'E���p��y�@mG8s��o��������J�az:�x��:	a���=-x��d-{����g����u2J�!����@+�C@:q�X����$��B3��l@�&�]I�_9c����R�aEE#nvc�p�����"E�������9����v����KxaT�q��3����\m�LZ�-��u���xhy���(��&��u5<X���V`'���:���OqRb��e7�0����kS��6����1��( 2�f�=i�S����g?��9S�����'��-�#/,,�jS�� ,�1��c}+4�������c*����������Pk���~jc��fPa���O�b5v���
�/��z��[����\�
O%��S��d5�Bo������
�L�E}����Z��`�����3�����yA>2nWc������6��8dc<��|����������'���
J.�?�����e��/H��"5�����m=
x��VCE�}#Qm��f0b�@�M��Mkf���1F���,��8���aSj������x�����f~^o:�2O
!5�l)�13	��\�Y�5"3�gH�e��[�j��Ld�)��Io���u��eu�y�u?'����g��l��P@( ���B� ����%� ��*f%
�\�����-z;�
ba���P��:�[a��Z������n�5+��g�/s&�?�����$ao�������7W�����^8��|S�ids���ok���B��5`���t�!�]�&/��pi�2��������Ft��|��r����W=�����Z����=L�ri'�0��^��?�������[�L�?��2��GGF�B0Dy�������[��O^�����?��U~������|�GfG���Y^^�Y�C,�Y�o'�����������,�;���E�����6�����k�3���g�x�����l�gb���C����CN�K��hrq��&*���m�:,�0�*�`����
���q���������@���T��������5�~h��oa��.��-���]��GYxt���X��������zk"Vcs!���s��2D���(�EW�����(�j�y�Z��1��WvV����d������y\=�2M�m�
9y�Q���afD�8��r�e�����.�����y `c}i)rer�n�*;�h<����4��H�3O�w����O��:�L��Eye����h��u�1��\,���Jc��������C���[�0�������y���KA]B
����Yvx)���v���y��-.n-��S��� ��h})���Y���@=w6�u�����
 �pbR�'��S�X�b����5�1�`�F��A�>9i�9a*|vnm�g� �$~Z����6�b����_|O�f)��BS
�n�!.����f���T1��s������;���P���I��T��V��7�g���'�f^7��!�����B�@.�0*�6 ��/f/
�Fs��h�o* a�E�\���&����JJ�K1Z�yS��x��.�9���GUJ�Z��B�Ym�`N��x��!	_��Z�5���E�N}nRvV����ee�����4�����q|�>
>#}�p�l���������WkybT>;�������mvck�Hm���p�$)F��{�l�on��F����lea�T����k��7�qC^r?�w�Y��6����i%8����T|����8�3�*`Ne#�
+a�4m�K,r^�������b��������_�����\�����O����mu�n�����;A~CK�s�W��w$��s�S�OU��-�v������MA+=�
xi
��@N�(��T[-���]�D�h�Y�X@�������H��M��,�2�P����������?���]X������x�6����B������v������b�8w	c:��Pm��I�	~^o��2e,��S��j�,s&��
�JN���=d�~���Z���:�a/�zy��b�$Q��z�������{��KW5;�.#<��������<��B�@tq�P@( 
��������	
�'AO����S���9�^�\��0�\�y��UI�3ss:�g�W�t������7-Q�N'GG@���
\Y�*%(�&�cRJV����Zf���T�|K��q"����65s���c�uRm�L6Gg���5��&���7�"�tG3I���4���uL/������a����5uk���~�k�HG��\r�3���x�R�[x���h������������hd�n}��\��"1����C���I�@�.b"Ef���!���J.s�RQ��l]�����E�tc�k�:No�f�p��7��a��E��F�������n���m�k<���=���O&�/S@X�=�
�u)��j�M;��0HN��6q���\]����sU���,��P@( �- �-eO���3
zO{O[���W`���R�hf�������PC�^�T�p-Z��I5��f��_}����ecR
�K���P���X���)o,�W���6��-v,����b��a�9W�I�����{�B���&�5���1��Sj���i])F �kz���k����a��N�i���A9	{��w��l����J�C�b�E�u�&Q{�`M��-�^�����6���|���������� ���d}�����j��o
A����ZI���F���_4��v�;������o?�m��LEF��M��{@��W1���R~e.��
�RN��
�j�2 �*���0*Z������������������0�<��r�`��|��|EW)j��Zf�gR����LB��;��������^�hYV0�g��0���D( 
�V���iH����ct&����y��]�����{
��h�������|O��:��Q6�FY������7�ez�����y�h�g�+�~B�\��O��,�?��?
+�4��_�E���4������>���||s�����{�y��x�z����`�?
W��m|�����h$�F��	��� ��^�9�F������o�w��k����3��������v�+��#��!��vx�/5<���2�h�����Z+��\9J�+~siq���k����A�)F;O��y��6�K!^�T�1%R�����/����-��?��s���U�I1J>;�bbbrS]������h�;�X��g
a�$
����?K�����[)F������m3�SS���)F|�\�?I�#�<�N]�����wks��7���������`@���s[���>���:���gKNcY{ghQ�wFaP��M�k�����MZ����E4t�bt;�������m��~� ��y��-��4���X�Q�i4Y�mS�_���;���k�x*��R���A�I��t�3�������TGx��q{9��S�F'����c�������T�0R����>�q�kQf���%B��P@(���yHC�I�pnN���F\���{�'��p�
�=W@8{��h��* ����
��\;��F�-���H5zx=�|��Ov{�H�5-n�|���QV~s��9&e�Ae(L����a��1�A��0�]C���i�,�j�m�x��:^��y�,3���"cD����Y_�G?./�V����ofk��f[�� 
��.?o��P0�9�}�M��F�m�q�9%�v�������h�����O��w��=:��c�!��h(����X�'��~�'���z�����DsU���99�P^^�Y�C����S�����o�i��r?���qQ����~���c�d#TXc���ps��F*c��&�n����o��c�QR�]�~����a���I1z*bJ�������8��S4P���:�s�P^�D�c���s�����&Is�_�'�Z�"�h�����������8�-V&E�v�x�����DjKb��?���K	�i�N�0���y]�
6.�,w��0g�z+)ktrm�x�5Dk_z�^>������� �4V6}��+����L��2BF����U
��~�Af:�\`�Kf6y�^wc���"���4Fk��x���*}i:c��I�M��e����^v�E>(����{MuT����fd:�*������2�(7��}���w��a��b�1��3qw�h�����)����c���sYr��t����I����U�a$o[��)W���S��Zj� ����b���Q��V�b��.�WI�d
7��~��Xo-`td_��F�r�y�����tS�v�gR�t3�a��0�R�Z��u�������[Cf~�\��P@(�[
���p�������_	afze��8_�+�?}�������?�������0�����0���x��n�*l��vn�Dm�T��J2�^Kn�M-��#��]�����T'�����:@2������4�2G�gk^_�o��
����l��(@���|�����j��65/�)F���'����������9�VQ.����`�9���(�bT������w���h0����n5�tOEL��5���q�s���������K���*a�7��aj�J^o:�2O�]_l��6��;����k� �A^�Ev����p��(M�Q���J�A�I�6'�S��?�����g�J��\��]�6��'\�
�TPY���e\��nAz���x���7o�1��������������pm��W%B��3���M�h5TI5C�����������m�pz���=�
$����n�T�����	�(R�fq���B��*� ���>�Z����b6B���Q@8O������� v������v����?���A8��3���� ���A!� �}�r���cH�*P�>/�bVT�)� ��A�6�+���X
��i� \�A��� �A��p��Z�����3��Q�d�������� v���#��x���������<��~o��Q.��<-7�4q�8=-	�J�v�����L=�l�������x����Gd�&�k�����/~��.~�����|P1g��o�f���.\t.?���@��3wL��kk57���B��p��a/����O���fb����M�n����n��id���h��;&�5�dxt��4��}-�>#���|�43�=Au����Bhn���Ao��$O��C�|{��������(?����.^����7��$ IDAT���������<�n2e3P�'�������K����R���������u?���K�D�N�?�wCf����������6�a2�pja����-'x��`X�$��[M���������"r~���-������ ���H_����$Waf�0�F�5s���?y���&��p������,�- ��j[]'���>2��+w��I�zLk�����;��'yY���IOt<6����C��d��x�z���)�^\jWa��~� ��>P�_�5��R1��3�M��ta$����)W#����������V��-LJj��_���:^�z�|��z��g�L?�:�������lL����/�~O5w;kx7��6��1��9	F�]�8��}�e�������8��n�'5��|�tv~�������z^�8X�_��_<��u��[���U��o���P@( �������������#;�)F��j�����F����>Z�p:�3�u�� W�kzs>����z�~>JXWH�#� ,�� ��j���{���������J��"�{�~>X����j��� |�����m��������ig�Ujl���/0����e��J���nj����?��~#j^�$	�A}���7����B�i�I�;�{n@i1���g�s����e@x(��W��4�����?�\n�����k��+8����D#B�����~9Y��9;���SA��p7� l��\��5[���
�B�@_R@8��j���a�\71j��P�o) �}k=�hr�� v�I~���82p���DN_)�Ax~��Z�A�i�T�/��*�j8�u^�b��Vxx~�G�W��4&;;�_���!��xcG=�uX������n
��P`@* aN�5����(?�n�~:��#���y����FJ-�bjcu���o#�u�2W���p�`6G	@(������B�~�@����$a���;� ���
	J���� �Y{��@l'��p4���Q�w��z����b��q-��g�3	�>��4���A(���&��g�v����A���~��u����2p�]���_9ci���<>�������r�HH�����DQ�^UE�R�TZm�^@�P���*������(��"*������ ����;!��13����&������
	��?Jv��y���Lv����������,9�P��i����j����{q��B�o��f��#�s���h���������f����rJ�q��x:����t���9�������A��M�>���^����ta]V'53��bNjX�5���
��(m���jjYR�y��0B��0A@�f! ������D8�����f" ��ZLsL�aa�y��A�O�W��f@6�=/)�&o����`�_�X�e7�ar#%FC� ��8��"�k����C���<�g������Rxu]�+q�I��y�GV
����;���A@a\r �AXD��n1� �S��?���n�k��w_F@����F�\���TV�G�����#i���k�`Z�@x��E�����YA ���_u���zC���Ah�R��H��I��AxJ��8���d$�����=������AX���{H�� <�.�"�@	�G�A���P�@�	����X�����{f�� ��	8�a(�;��xzVg��d���� �}�y${�gK3�:��=�Z���bA@b �oZ2Z��N��a/��<���C^�c�h���+�#I��������[�"��  �@�&��;S��A�=�Qs������l�C[<���c|�|!&z�l������?%�!&�_�K�Ax\n�X�A��AX5�����d����_�}�w�Q��3��� ������ v��\N����9kU��*��n�������h�XV��f2a7w���*C L��P�q����$�n�_H��9$�#��"A �>�q�n��d:��4�nH�*�g7xps���U=3���o�~.�GbA@A@�6D���]�V�	���v\�W���}=[6$� l����s�g2�CM�"��C/f����pG�)�D��A�,9��}M��'����$}��
�������+��������q,]z���L�����V�
��A�A��������j��U?&�A(������1et ���#�5�{�"���*I)m��d2��2e����L���L��z!]�#���
�_�h�^�t�`X������Bls^B�����}J���JA@A@��g\�
��s��~�u�\L�7������
��FQ������CS�i��0�}�>{.�)Y�M�)��6K@8�������;mG&S��'$H����}�d+��n�y2�FY�Y�/]L�[I�:��\�s�:�>%�m"c�"A@A@A@A@A@A@�VF�(1��������ZS���=���bV��%�)cQ�|�)�5��II�,�������ZY���=n@\�����Che���S�X�]��d������F���e�f$M���4������=�)R� �C�yy��F���������%=�� �Z������Q�.�N\����r2����]���%�K<�9�y#���u(x
����eS.II�@�a,+����<p�QW~�G<���!�1��>��z��P\��<y��<�%�K�W����I\O�z�S\�)���]���
���E�h������
��s�������L�`��/����T�|��V��Kp���M$�4\i�P����}�Sa�x����C$���%�����9b?Z�~����������
��1e�,?@M�"+?��[�:t0&��V��+�gvB��-O���H�{�S����w-Zu	RFW����j
�J�A�p�va:��}:c|)�#�q����X��S�:x���V��	�AX�{����=�����6����v�
�{��4�:���P=���g\�
H��,�
Lv�������q�q
��>��cI��:���Y�sfG����d2S���(�O1��c������ YGa}�7����k$\�0�u$)%��0&�!�>�akt�.�.�_���q
�NO�(��D.�@<	X����w����T_����HU���7!_�;�M@��=����A@A@�0)%
�_��fQq�����#V[r�����-���x:�e1�8S�A->���e��h�b������H���7����O��c�|Rk_�q�u�b�����}\�G,Bh���bJ��G���z��������;�u?�e����-����MR_K�>i$����^�9H���E@��m����-��T�y������J�����������c��/&�Z���Wa��V���:��Sp
�����I��������>3�	�;]���|��^Q��po-!����N ���#e��
,�c0�u;�4��'R]��z9����
p�:�52���B��#r���tg�O�}�l>B��ULy�3��������d�I%���#;'��������<R�k��23���y���oK�9y��U����dz_����md��������8b���a}������|�J+XMp��-U��G����2���>|@c�f���5IQ�.NH$�r�������������v���U�+�O��t�x�d�N�4��FA@8��s1����(�;�[�_��p���A�h��e�84���R�!���'�N���v����T�!��h��"Z�	�A(�W�zl��o>����~��?��A����B��7H}-��K����{q��9 �����PV�gu]�KY}Hj��\��J��,R�K)d� 3JM���CV_��|b�-_� �uN!�����w��Z��0�7����
�w���}�Y��=i	s:-�P��<�w����P��p�9C��e�=
��=W�69�u�2�p{5c��d���G<�����@y~��w�!�����'7��| �W�o���>���g�3{������+�������d=�P�5ZQq�)����
�4G8�9�)r	����ML������@��]$�)18F���<P�wr���|!��S�&�� ��;a��Q� L��lL���h�Z��������A��7G�������w�XwNZ>��ga���6������+h��dW���E��q;�K.��~�����D����9�m�O���}��[b�q��� �~|���W� ���h���A����A~}zB�A(���`��@���C\5����3�G���eZ��o�p��U�������I�-�cv����LOq5!���$��Bc�g�,3�;d����A@Z'�:�0�������oD���h�'��� L���X����e��&>���}�p%F[�������_������/8������;�a9���'`[������os��.����P=#���P���.�6B�u�<�����FK|&�2Cca,������S'(���M>�#�0���u!6+����(6u%�b�6�r�M�(�A@H0� L0��gx� l={)��g�D��?� <��X���@���O��@�P����������(M{���T��yG<?�A�ah�z��
��F�Q�F_���0Ra���� ���O����g���i/�����������i�����{���`e�
��w�[]��l�osW:���M���,3�_��M��04�/Q��������'�:���6��S����}�dw1�
J��w�1����juk^�A��*�y��Ov��5����S���t���*Voq�n���*���c�1�z+�&o���`l`�l��>��u����R;�.��������_l�9)L��2's�X�RxzN�w5 �>y���]��Q��_����V�)��n�a������i��8������JN���CG���>lp#������_9��_x���*pe{>$�SF��+;sV��J=oL�_��M7�0"��X/�-�U����y��G�����c��)�;y��^����n�?�q��k"�W���_��&<\/N�����7n������e��31�q:���n�6;����]*�S3���r�����3�xX���3��<��:�v�����&������I~b���e�u9���c<Nk�@��%{5jl�C�L��`���d�������k,���`x���H�1.��?�\�+��b�#�+1�}��c;5�8�mpMw���%���������Z������IL�!1*�AL��~�gi�r@�4�Q]������7U`�>/�5U@
\�]ff�@G���T&#��)[�j���w�/d�4�_Gc�G����s{4^�����Y]��n^v�i�C��%N-�����g'�_|�p}O�I���7�k<����N����������rj,�Q��r�>N8I��
D7���}Xcg	�A���#1�D�s����}������ld�k�H��	�1�:�K����:�uR}>��E��M������[O�v�)�%:5���{�CA����A���a������_����@�F0�@aQ��>��y4��|����F���H$�X�_���#p|�g��p=�/q=����� ����a:��-��p�pah����,+C�����[�yKf��lf^���E��Y)<�~��7�!V~u����
7a�����v�Qd�x<�G/�F�����kv��������7:0�����n=���C|���K��yD�H��L�U�fa�+�4���3�@X�d��%�
�,����A{�W��mC�c��[�/�D�����~��L �b�`��+�/C@t�_��i5�?�����W:��h$�t�v������<i�{�<�p�X0�8$W_��g��5L�u9��h��GGM��	���S:De�z��#�)�W�&n4��BA����6.jl�_�����t��y��L��	������Mx�K���=����@ |s������X6D�T"����v����]�������a�f�-3�'0�jL�B�MG��������Ub���^p1�.q�[c��~���d���H��g�M ��A�3<�V��95k��yE��a�������/�N%��hx[O��l����o
���
���k�e����
;����
��S`���������a��n��}���G��=���
�JC ���n��|�D�8�g�p��  �@X�A(�#B�A!�p�p��M!� ���a��;����o�#�w��kW��&�a�K�����!�L����el���'���YV&�Hg���m���+�m��
q,���v�zU��s�����:q8�{a	�Rx��4o\����g(c��v��v*b�a�@8��iL�c2C�z���O�p��G��6Ee��\u����3a8$#)1�	IL�{:�����r�n_����y���p��n��
�Aq�I3�Mg��G8T����isTnz;�	�uH.�������|:���IY-�-����x{�%����4����=�T:x`d)������.�j����S���v~nG�@(3���L�c�KP�d���h@���i~�G�� �O��|y�������������]e��Z��\���R���+���
�#-���rt%LZ��)C��o�k�.��J�6� ����{+f�;V]��N�����������xtf������*�fg�y
�%r�q=�)���*��
��J��)�IO5��jL�� �W�q�V�s���u��[j�����t�9��*"��G�J�:�n��=F'��Ceja�/YG����4������{���g}�~!q���s�`k���Z�gzz����q���cn���1y��$?��'@��Cbf�@bs�K3��g\`T?����h$�C�������W�G��E���I�J��e>��3dfu2&4���^��VTj�����Mb���6�~���5l�bb%FI��y���`���Dc��O�9��~��aC�uk�x��`c�p����*�nkp�:��T&�����,�����F&��  �  B�a��Z�aB l� BB ����0~�-�CH}.D�+����������0�p����tr�)�w�a�����(�\����|��
��Kr�����7�'?�pG��OU8F����h�L���V�U}�w�~/�/������������P�������a�A(��[�,1D"zKF�����������O �=,�csI�eE=���X�������'�����7J���j@����Cw`�	�:����;3�����[ ������b�+9��s�Z��33o]#|.U}��n���a��18M=9v��g��u��B�Vb��Y9��)�y���GT����|>�+��������(���'n��Z�����B��O|�����1���i,�����z��{��M�4�s��AejR�cu��k��6��
7_��P�I���D#c��3~N��j�%?��r���|>wd����j��Ec�23}�S��������2C0�?d�R��e�����#i_���jnvu�@Ol��&0�G���@��!�u���/����'�>3\b�����V�#�y�q��R�����k,9K�R��d,p��>66nc�y�����J�l�7u1_ qM�7�p��g.I���
rM���Z����lc�
&�A@"" ��0��@��'�@�z�JDz�����b�~�@�ta�B��f��B���xvZ)�Wz{��|}��7�p�]�<W�'�����h3Nn���qL��Y~5&��S��wS'�8�Ws��J>	QBR�-�U�������;1-@�1���^x��+�<��=c�zl��i	��[pu��e�
�g��IF����ut�0�>E"������v�������[w���~�CO�2�+�<���^�����������H�'���p���q�x��v��x�k0��,R���@B��/�{>�X��|}��T&�7OX��E������T��
�Sb IDAT�_|��M�s�%g��,�S
q�'�.il��%���q��.!�|a�^�����!���r�\m��W���N����6�<�i>�����
K��#���h�Pk������3����G�1��  �@��@3��v�[����������' ����
��M��;��y��ib������^���)$<������{�k����?�i/�!}��>M�/�8��p��O��C<5�d�>�[���������=�k��H����e�2d�G�y�I]|�'3��>U�
��L{��.��u����q��]#���SZI)�p��09"���nA�>�k'��W��|��mW����,dx_�o�`��I�[�^�1D/=_�Id�1�n=�[����Z~��c&zU&�t��k7��O��{8���o&�*��.��"����FB��zA%XuT�
�u%I�<���wVq�x;��+��
�xuE������/\���I���wu���lB �>]b<#�qY��T�_��8�3�6�?Z���Z�@h8����-�(�8�K
:I��#qN2$��% C�w�*�XS
k�46�x{^�+�j�9��&2'LE�>��/��Q�q������3|%^� 1�o��$��|i���@��H�&�� �f	x���{�������{�����H���N���x��\�����S�����#���5�st�p����>��<�ny-�\������5_������+G6,���'>f��
�2t0f�>���C?[�u�����}��(�R�z�l���E~���I��?Uq�W����
�����������%���~�����D��Vlh�/��n��q	a�qz]H
�rzv<�@8��N����ef�+�4���My�$�x���1���r��<C���n�74��h���h	�X��f���dx����j���#�&���4J\n�{'�:P`
����\uU%�wd�W%�����X/����O�2���}�����h�w�������SxzC���%^���[���m���'��"�aG%�����{Dzk�a���8�%Fa�������qRb��1y���>2[��Wb4Mbo?�(���J#6(�K��uKUj\���`�/]��b���}u1�4f��F�:CL�z�����ycsxJBF������g<"��W�q�����7Z���=%_$1���a�UN �s��t���:�F���tz�	�����ky��h���8^A ��0��G/����6���/R8[����� lK�-��p�O1��� ���!��cf���L�-��(t��;��}���%F��tv7��_�=�[���=�b����Kx�w*�g�cD_���)E{]������v�Z�~���J*�_l��A���u�-g�y3�x��3������������c�� U�(�Z�{�U1�C%������}G
#������X�:n�_�`�����y��c"�f&�H��-d�x��(v�i]-�l)L����QV�H&�Xe���x�����hc�t���R�����i�����Q����N����heCu�KH��L���+y�����Vzg�rRe�J?���U>��"n~^b�����U�M�8����������:LQ���C��N}����E��	���3��

����G�q~�����U<0����2X�tp	��_e�="f!��F��E3�IL��:�*j4��
s�k��������Obf�Dw�u��;K5�sH���]���7i�:Uf~�w�X_���g}%�}�**5f��\���s$�[���f�����9NXs@c�n�5~}�6���&I��,����}q��T�!kv���@bR�I���q;p8`C��FY�����bw@c�f��T�����'��SS��k+�	��X�� �~������&�zPc�f�}�%>3zbn��r�N�~�����5��Pc�.�}�%F+4~��s���'Jd��nZ�@�k������%�������	o��2y/?Ufq^�W�8OA@���p
�U�����A��N�0_� ���Y��k����\�*� �c���a��9\8[���������M
�k����N65&����,�����<�j&W����0~lU���y���F�U��/��+��#�������f9���de�fg�@�����h���$�jl�_����\�h�^n<�����B}���}%a!�SjtDCIT@cu����,
����Q��=����Y,��!��0�����Ge~�g���9����(��Up��x=F�q>8����V�q�Z��
c�A'�G���2�0��E���O4V��w��p4����A2�;#85f����#�`�
6�Hl�����Qz�k�e������Wa��G�	�Gs)F�.�Hv4P~�'��>|X7g�=y����S��Xl��I\�l�[�q��!�<��F���������#���=4��U����VE��CA@�	a����iG����d�p���q �"���� ��6{��=GR�v���Q��
�����~#�w��kW����p�{~���S�=���H����e����O��z���o���A��]#����)�#I)����19���P_G��v���#��Y!���c�1�zx}@	s�&�:x`j)��R��\#��b'��U���l�d�9��F_�������f��Zj���	V�\����'���C�l{�����i�L������g��wlU�@�s���<R]7������9d�B����q�i�"C�f������s��
c��C�"�����*^x����Z��9fT2W\l%�!��Z��]��?td�T���1�l����e�)*��3wa5�tS���kS���F��w=6�@��(}����`�z=6��C�}C*�.6����2'�?���.��A)��|��B����U�~U�5}�$&�_{��I�@�/Dc��j�]h\�k,��nMahVLI���B����4���Z���sW�i<�C��rt]���JL��?��O�����������}�3%Fu��;�;��c�;���3��l�.J-�)q���������zK��P���x`�����o-'Hl��� �����<��D_708W���7��84X�O�������I��.���uo�A
��	��$fI����'�j��B}�����@x}	[��}�m0<GbJ����������z�wJ�����/~�2��x�������#��'�L_���"z���'����k�X�:��1�  �� @��A��sV[W�^����[�{l����b:q0���Z|��=@������8�]�C=D���.�Q�-�H�|������hK��z��-!� ��	�w�������%$���M��o�<��A������3=��1��&u����/^������pZ6�t���3�"<�P�A�)���p��_-���!�)�1�
1u��� ;E�F�:���d���0qX�!���b"�C ��uq]!��  ��HI���������p����������c�B?�A�������C;/�v�r?�L�V~�����{�\��7� \���e~�e��.F� ��x���0���A~��h�[�y�\;q~�A�/��-�|��C��-���Ne@[L��VG�)=C-�Q�1w��c��/�n�lu`�p�B l��/�.��  47���>�w$�>�/���o�z����C���w�'��H���l��������������3*1���O�B��y?�/b��^Mw&6>���o\��	�A�r�p&������x�S\����'��e�=��{F8[�<������x�Z�Di]�Fi�#��0�S����u]���N�$a��M�4���?��_<���\���E FfB �G��1A@A "�A&q] =[J�aK�	�  z�hc��0~.z��a�v�������s�?-�[������Z��H��&� ti<�-,���1�_�^u=B��kc\[�r�@��wO�.��  �2�a+��c��Ax��G;��A-1q� �8Mw&.61� wm��w����{�%��o�@�/�}O"y����N�9�:��hJ�^�C�}F	���*~�����C<�������#��	��P{���h.hQV|�)1jIM%��y�!����j���=���.��u����z�QW�6Li��E��>�P/1~}�%F�C��m�sZ(�@�	�:G�qn
UE����=������>����A@A@����o�;�]�Q:w�j�C������5J�&�y�s���8�=�����z��x�x���C-e<����)�L��Z���H���7����O�z��~q>�R��|m���:O����q~^+�1�����#�S��>]���czf�~���E?[�u������}��(�R�z]f������u����wo���v�������O���f U�����q�"����xRA��:��������"���R] ���� �	���Sp
�
�kW�� �����aA#=u�p�_B] �	KZ��_@�-T����	�������G{/^���P��z^�����\�<=�3���C�,�@�dr?����#���.�_�,�{"�i2���LPbXA@A@��J@�f2����@�����A��[<����@(^�$`Y�G t�������hB <)�!��#$ ���	�@���--�����M�X��a����@���@����a��@���)�8���n���o�F�{�CR���=?+����A^�A
�t�Q�E����nd���4�#X�!%~��� ��M�u��c,��
����s6D�{:I>��qU�U}Q��(�"���A�p���Q�o�nU-N���A@h"��;))���$������'���G]�:TV�,#�>��i��7���P������3��A@A@�<�v�.�i���{7�q
�������O"�g�j]?�p��-%_D���"A�K���eT�]����a�H���Kj#��(N!��i���Q�}��k������
��g�;�A�r>�M�T��n��(��F�_�U�k�@R:_�������A��9$�A���-�A@A@A@A@A@A@�&C �k��j���5�([>D���d�anW��4MNE�T���N�������y�����g��
�'�D��D�m_
�-���H���N4Rl������ �����l�4����D��
�����H{�G�p2����{���=X�hi=��\�^��#H']�f�K����Vw������a�]����63Q�ZZ��w�hL{?Dr���{�-����%�j�[�XA@A���l6��>[�t�%�q�W��H���'a:R���s��74�=?��+���@����8�c9�V�z6�UE*a6��"
B����!�`�ec��k�}�p� ����?�����I�����\|c�����$��r�u�q��x�/�8	x~�����o���8W}����t�I8w������!�����v!��#���;{�V
�s[����a����V��.����[d[�����Zq�fE+��{�zp����#%%!�u>r�����W��5U�W/%[�{��]�[�,��~^���y�mh���ww�\�k�~RN;
���U�t@���Md�����k0���d�u��v��,�]�<l���d1Q�[��V���[Q0!oZ��c�+��$��fj�!������c:�����\U�z�uh��F�������
��c�]U���A@8��Y8*����C���S��G�uHb~A@A@�6D g�������A�&�T�$��!������O�<����9����=<,^�%�lK�W2&��jwR2{cyj�C3Q�^Ar����������������4C�+����h�K�wb1�  �J��?<��5u��>��v;��
�|X���r�*;�3����v����(AT8&�����{�N�w7��G�r�m�r��6�����9��������:�p�g6oG���X��V������H9mZu����B ��:\E?��,�]�lR<�����v8�,�CnDM�@����@(��YB T���QO�>2a�+dU����X�Sx�%3NK|��4��xL��+�{��.Q�v,Ze������-9�h��c��S�9���>1F� �������O�s�nIq��E`V/#��0�0�`�6A� ��  4��]���<?���e����w����`-�qb�=���8��%�X�{%��|���G���L���i2w^����J���@�e��$'
m5������'��u��2� a�@(����s-��BK����
�������u�p��'����� �.k���[oO�?���"�6��j���[[�+H�{GD���JqN�j�M��#�:��G���O��������N��:�U8S�B���z��ld_��"L���bIM��P��G����(j���>L|�MF�������,�R��
o]� ����������E��B l��JCp���R�d��k[�
jU�S����������.���V��X�Y"+~#��D���� 4���"�i$�A@ZK&��7�K�uV����3��@�L����<����A�L�c�F8c�&�C�����
tS�y]0��V8�\��DjM��������@�
������}��������e�D"�%F�@�B!���a�P��b$ �(�	�0
X�*���	��  ���[�����@xL�G5���%J@�	�+O!FU�mR �$���Z�K�%�����B ���8*q�@�8�����\sB �&;����  ���K@����&jeB L���+���#	M% ���77!FG\��A |����~Je�.��#3��23��k�/���?-������.�}B�JT
'4��Ry�1��ViDb��2��W��Y�	=�2���qM2�������Te�3*�}���G���L<r��z�eG�]c��*s�QY��.q��M<2A"�o��s�R���f�
�o�7�>!�h����Fw��G����Gb�|���5vK�����Sd����O���*��k����y��d&�$3�"_��R�����V��S��'�7�3L��/����83��#�����Pc�|�g?����o�]&����G��?���������[�\!�v�G�43����;����^s{��F��n4W��wV��@XlWy�BcM�F!�$Fe�\�6�/���D?��S%*K��eby��XU��t��
��3U��l��V�=P5VY������y�Mbx���Y�e���^
^
J��Ty�\cE�f�s�d�u��)���w*��hbsG#_��nf�+*�J5���<q_�Qfj��{_�C��b��5�u���4����A�j���XT�z�X$�����^�w�)�R���3;���8����%0����������d��R���h���8'S��,�~�,]��U��jlqy���$su���|�C��+['��4��ZeQ�����w�+n(�o�8CA@h���@�i&�T2��j6���_��OS�s���M��xm%Sw&g��T���
�}S���t&�#S������|��EQ��_^���������J������	V�\�����0,7�si�iQ���?Y�G8)�D��)�iz:�{7SQX���Z�S����������t����?�G:{�qB ��[s�%���-�' B�!�����1���[������5m��k�p�^5��:���A�P�y)r�j�A������8_��]?�������U������T������Q�x
�1�BJ��:\E��v]����U�IN�������W7�����������|��n��7��07o��[����$�~�r�u
�B�C�� IDAT�U��]��{t������{�_23���Y����1�&7�}e�hf��U]����/K�U��
��V��0�u{��� �6<�4�+3HO�e�u���3�������K2� ~��z{�P���!���U���,6������oL^7g��}'X<����3!���<�tw�qM!p4��'>��$f�21�\/��_�������������!��i��N^�������
3*C�$�r7�~1=	/v�H���}��*7�S�r���z�V5(��
>8+�����}�vsk���L�7�E����W�B8�4g<a
d�!(�4��Pc�6������YC(jt�@�!4���Q��	��  �@�$��@����
��5M}�b�=��{:��V(
jQhe����i��-�@z����v��v��9�6��+0����b���\����R��!
�+e�v��,Z��N6�O�}�v���"���g����H�<�S����2�r�����0N���FL�xR�~�ON�u���2����W ��fy\�'^����o��	x����������@96��%q��c�G��hY�����b����3�a�x���e�i�_�A�
�=.1��dF�����w��~��KY&6�)3HO�D���.v3��if���e�X�����q�]��d&
�H2A�z�+nTX7���6��y���z����$&�����O���U�s3�1���b�'&b��gd�V�����uf�M�bv�yB��`��WK�H���t���Oi���t��>�f��2>5�����np�H����3��-2���sj�4���w�@7d��'�6t��{������&�6��������w�//�Tn9[a�-fv���n����(����Syc�;���q&��,{�G��{5���J�dW���MT��,]*����&IfV��H������\#����:�P#+EfZ���6����2���*�p'������n�!�W�&>��w�-Z�
�9��2y���|����z��x��Ra�A�s�L������q�>������bff�X��ug�����6	��**P���6�4�����[sM�`80u!�K�z��~���G�e��[J%�v��Uw�y�5�,S�k1����D]����j���P�a���R�~D�l�@(qu����,��(�Z�1��������b�[�$^�%c�9z���|���w5N�5�hzL
o�����  �@�	�B'�~�������~�+Z]��	�l���O"]��OHf����,36�F�[e����sf.�������-�3��R��������uF��
N_�ota�%����]a��5,\cb�����{��xA�
Og��4c
P�������B��|�H�g
����k�����?%y�T4����sq�p6r1�}�%n|� L[1� -����A9�'F;�8^���pF�N8[�������=���3���e��/�&���q�hz��FJ��d	p
z�f���f�'�E�!>����
>��o���1����3^;T��Fa�+�������dFs������P}�2���U�~���
~�Bc�gB�w��i�����_@n�	�SP}�B�%�eE�����r_+X~��W��y�sW�%����2/3q��[�wf]bb��2����FY{L4&V(�:D�+-T��%!����!�ho������B���e��UX�`����;�P��KZ������o(<`%<j,�Ya����F�z#%F��J#�����N?�]eH���!������]
_f�x����i������{��{U~n/�'E����_�DU��&+��-�@���5��Uk��@��[RV��������V���&��A@A�8��0�����
�,�vv!7<��k?���$}��7������r8������s����z���0�	�16Vr��+���9�g�
�M|�\�/�j��_�����o���J��47g��t7��?�2�hQ;z��d~������X� ��e;4D|bZ��36�P8[�~{����U8��?� l�P�W�Q�{/ac"`�
����|aT�l�j���/D�8_?��Y2�^��������<��nw3�T/��Y�p����J��"�J��'�����*,db���EQ/{�����n���y�j	W���mo���a'�\p�����
D�&~G���@c��7�Kd�����A�����1��d���2�Pe�G~�JPz�	� _��n��
#G�@H�K�1NA�5Rn�.���PW��,1�[l}�y���U�@�UfFV>5&��W�4��#�Cq�  ��  �$���0�@^q��=_��!���MO0f��@�_��l�N�2`��_������5����X�$hc"`����d��+1z�ICZRzG�pF�J(Q�M �(1���H@8���� �)�q�oM%���4��p����z�i�:�[����~=�P\�~=��Y�y=oB��C����0lW^���-m���>����Az}�� %�9�^�0�����J��.�����s!z���m(�E[+=�,��^���S�3q��@������V���(�o�Pg~��U�a��M���@�Q����`���h�[n��O}h�������)]�����*k��X�r��x�?~���5qB�	��P��B��%����&E"K�4�YF(1P�p�B�Ss����|j'��)������%�
�0�W�_A@hB��"�������0���������A�5gw�.z��aw�=�<�^��K���q�=�(�V��5N�^�`�'�6=���X�.;�2�sI.�P���A��}�W~D:�T\�������p���	���=���8.>�U�1�o.����
��.��]�2t9r��������s\�~���2��,�c������x�w=�[��y���|Nyk]��� l�����9��I��&�U�����a�L`��>!UTj(4��R����K���8���b3Jxn~���c"�9e������o%F�~�z�_a�<C����w>7�l3��%����n��s�c��]��^���)�x�%s@�W_��P_84���oW���L������&���<
�=o)��G=z��D��	�;�~SE�Q��y��|%F��#sV��pw�o�F���������(�i3�>;��{��6�AXU�rn�J���(\��9&�����g���c�w��<_?���`�YX!�p8����&D���fbI��������H|.1�  ��  43��� T�����������l����;2��vF)��6�[b����3V�d<�.����:�g����"�G�.�G#$��X	a���y���pFG^8#�Ax��7>d���$2��
l�P���T������$Cd��u����d�}��H�Y�]c�[*��TYU�@�>���*,=M���L��k����})��/p��R/���%F�����Nb�_LL�@"��j�5���Xg��q��������?����gp80��g�I$E����=b]����P}���9�1�A7���x�M3S����j���������}��
�����t�~W��MA��X�:��]�������#O��A��z�<�W��$���j��H#���
��g�FY��� �n|�����[��=.���'��_(\<Qe��-������N�1�Cp��$3+Gf��P�����\#���psh�X]�.�[R�)Mff��i���A�ScM��i92����?*�3�<�U�,=/�cK�T�Ui��Z��E�T�:�x��D�����m��e�fHt52����]�����E��h�@��bM���.2���k���2���
W}�U}�/���R)1>[f|��Q������hl�d&�3T5(�l5����3�*�h�cJ=�"�(Wf��1����U���tk��QaC����Ht5��L_��2����k[q���J��=M\�N'���;�~C��  �L��w�L^��?
3c�?�:�>[����wo�M0>��AX�As�lL��F�l���Ce��"nx��@��j&^P��e0kz*���������V��p����� �8�)G:��,�c!��'����9�:(j��������������|i`R�����d��s���	���I���|�����������NI���V� ��*D_���ss�?��I�������J8
��u���D�1�Gw����{`B�32����>��=�u�?-�:�������)���:���?��4�hT,��!r~z����Y6�^|\����/;�4�u�Hn�=����{���$�����a��F�����z$�V�i��zK��_^ce`O3�����ht�W���@l)Q_|��2�����WS�/���{z��s
L��R�@ �����WX�[Pc�A��aX�����a#<�Z���@�!���
�����<����y���Y�Mf�!$������,5���U�O���~����nn->�����''<"n�^����A��CA@�VN��w�����t^{>�|_��x�>�-���w�����K��C�W��;��\�_��5L���wC$�.�Gr��JU� l���d����Y/m�!���6C@8��V7����pFwRDG�T��Ax
��R�y)r����64�I
�A��q��O�i��U�����hT��5��w��p'���Ip2����3e�����E�~�;S�7}��c(�+�����Y�Zu����~=��U������������Z�o:V�^���������~���*��H����_o��>��u�*��_��C����{���c+1�]��WUO?�~�Lobd�B��K]F�GM����M�U/+,x_��o����L�L���$rm�5&7(���u�I!{�E�v��P��3��w�<{�&3�&�)5p&*o�U��]-`/�w����A�����O��/�FJ���,����n�?{gUu����%3��	a�E@�EEp)X����V����n���~���jQ[��P[�P���FAE��d	Y��g����l�$�	3�L2	�������<�}���{?��<Kk5��E-�
�vR���Y'+>���,��fh��
�M����K'nt0�u����"�������N�]��3��R��J
�[�inC^�J�D�+��'�U��Y�
�3RnI�p��#�D���F��?j��iQ9���
�%)\�����6:{��<^�d�
�'j�9]�M'��:���XU�\��W\:��S4�.K���79y�Ne}�+nl�pY��u�
�]Op����@�rg������������h�/*�T��
*�=�}..��(|#Iq��^n.�r{�����z�L�� �?
<1�8^������DU��*������O�_��������a�Q��\-��r���0�5�6B@! ��6�P�)�T���ih�}�:����J(5�B������6;d����S��m���_#����v�����>�Q��S�3r��I|�
��>tw_
�M���&�Zg�k�84��H�����T�|��f�}��W��Q��������z��S���� <���|n��fo�Ou�����q���c{���<
#�@����&�@��vG�W����aB��<������g�4��!�f����|/��'j��A�/q���0���D��n�0���M=NG�-������C:�_pAk
4! ��B@D��@vF��Bj�h!�����B@t��8�KN��+� ��8�� �1axk)�c���-�<u���j��_��nf�/��2CFuR6���\v��B@�^#0���1���@�_�P����� ��U��NV� <YW���[����8�A��	����q�wJa��z�oz�G��7<�B��F�w2�%=���[B\����I+�U~t��!.��>^��c��7=g�����@"0i&��B@t��8���{o��������e>wO�q��"-J
�H�J4��0�t�o!q��KZ�=q��� ax;FZw���,��Q7~�e�*
��MP�U'�@�cq�@c"�! ��`�A8�4�t"-��0�w����B��D@�'�j����0�u�
�
���������!�mt�]��9�����4yk�L�!�atd��������"&NBm��a�B)s��bi?���������KH��&~��$�''a���9�V���h�m@v�k�q��n(��XA��G��h�l��8�4V�<��8C���;�����M�>��Kk! �@�	X�/C-6q��O�=��/��B@! �p����c�S�T�������:�e_��}�H�7AE��gi<{+�3Kde{�@��K16(48���6���|n��fo<7�����o:�k�����a{���e�����G��B��j@�'�?�$����������-��m�Oh���hf_������WB��\��r���ng>�8���1�=��|e���������k��q��x����O��`}�;=�E��'���~���� l'NA�q�	�����x�l�%��#a�D��:�J�2��_���!BO
����������X`���G{��8t	h�}�:�\�\�V����r����B;������g�r~v�!l�������B@�7����)���
�������x��B@! �1��93��)�f0���j��7
���PG��3s	����4L\���5�!������+J
�X\Wq���HL'+��/h���U����o����@x���y��a������lq�`����&�9*j�?�K�>�Q����w�=�����A�!��������6����.��� ��$�@�A����@�A�7t&�{>�5k��x5M;������~v����'v�A����Ah��$E�����moo���#����v�������;�ZN{������u�v{������B@! �@W�����$����/]�c�������<=t��H�D:�������-o�����Q!�A��D��t��'�� ����q��Ru�a�����R�9�_��A�>?}�_�����?_�{`^�����/��MWc��'�p����������_���o)YC��g�&!���K�w\g~��Z4Z'������G`�������^rqw<�o�'z���Ic! ��B@! ��B@! ��B@! z��	B_�`�_SV���51	]��p��S��*�\��j�����q���<�?��S�����(u��L��Vq�<�vj.M+?#����PgO��P�y�^��N�4�kT��������@�:�3	{�gh/����)��_�����S��<c����:����~�5`t� �r6����E�?�����e�l�mO,��~�#d|���/0��?�����|���@��A���A���A�z��A��@s��MGq��Fw�������/�Vz��+���"��-�v�\������%oJ�XZ���FM�p�$�~6�g����@)��8�a^����d�c;����^ymf��.�����y$~�}��k�*���b3��={f�C��6��}���p`HOC��8�
��CP+��?���
8AI�O��u�<T�������R���Ar�h�wIh�O-_c��W��p�E�G�~.����fD�A��!�&7�64��B�3��Si��`3�'m�|���W���,��,��9�f���$�+���a�>F����96�h���-k���8���>F��#n}1���=�bK�����}�~t��
%���u�3��4~IM�0�OE_[���������K �~}�������..T�-! ��B@! ��B ����Q�.��mv����vl;V�������w8�?!��@+�����t-q��Hd�?�������������
�q����~��� IDATA���b;���}�5�F�C<P�	!qS���������[�����	��i���b4��Y���]8t����,8����@Z
*N%9���E����P
W����/��1C$�������[;�N�8�#mU���+���!��Q��G1
��N����Q��O�t��>�����q�K ��~gb�p�����W������x����<�'Zq�'v!�e��&�z��i��O��'
�,D����'���o~�'���?�O �����}|.����U��|���6V����G&�eQ�??���{��f�i)��`�UT3a,�����
����]����6a���e<! ��B@! ������X&��	���_/�z�d����
+v�=RK��j���N��a�V�D�hhl�'.�t�I��+�0��]���{������@�l�.a�u��	���?��w�������.��A�����<e<�/������H���>���JcB<�GJix�4�F�rz���F�O���b?�0��@�����a�E8��~���������\���_��y~���_�s��!�W��o�0�����M"���(��[���e����;5�N�hL�F�]�/��IS(�y�,��L�������fG�Ah����?��BOo8��Ysa�D�������KX�� �yr�b]����"�]"m��B@! ��h��a�rt���(]d�f�`��#�E.�$����hE�_�aih���u	��^�~����p��8{ucL�1������#�����3%��ac1|q������*i�F�X�	U���o��t�&��sQ���/��QJ})�����<�5�O��h���N�.��S�B?4��}�N�)FE �z?�Xx�0B'��)�! ��B@!��	�@x��� c���0��G�;��@���"���/ax�$�I'� �� o������eX��uc�zd|! ��B@! b���"��E �V��������B E �og�������.�1�Y�*k���NNB
�
�aQ��m���<��bY����r�+fM��HT
.4��4���*V�X�@���4f�[X\��A
Bo����(���Ws�5_[-�����FNBs���8�o�����FaYK+���bc<s�rY�O�����f�k��1�X��?���cG�uT��)�\o�$�O��y���c5,�n��U��AY<<4�	��fn��#��ljf��3VAr��3��i���K�9_��V�tJ95U,=���C������$m��+�ky�������1*#�y�i��0��J�,/! ��B@! �@���@X_����xm��"�L=7�yi-,zO�s��cN��d-6
7������/s@��yd��eF�5����)��|�_���������fV�AJ3����UI�6t�de��z�}��/gP<3�L����r��<�����V����A	\}A�_O��lN�����<[��rW,n^��;;�o���N����c���7x�;'�y�x����#a�,D�0D �����N."���8����0�eRjjv�m�}E{��R|�����\�pMUZl������}�h�1�����Y��y�f"J�l5���WnEw�H*���;�asl&�2{�!Z�n�}�����5�t�ms���4��KR�x��S��6��u���F0�~&��l6a��s�?����y��8V{��u,7�%^�'J�����wT�%�����t\ ,0�)2�.6�_F�8�E�m*UX�9L<���4v�3''�F'{kC8(�����,�<�������Jf��cT'�0�y{�9V;K��pB|_�������Y3��P�*�(0B���j�~U%\���sl�Opu��N! ��B@!�����a��w����H-�����Ml/Z�<��?����<�~'�"-�X�j	wm�w�^�2����L�=��E|�L����a]�V��r.��7&x�O�]�-e���'X���cU��]OgeC�}q���2�G <�{�_
�;q��67j���v��Pu��������qz�����7z(��.��-n2N��g��'�� ��u=�Ax3������()��|V[�����A��?o#�_b���a���:�������Ri�A��D�1� ,��f��tf$z���o����e���R89�	��K�������-E<M����T���@��
�e�9!�d��eA����U��U��!�(�u����#�S�g��<�4����v6�������'o���d���,��Y����A�X5$�����>*|a"s�3�/7�����SXT��b��n|��n{�y����YwBI1�HNj���b�{�U�2kSG�dY������/ry�Mg�[wV�7�������|&�5ai,��B@! ���Ua9m&_\�S$����p��g1���a%��s���(��W�m����3��/��_[�]o;��C�����`f��#�E.�$yD����e������s�����.��Nvm�����}�p����������3�����s��@K���tY�0����]�T0��
/�o.�]�sm&~~)�^���k=�w�TT�x�C�We2;
�>-������m�|]Lw�i���xO1�a�,D�0�A��#��\�A���� ��_��-�8c�A��Ut���5m�.� Z��)����cG�Tf�T���L�GtR�N��vb�W��
f�i)���bs6���"6Na~��K6����)t�����fm+�(�X+YB.kF�P�����!��R�u��BaM���owRTg���c:���c�;%i(a�u��������������������D�@! ��B@! �@��Ah�[M�����*���\��8�_��A���_����u��i��{J(�>�/u�)����?�(-���[�bx�����E��2��.��O�;7P[���b�����;�X���]uP^jr���
��y>59�OnIe�����]��8g�j��;�����#����bPq���J���>���_�9��A�w>���_8��A���8c�A0�cC%�;��R?FA l�_75��;\u�^h?���A�I �,�����������m��G�@E\
K������v%�y�9�M-�9��=�!�v��@ �����dWm�xf�%3#����Dh+,���B@! ���I a'�/�N�u�����������+ms��������������-�u|wi5��R������s��MT�e��8�$r��xf�����z��:�6rQ��)�����&��Va{q����X� ��!�E@�~d�A�m�~�AN���w�O�����#a� ��x����V������jnAw��� ���WuQ��g���a���R�0�@�q�5���Q,r��u�w��@�N���.W-D����fs}��TEK�:���m6������-�&6������	����A��>g��B@! ��B ���jt7��e
B��,_Z��=Fn����qr�
��J�z��l>��kb��-l����J�������!:��������l����V*�3���<=1v\�m��v����W]5�G��u����~S����Z�p��<����@���=�6��T��O�yI��?<����_� <
��iz}���6i$�%�P�,L�����.��A�K����-�j�u@|�������O�^��9�5�s~Fb��A����3�H|[�Ik#7s�c�L`����u4�/h Q����k�3zO#�L�!�g �*����9����Ns�������iJ�)�S�VS02�1�%,��#���LB�W�,���O�m��XP�P����S�wZ�`	��+���
��pW\�! ��B@! �I ,��r�/3��
��a��
�bt�-�:�D��^�3���'��L�7���S(�6�.��4����e�v�Q�K���T��P�#X:�X��8g-o��;����}6��L&����c&>���hn�0�a����8��)��q���D��-�� ���sG�`q����Cu�������R4hNM��"0����,����s��P^�S�ul��/b�c��2
��X~Z3
8��g����1����#@�K���Q��6����ZX��l8r�9���#9^��b�����-�4e�������_���|>p9��c����'�0��;���a:S����-�,���%5zO��D�+�n����)������g��XQ�N�]��*�yj�`���l	] 4�to3��S�,Q���b�c��2Z��p�Y�! ��B@! z�@85�l��_TR8.�w����$lN��4��wkY~��V���o���q�}}&�?�@�W��4X�t��-��s���~��7%����2/��i5�6�q�r���������}.�����-��`���\�8)�g���W�������tL���XRf����\��z���32Xug:��9U���Y�a
�o����k�����hzek1��@����\��Z��
��?8���4��3��������XT�'zH|iR[��W`��>�i4��� ��wR�0��Gj�\�#5�a��)5�a�OKg1g�+��o��$�+})F#7�������3�:4�S`�Q�N��A�:��;�����Q`�"��P����9���+�-i��1�X��m�����i�9#�)^!�S����ci���
4��yU�����!>��A����Wf��X��d[�~!�c���vm}ii���p�%���Ka����b4�-.���B@! ���U�8]����+�k��b�I�R��/v��`V��;����\@z���v��k�����WK�k�#���`*��:�j���w��TK�W1�+p������`�0#�����&������"���?Lw�����|cM�FZ�,�s���1#�E���b��������R���R���q����~A�O����a_A� ��j���_lmq��Lj�wpW�� T�M>��*-6�z����>zt������@��`�c��Gi����������� ��Ru���ln������YgnG]#��a��eu������L�+HgJ'������,*m��U���4��Nn��n��������c�Y���d�b��8S��N�
o{*v#6����*�U6���x�3/;�9���h�#��m�P%�?�ig���,�0���!�
��A0��,Nu����
�������LC��1��5G������ZNh.���\}&s��|�&t�
�3���FV�gum�;6���JOe��t.�ou��B@! ��B��	�S�����T��O���
��������p���9�3q:X���e[ZX}�uM�e��I������0z\{�M���y��"N�����uM������s���;=��.���e��z]=�n��'gPW_���g'PO���Nv����u�c<�Y0=���
d���M D��@#��D��\��3��T��8����]�&�?od�v#=S''0��tn�W�#f�3�a���Y��;�s
���%#w�A�^�! �C��5SPq�(����"F�	X�0Fb�0Z	(�eh�/j#2x�k|5���N
���_���X}����#������������k�M@ �c"3�+z�@x�f���0����nE��.�2l��$Z�b�:D��m'M�|���R>�@)-�x�=(j6����C����*���7�y�}$5��
�
8k8n��������,����C'�E
��;��B@! ��B@!�@x��7���o���;Lg����N1���w��Oy:�^���/��	� ����6/�?�����72B������O����~:��X���Ax�������I�����Q������8�%��'F�A��(�=�~
$! ��B@! 2���h���������l�hoL u ;���&�R���0���8cmE$����8�uM��y�J������"��.���'���c���� t��tH1�U������2���c
�H;S�)F�9��
����KXf �^���o��Rkoz�C��7�U{����:-����fB@! ��B@!�p�[�3������$���r������
:V����
�����k����P�>ah���Zu������Z
�������=>4a�Y�p�x��������a?=Cv����r�_�������{���� aOW��'$ �"�B@! ��B@��AX��E�ma�^3E�qS��11���L�"�,h=�I�]� ���qE&��^���" B�����b���0�e�s�������1�fT_��4�A�3�������+�����#���k�&�z��n���X�wb�zm�_
�+���j�%�i|5;���l���_��>�U��B@! ��B@! �@+e�$���a�Q�������;���)��
%!��@csq�I�K"�W���o|�V�3f�h]{���*�������Pu�����t��'�`<����|����o�:O��z��[O��e�>G��C�4������a���6g��w���������Nt��i���j���'m�����p&>(�O��}cmF���m{v��8���	�KU��m��_Mi�����J��>�B�@h���Mv��vH11��:��-��9��+l5���WnAw����~�pj�����Rt�N�����f{��� �|�)��/D��*��B@! ��B�$ �<����vT����V�0����<��I@@�*%w<
G��KKbs�?�����{8/�������������?xFa�J�B��t%_���4-u�=�'^N�j"���-j&�#uD-��}�Sw�=��	�)�8Q��nF[��B����'��h���h��-����9���z��B"������R���3.$i�{�OotUaC���9x��B��%� F����:&_B�������}YX6�
������E�Ah��0��F�+�	E�zc�e! ��B@! �������������������K6���G8��1g�D�(-��f�����[ 4������(
������ ���8�s���:�$������a��_������u��X}�v6L����%����3��_�6��N�7Q�]��v����)����j�)�����	y���/�Z�8�a��P4zt�I�(��%����{�}5a���! ��B@! ��B@! ��B@! �N j������C�'c8��Z^�:|(�������q���K�

JbT&��Gih������9)hZQwDsN���i���Q{�q�7��k��g����Fo )���-��aI7���ca�y(Z���8��b'��\�3���h���GI8e�Q�S��r~[=��u����p�N��B@! ��B@D���!�\��4k�����������?E-98�.s�&%;������8��c/��6kq�NMj��n��w�9��c��g��vm����v�@���6���*]	!�]��Rl_�s3�S�������k�']���S4��A���p�~������$��D���������b�������������z���x'���(�9���,����1���q���l	��$.��X���	�J�Q��nqSHY�������iQ��}c#U��I����Q�c�hF�A�V��z��P���A$e�F��	���1i��Cy=�q�RS������s
�I?�9�������Bs�M���{8��SY�q���3ec��=-�/A��{8��t7��S��4T^� IDAT����<��c�H�B@! ��B@! b�@z�"���Bkm���;p~����87���%��"�{�[�������]��-�������oB�~M�[O���c��'���D �.@9ND���P���_yB�K���5��LR�5��n4[�Oi�kP�;����W t�jR"U�uU��t��\M����.��L���c��]��7~�	�q5h����u/����FY��C���=��7:��@�������Q���~��Xb��_���iRq��v�,�zc���UW1�Ix��,fg��������)�A2���DNjQ�+]
! ��B@!0p�YH;8�U ��}e�K10A'��V��Tv]j$'"��l<���%gfP>I�!����[������w��K��&�.�M��� ]�"a,�� ������G���N���d���'U?�����@�?�2�<*{$F	8��i��8k���M�����0Z������-�`��5q�|%���A��������
}�j�'��T��r�4M%�'�E������[��a.��]9��#��0���<3M��O.���5��zEt��3! ��B@! �@�P�T�K�l��
�.���}����W�\�L��&����N�g���K�����[Q%\8��D ����� "��"HB�K��n%�W~����5�����U@N��<���pBW���%�Q��H����Gb������e�[��
�H��O�0ZK�J�����Bm�?����6��v"L1*��v��'"��"�JJ�	! ��B@! ������}=�����b4f�C���m�)a���"��N"�@������ �Aa/l7B! ��B@�@@�>XD���1]'	E �o[]��VL������FVZ(F��I��2���5�����|�N/}h���d`��i<x��d�������<`������a_�~\G�c}�M>3��xie+�������y�<]�A�6��+
���F�w�3S�<���-&V����[����1I�� ���4�~U��^���p�os�s��_;�_��GY��4&��/mb������������Z������&16����~�J{�6��S����9��=O��s��d~v{r�1����<�V�u��2����H�[g���L����B@! ��B@��Y ��ZWe3�������:�I7r��I,�������&V����1cdK�(����p#����Ub���w-SF�s��	�J��
�b�fg�g��W�g��,��csP����-l�uB���c�Y<AOjw���<+�Y���ebE��3��I,���XT����%E6����p��b04����0�D��:&|d��p_������:y��f��Q��y����<a2�.��I����B7�b4
P�K!�M"��a77Q/&ax�����*�~��D�_���es��xj^�V���=��&��7��~;������]�QK�Q�����[���1N@-�
3���5��5������b�N�W�� �E�Gs�]�E�T�|F\�`�L���$aOK��K�����7�_���}
��q��gB{��l����������.�b��	d��@�?HGq��C0q����v�]8s�V����x�B����~|�'+�y�O�LK��Q����� �z�x���Z�jTu<lL+Oal��
�X��I����W����1u�c���Cof��C�dy�\������96\�i7D�P�H;! ��B@! �@h/~��^
\��+�W���l(svz{�9�����������"P/z_����{�/��UjX�I���jX�V���rF���4�J����;��x
��s�p���,�S����q|��Kf�1�0EKA��+���9���L�cp�)F��7�6���������@�9�U`���z:*��"y�0����9��+�|z�������������nUj�A����;Q5�����z�?��L���Q�3I���~?����[�9k�i\����@[
�����|?���	�W�����}z~��q����j���+x�x��q�=C���li������s�|�J)���1��t�=7�d��du��[5<������1�k�F0eeB%����;^�A�;���:�����y����#f;���0G_��d��;��#4Uf^^Z�3U��|&�������\��I���?��
�����rybZg��Og?v����!�;g�w��>~�����0�g�����G ;w�j�����&��_�!� ����k�x����(`���O�<���/f1�}�hg��J���|4��n��Jc��u6R99����DC#���B@! ��}N \!)_���\-�J��f}l�-������8
�@���o��*>��k���L-���_����������(~9X�qq�
[���C��KRxx��T�s�'�l`�Vw_��'<���+Ob��fd(�k����!�����p���.�nCI1��������,�Hd��$���*�0�BSq�����������B q�Aa�8E�A�:��0���uL�Ys~��/�k/8u��g5���
6��c��>��oXx��r>�=��n�Z����j^�*����=m�<�*t�m�=u�{��{��j���F������5����K7�O'�w�xZw���/���Cl|�����Z��I�d�+U����s��3���4���NA�Q>~m���~���Sxnq���3G9F! ��B@!�?	�� |dv.���x+��VK��L6Lh�`��/���=�5����Q}��5_[)��Sos����y��4��o�����l^_��b]����KC���fR�a|��dY
��I���1���B�+�W��O�����.����RQaa�Q+kj�b��;&���@xf��\Jk��u�����}]"��'ZCq���G����-���s��q���� <�~�'�8�['q����������{U��;������;/�=7���+�����������_��EI_=B�Ic��R�Qw����y�9m��W����B���	��j@��a�cd��qL����g���Q;�zIZ	! ��B@! ��p��B�j�u�<�D�B������Mk>?�����D+���,�*�G��)��,�G=k��@liq�����]�L+����jY\�9��{,��)z�������4����0tV��R��M\��	�����8���"����� ��PZ+MGC�Ax�#Aq?2�W[
�\(�k����j�J�����j�4�`������� L���
��y�?8��tn�[��a.i�~8�"����@�N������z���S�2�Wj
�F����� l|����������QJ���3��+V�3M:B@! ��B@�-���WPv����Z����&���I��L#&i0����|�8WM��8.l��?;�'0��X�G�������y���,���uL]oa��k`B��1���4�H�'b�-X �1j��������1���$��T���D ��2���G �U��J� ��"o�W{�>�!u�q�����*�&9�c��G �� �g�z�G�;� |��T,��P�p�	���� �E��j����������K1�����'�������'��Jdv��E
��b]g����_��4GO:M+�|!�9!�N���2��D��kMk=����!�+��R�z���������$�@pM�3�������>��if���r�B@! ��B f	������.-�Ma}"kf%1����W7Q42�5����zS��� ��c\�#�
*��l�^��/�X��df���-�_�Gp�p��\��op�5���D�"����8#4���@A����!q�a7S/.��@��A��������� ���S�pv|W��w��}��%h�x��B��;:9)k��;j���4V�����N��6��?X���/e1��S���R���{���#��Y/K����Ll�'r�y�������]��/_�dN��&���
��s��s�2-�3�C��x��:�/Kh�����rH
�����Z���T�Y����aFJ�C���\M��y�tCl�c���@�}�^����&��t0�*3//���M:x&��#�������f������{����K�:�_Wk�@h���X��6����H���7���Z���',-��B@! ����@o:+�3a����e��H�����lg����0�!/�]���8�,��41�?�0.����"����U���c�9��=DG��ah1��\fa�6�EC5���W �{I��jIu]O;Uvi���Z(:-�
����vT2c��G�Jg����������,��B����f&����I���Gj���!��-0#��=��}�S�;�11��/
�+<a��O��d
O��w����%]8{�����g����y�%~O��������'��F�a0a�9?;�O�����x���G�A��+����8c�A��4�_�`�rK��6+]KU��@�-<��r���4���`���y������1�k�����FO��s��?�������^����D��L����%���JE���c:�za���7n<i^./�lp_�.?^Z�]�x���e�-�������$<�����y&Px��<��LI1�:K#! ��B@! �#��t����W��_ZC��)�6��v-���g�Gf
��`�$��Z�f������"�
��$�e#�/�5����S�>��nM��ldK��9z�T���/:�,�g-O�sh���3�0(���H���a,�� ��A���A�?Nq��NR��U T��Cy=�q}[����N��������^�c��3'���zf?I;�������wX��������g'0���>�@��Q�����'�9C��8�X �8GO�s�IaZ]-��c���7�z[��	�z���������zj�+�����f2�����e���O��LE
/���9^�e���9Sx`���?l�.�=�{A���a��Wjyf��C�:&�Hb��I�=Z`n���&���x����O���20srsoN����g*-��B@! ����@o:�l�,,����";)z��L��q:v}R�B?'\����Tv��g��6F��M�^S:�l����C�T�:�0����F�������>����L�v��c��x�;3�)����ZX���r�
���}�)�x��%���9Yof��f�x�Nb�YR�,�n��)F�q��o��}FH
��^�N�gR��m��a����9��a��A�Sm�b�,�=�Wi:�u�\7���z.��jG�{,��MT%o$'��b�����j��q��iK1��[���1N@-�
3���5��T�{A�MS	�	w�<�������Bs��(�`)F����@�/��MA�0�h����E�}=�o�0���$�.��2C��B@! ��B���,���ku�Nd�Z�0������� �����H����_[����r-��w�����83Gbu���Oo���"{�d���)��|V��4,�%9���������������eZ���L$��'O�^����.�'b?g����>��b4v?��A��{��R���7���|�0�`��#���#?"�vB@! ��B@�*����:�h�%a@��� ����oq���� �@`� l�"��q���0�u�
��L_G��}]��4���w��9{6��h��8��R4��� �	E�Cs�]�E�T�|F\�`�L����� ��/�����*�=��y5�;��o�;�:V�~�{�B�z}�����S��d��C�d���.��B@! ���#p�8[�k��.�5�"a@Z�;{~��C�����x�@����9c?��|�;�_��4��������� ��9������������������p�tW���}�G��O�1� l���O�0��v���v��Kb����2(���Ay���B@! ��B 2�A�G�ad��h�b��'������"�K�B@�A@�m��A������ ~�0X.T_�����a��3Q�tAj&z����k���j�B�!� 4����z���a�e����<���5o:��?���ux�&���B@! ��B@�V�F#Y�.Fkm����a�(�V	!!��f�54m�?��w��Kfa��������A���P�_�9���[
���r����{|�������kh����+��������ML/?���T3d�s��7vW����SEQw7��8Q5�bY�{��?v���p����3,6��d<�Tk
��uo�G]@�7�������"�_�#b��
�x
mR�w��������u�����x�L4�����	�O �wG�B�����[a� �2_�&���<j5�/���	��3������+��{R�&bOK�������}a����������N\zB@! ��B@!pH�����q��s�v��Y������L5�����u+�m��}X���w��^�0������ ��5���@%`���������u����c�����>G��YG�L�R�Eq��
������@��I���IW���{"8z�n�����������oc<�;!����b.�R��%�J�u��q?��A5��F
����g_�&N�A�z� �~�i�&�Y�p�X�t�@U�9%���b:�G�������.��T���������KolzC! ��B@! E�@|�%$'�����6���}hr�q�y��n�������@�0t��
#�b��)��O�=e4��,@s����.R��/}��6a������A����?��K��N���������X�����������i����g���+�T�p$N��u?����B��0%*��{��K 4�w#�����m~�5�u�L�@�1��+~���q!�������L���Q��8)Z	#/ ���1�����<���0lzr�B@! ��B@! ��B@! ��Q'5�P�<Fc�*T%���4�c%8����7
����a�����d�(�(�.��u?�tYY�K����&}%��8�s���}��/F-�����c�ut��[�������tZ��b��HK�;8-u��%��m�.Z��	)���������)��F��������9��~���y:��P�SQ6|�z�(��W�#M:B@! ��B@!�_(
J�`�b������������q�{-�qX<5��%\t	��}!g�����C��@?>

Jz�}��B�q�8�w6���Z��a+��j�/���S���f���7����).��s�����D@�jq^8	�����D��A�~=s�+�(����C]�7�<�K�����@��e�v,;�D��C���w9M����Z�.��A(�
e�PTG�x4:<5kt������)�t�{Z�L�rx���~k"Ji���������(�K�l�&.�}_�����7�I2��s1�_?A����'���;�����H}�S���[�%5�'�~��f'��aH9�s��B,kv��z#Y��#.���L
��I�Q��0�����(:m;���s/��[��B�Z�h��B@! ��B@! �9�;`���������y%��ck:��'&�G��1�,��\K��>�^��=��{���<��
�
����3h���h ��>�w�L�c�H��>4[v���2�=#`���1����������PU�&�����L�f/�(9:�j��=��z�=F��~B�UW�%���aH���*�M�ql��?@=�=��7:��_ <�]�$� IDAT��c�^Gs��v��	�b��%s9�#�%y�mm���4��H��;C�5d����{h��'����Xk#jT~_���T'��
��/aq2���]! ��B@�E�����n=��9f���S8U�r���7�NaVf��5��5E�an_���v�c�Vd���L&��u�6����K";����Q�q��]5�����@���C-���D�������Q���]`�|\��������<�3�-���yL��#���J	��FC��7�O�������GC�!m�������{a/:�=��kH�����
���h^�Tg���'��������Rjjv���%�BV����5^a\����y(�����&��0E��h;m������naq����qh�,��S���
�:SK�'���\l{��A��I��z���o*���wO� <�������z6������|sF~�DV���8��2;E���G����qs��+���4���=�����������If�3���ETK�B@! ��B@t�����h���������v�&jau��Qx� >z0�,���+z��������"[�e 
���Sp<��9J7%��[Q�}�m|��z~��C����m����R�����/78u��@�v���h[��|&��1N^�Us+)�
x��J��
�,\����������J���-1N�^�;����W�f��h��|���k��oK3�����L���_�>����1��+���������Id������io��T�������;-�o�5epT��#�~Qw�X �����o:�@ ���=�=[E �X��"F�t#��B@! �@$	X��v�1E i[uaX��~� �0�e�b##
W������z�a��� ���>~u���	�x]�;F�M���?{g'Eu����z�}�VYDAT�I�h>������h�b��[��5(qE
�k�b���� �F��
paff�Y{z���W��=��t�3p�~t�:�9�9U=]w��s%�NzH�A��]�ap[���S:�9��'{B*��3w��n����h���0,:�N�������5'�n�KE���  ��  ���N��:�=���\���A8�B�q��Bv^�A���?���@�n������r���A�����o�]?3�>'�H|`�w<����_��������Ah�A�����<:o�u�e�IY�5���2o����]}}��_<��u6k0�)sE�����45�_�S���.�D/�3�@`	D���Rv�:��u�{5��P��8;K��l�R�7F�)�v��������W��`h>�����YR"}���J}�+Q�$��h��g���%q_���i�Wz4������Q��5��zt�t��a.��d.42���t�S��7e���Su��U����f�D�Y\m|��4k�y.���1=W�W���6k������6b3��[��92���N�W�[��c}[���]�T��s^�2s����+1�~p�^A���[����1��_D"xA@A@A@8`2r>YJ��{y�?u����'q��J8�������Z�_��D��{7�������)=\L8�c�*]�����q�-{��������"s0��f]���� ��r��J��6��Be�����^�k�x�z?f�_V�e�fr���m���G���GWy�o�8
�%�cmd���I��<��|
�������)/�*/�����%w���)�>u�Q��UT?�u�>��3�lf�n���:���2�Q�_���y���y�U�yf������>��LSr�Wqb$B�vh����L��|S�%��l������K0{
=��7cY����`9�J3�6���D���q����9��o���Y��c(E)��U�O�PZ����fp��T���g:�����Z���j3/�,d��]\l��#["�P�q]��O����G�a�9��������)�u�}��_����PZ�Uj��U����R���iJ���X}t���_��*���X���pf�=,a� �[����o�������k��8�h��� M��F
��f=����At���mV
��x7}J���R3 \�p�?�O�)���U"��@8��A�����Ah�}3E
���m0����h�o���a�O���P����/�qn�F�L�8x�P��@������mQR�Pg�ejlm3m]$FL:e��G#�W��WT��&9�b��k(��
7������k�e+���(4��y�^�J-�p��9n�-��:Am�����3�R_��e�����d�c���k+*���(j��Ux3����b��>�I�v��  ��  �� p�H�A�Lq� {�������r�����%�ZQ�%��7�Bu�@�L����"3b>�>��)�Z��$6��}\~�^������[q�w�������k�S���~X(�{�ZV�F
B�t�$X�7l"aH Lp�<N��h|������;:p����j��(���c	v�����#�:�����>Jk-5�������^W3��d���E;��ar���qmd~�����P;0,����uS���~��jx���e��,�����%�������Y�� &cQ���8z�Bi��d"��Q�b���:e
����r)F��zs�u�a�����>v�x��0\���>�}=����AN1��r~��L1��5��^0�����:���+:U���h���y�p�Y�o>M��&���B]�����P���N3��c
*wk2�{��2wP��L �mnV�v���\�G
��q�i:�x4�l������,�q�a�S����yU~��3._����3�:L*�`|��]9���u�W
!P2��a����
:c����T��kb���s��9Of�����������u~X���8�@�	��z�����DQ��N,���2w��L����j��N���7�n��v�FVXh6���b��re���d����U����>�F�:�+
������	���%~��8��8@A@A@'�;����^���B�e�h����>��k���W���YaEW_U���E�;��)CBN;_��%�n��Nq6P<�'7_���Cd���Z^�%�3��#y�t�E�Il~����\�G�_�����<��/?��]x~B���p52GX�����\����;"���n��A�����)��Rr�k$
�zT.��|��O%�|�#^�B����P�LGO(N�Xs��y�]w���
;��s�s��dp$�p
3�y�q�i,W�EPc���%�%���"���E��Q*U����������#�t��W0/X�l=�5�l��\����4X��j�#�ip��'wZ�����>r6�Eb���60!��GO^R���2	�b��;���e���r���R3��b�)F5��8Ws���a��@��0P�����^�Z=��Z3�x��5�a7h+��`����0���� �B��3�Ah|�������A8��OR8������(<�7HR�A����KS8g����� �/��0$D�]=~���ZO�4��,z��!!��Yg�:���\��'�r\*IS�t�SQ&��x���tn���X�4��n�T)FC�1[^a��%}�5�(�5�>�kh
%��K����*���^,3��E��� ?m��+_����E
7�d;Hw�-�Ry�=f@�����ouS�]i=`:*Oayvra�e��{,2f8���Yy!19�Q�G��  ��  �� �;�s���?|f3W��������$�Z�
g<�r���#N������_��1NA������@>�I�B&��>��I����/�r����?�B5��p*R�a~���w�������jI�$j
4�����mR��EK�Q'�D�U��������u�~f����/xb�0�o}��S
�v���dF�d	�J~G���P�=Xcs���0�W����ju?�T�,�����\�R^)�T;\V�B��HA ����G�	W����t���i�9������4�����o�SG�V���)F[����k��3�PC,���,��T�U�	���R���FjR`{$�k6�r�q�H&��5L����3�3�d{��5L`7i��a��-5]p�&���%[m�G���7��k���y�� �:�A�1zWg�=!�]�A+�����%M�h;�.r=�G��v�lQ�CqTj2����5U
�����73�]r����,�N����K�������p*��~��r��~��<V�r�?�`�=���0���oK�*K�wH�rILrI�e[�.�A@A@���@��D!�Z��3n��3R���@H|}�Lb�iH������n<����xq/�@�q����������q��^�� ���	dV
�T�2"~�,
6p�����q�\@�3��4�@��ti	"�~`&'M������Kq�K��W[b�T ��w�e�F���JxX���95�� M��Ct��������e'c�����3e��P�N_�4�u�&�A��V���U83[������u�jv��FO���h�}�tV
B��o�j�����r������	��d
� TpkU
7k��r��q�!<!�N#��bK�T��g�f�U#���z&NT�0�0����:��:k:+�����s��^��c/'�7A@A@A@���� ;�<�L_&������p���A(BR� ���A=@-��n��>_� �T��K��[m�?!�@��������7�IF:L#=h"1P���m��Is�XV(B2�����D 4��U��~�������HY����<I�����Mt
�t]�� ���.�>a
�.��$/���:q%��5�!i��N�A�����pvA�zO���8��d)F����VS[bO����$��L�la8���2FR��+\+��)F;�~b{���W�]��Hm�\��N#�z���4Q�)F�2Od��O�N�a&K�6�������&�p����tr���"��K���i�4}lT�:���\��#��y��@A@A@A@8���A����[����H����Wp�}��2-��w���-���y���j�.z�� �)la&��R�z�������s�	��n��>�\�a���@���|u,H7�h�4�����9z��Vj�%�R���n[�C��f������ii�#���J�bt�RH��e��k������q�j���%�h
�h�u�L����;*��� � m%Ej���dC���v�f��]��pv�5:�#��VW8M�p��R5'Rx��HR�8}�1�}����f��]��x�Ic�jK�i	o�)2��9�*m��U�>���p<�����-��^(3>�6^f['q
���*�n�����	������G��f�I�
7�i��[���MA�����
�e�j��T���5�n�����T5~Z���-�H��8�Z�T���u��������T~P�{(��/4�Dl����������\���>�[�5��V����o�{h��
��=M:e��5F������znl����j���������{Q����{l����.��  ��  ��aG }�������#���m�A>\T�o�m���y��W��}�v�^J~4���R�����k/���e��A �m������}Xpy!#
o�u�����5|���������������b�{x�W������|E&t�X�:{+����f�{1�Wh�������O,a����i�28��s*<'9������_� !q������%��1\XN�a�����!d�I�u[0q
B����U�LGO(N+��F���������)R���<X���LG3mi$������dq�,[��?u�gE1�6��r������Rt���������	W>�$�r�-������y��x�T�A���%�g���M��0'�i��U������g��:{r�U�@�E�s]�������L9�"����R3��� m�T=�*�p�����S=,�����R/1��<GOs���1jI.61��!�������J������vD�Qv��q���a�A�UW���K83[g� ��B��WT���/f���M 4D������I�+��:�"WK����im�D5[��>J�������	���+kU^�bf�HRcD4 $��T'������������x�����2c��G��7��O)\v�5~�?�rJ�
����a7���;���D TQo��uH
��6�h$��  ��  �
���Ij�
/���z1:b����-;x`S��N�W����.�<\�o�c:u��o�]O �46��}\~�^�%�@��_�����R�-�a���J1zW+)F�����������@r�M
���D�l�Oj�[���ZS(K�:/\�/,f��%99���B���1,���2\[��y�hJ�~^��.A��T�Nq�LqF��T vh�L��L0�H-���g����"����(�0"D��]I1��V���D�%7/9"�m��a�������>�zt�A��
at�K��\wzu6k0�%���Q� ?h�����S���Zn�Q(Yb�[�B����Wa\�N��
e&�C��}����&u���:���K�C��,���%J����	��c����jE��Y2����{U���h�<��:O5����q�[��,�I�8g	�O*1��1VP���e9
��J�?��>���\w@�
#fk_��#36�AZ���Q���n[3xnO��wa��,1>K����0}��-��x���,R�9e.�����LL{���  ��  ��  ��w�������:^��.��Y����sdT.>���g����~������{p�Y��Y9��I 4�)~����'�m���NF��B������[����)�!��^�G-�?k`]&�	�2��&wD~�v��O7[�A�`�d�T�(A�R��/�����B�������!��j�H.~�d3SqY�=S9�H�������
G2��n�)n.����� ��`/j*���i�8>�0������j��[���nnu���,P�yQ��q#)�)gs���<�I���w"���/4��:�����0���3�h���V��,�vX�g(���pN*������)J7(��� Z 4]�>f�������VY�Z=��R��,T}�3�.N�����{�����M�u���6�Cd�A��*[�����7n~C��_Y���5Sq��)>�������A�p4��z�w?F�6
���AH�0�=~m�64�^\����	���K�r����	x�x?�mv�Ir�Y��5;�]���i��U�C9QF���?�t������a����X�2_�o���P��!��y�^�J������j���]G
!�i�>��'A@A@��F ����I��y�������MG����������c��='N1��#v���	��xJ"��M $�������k(�F���Z?�&�{���^H7�������#I��o�������(��1��$�ziA��������:���/�3z��.�O����:c���}*?i�����<�-����fGo5�������n�xu1B �b"�A@A@��$ ���{��}�� ��k'�t�Mci��sU�;]�����.L7p��& ����v��	af�$�]�A��c�M���v��K�7�����!�K%8e����l���uF1G���{���A�/y������B <h�%A@A@�O�P#)9������i�h6������@�Z��������)��-����g6c��Z�\�3R�1���Y�% ���)�����A����.� ,�j\���d�f3k����$�����7�\��S���,�?������__�X�B <�TLGA@A@R8�B���O@8;�q{F8�Lx}�����Q������}J6������.������_��<C� �lu:�m�� IDAT] 4��7f���x�i�j�������@$��sTj���y��0��OS� \�s�D`\����'�AS��(�N�p|�e��=��������m|�����C��-\=	j^�o�V$������~��o!����VL�A@A@A@�8������0u��4�y_P���w<��Y��9~0��/B��r����S�*���n��v��[������_��>� �@�!�p���hgLC����:J~a���=���'����~2�=��c;����a��FF���xz��C��`3��o����^��a��?�+zNq���Y	��� �E�5ku���b�[�S��?&P����~A^�"�������o�*J_ �w���`�;k}E���  ��  ��  ��!O@{"��CY��)�����8��-&�v}������	�=��p����
��W����,���=&��>�|h�����G
��A!�~{$��F���Y�P:�8����W�:��c����~��l\p�e^qY����h���Z����M���V]���06�u��/�;��f�9���m/D3�xm�64�����Sd�!Kp/��������5d��'��|<��M�SF�q����$9��|��
4,^M��������A��y�����oBq�H{��E]bC� A@A@A@�nE`����_��y�g���s��4�^J��7P�{��tD��K��s�~LnQ!���x�~	�__�S' h	�{*�~UG�_-����?�c�i�����>_�����%	�wO ���p�0��Kq���H�wu�h�OA@�HN�I���k�ms�u�� �^������ ��EHg\i����������_�?����pH������%]/-�1��@(y�#}��7k�55�3Dq��6�w�w^�^�U��LL�����G��  ��  ��  ��  ��  ��  �@g��P�5�SMP�"��%�/������,��5l-�~�$gRn	�j:�
������Q��P��#O�xiX�>��F}z�w�x����kF�V��oG����*�|���u3��5;�*$��������-��[�."��h-G�4 ��h�Y[� ������I*�\�,�w���*rr/���A�����C������������^k��  ��  ��  �� p���K�"��^_������\z)������D��N�Cl���td��'�~<���a�����C���7?��K�/�A�+�G�3����(���(�	�9E])�6�"�<�b����oB�tm�O�y��^��s������uM��>D�
��:i��5������U��������><�X�W�\$��qw��NUo#zy%��wi���9��A��������nB�7���9�rGBw���J{�>�%4U�L�5��7r�{��������^n���p-*#�K_�'��>�#p��-�MA?i$�e��k���s
�4�T���V�As������{7�3�t�~T�*�+z����?�!y�7�-{y�	P8��	�W��z	�{�w�=_�PA@A@A@�	(��
�'���~Ep�hj����$�M ����:�=O����?F���i+
��  ��~M��������#����?D�C�5G�=6[,{W'���OB����ap�s����~0e��������~�m�����Go�
9���@�P�_�'&w���U��V��-�g?���e����3\�N�a�q�]zE���@X_T�G���������
���~����mo�~�a��u���\>�.;��O��Jy�{x�����rUg���;�� �!�����,�3�����2$ ���  ��  ��~���{T�
j����-1H4o��frX9�E�[EH�����^�[�U�����n���y|5���n�����5��%����c��dI������w������1<O��rQ��$�O|��1���X�������-�
����<����;�D��iU�R����zN�b�c���Ck����3�D�_��CS p�'����R��_�\���A�a��q��P���m'I�,��C���HW�6a�Q�CL��c&����hHRw?�[�#��j��_���^^AM��hj�	�U|"�
%�6�G�����6��#�!gH
��)9�2^��W���"����G�e�A���T�����[��n�m2{�Oi��_�-��d,ZY�� t��`����t�F������g����:^Wl����LY��8����a3�;#�]Y �b�K��rrF���[r�@6\Q������e�(���@�~��A@A@��M@>r�~��7������<�poe�?0���c��>��%�������Ky��@�Y�Q��(�?t/K�B�Hf�����x� 
+�so��F���B��{-f�S�]e�D]+�*&�w��������Z���p���X������R��:��/�������|/lJ�U�a��"J���|�_�?��Cp����d�:P��\�J6c�������rz#�J�����b���l�Id7'"BE������ �+��@8.� �L lI1*���U����D�(��  �� �����G�@��M)����@hw
��]'����_B���	����L�r�k0�%C8<_��!�b4�����B �j[:m�0>�(a���B0H8��0���� ��:�����Q�C����.���^#!v��'A@A@8@�@�p��A������3�v���@�U���a�U�V�H5V?�}��=��U6�A�C8E��������A(!�����n�m
]�A��c��{x��zV�R�/��'��������N/��%����55|�J��s�wK�9>�R{�����~��[g���}<�v
/l���/���;F���Wk�p������=����ZJ��T�h���v�������!=�������0�������{��%��P�f����qfG�[B��u�F���CK��������09;�kM��[��O��U,x���w��g\����<��Y�N%s��s���r%/$�AXW^���jX���r=�7����{[��}����F
�kJ9�6w��2��r�|�(�L���Q���S_2�S�e\�P�-��D���t13����Q��	k-�m`�;{x~��rs
�0��B�'|�{;�(f��s8Mj����}�}
<�t��������C�{������~���)��{2%A���k�N_�'Q5U��k9�����r��.��p?���DA@A@A�`�D |k��[[���;�Nd&����\�����l{-oK����������L$&u���^���3�9��_�����m�A�(}j�{�|W|������2c���}�;j���e��W��,���:
\
����H��P�~n^�au�^<�����y�l����
x�o{~@v��K�AX����Z��je���p)u�FU����L�d�Z�
=R.�*G0G���,e������9�,S���V�&���78J7��u��^�`�Z�&��,�(n�����2��
�-r���������0vC���A��<3\�D�P��Q��+����L�[��tG?n��i5�W�Y�y����,�~G!�"�'�=Y��,Gq�x����X������g�6�0H.f�2�k��r��*��9�u��R�Q����KUs���jy��7Y��=����;y��oN��E�Rv�F��B[j����^������T��;��5��XcLsJ�F)a���c�5�%;
�B�-.�x�.�MZ5��}�z��9]��ro.��de�m��[�*kF}u��������a��:OU@90���mG�H���t���%��<go;F���n��*am3,���K\��mR�����^���%}$.)��v�N�P���nX\o����}$��b/iAx�L��J���y/X�	
��Xh)�kw��p���O���-��i���x����-��Fg�>��?4/3����x����@^	!�@h� ����mD/�L��l�#bS�>�q0�kK�e���]��4�~��J�+z����?`8o@[�y�	Ph8��}��� F��P)������En	�K\����t���7��C��E������Dc����L���)Q������k��9�o_[�H�voh,?�SYe\e��b\k�'�@���(�O��)�6�����r�<S��E�r����\?���sS�XA��!��I��C��kOmdFm�������u>Z��)K<�=������+�Q
�����=~����9�(�����i�����H���������8"��bN�[<��q��FV_��+v���H����y�M����l%X�6���|DA@A@A��H[ �/S���ja6��u3�z�sB��_4rsE�dk����@cg����9���8�!��R�-�����ai~
{���:���b�\5�#5�J^�px�vP�5�Z�vG�k��(F l���F�$X���PV8�,�t7��Q{�.N\���|&��
[���'q�d�;	��V�b����7��@�n<�l
�(qO�L����Bb��5�1�eJ��V����z $`�_r?V8�FD�t��{��E�<i��X�mgr�&~l3���>^nb����yA�u3�����L L"��J%�����`�ia�4����lJ��b��t����Z������b4���P��D-;��wN���{�@������kc��^'11|��Z��/��v�����-�����U�	��1�`d���}�sO��h�1g�C L�'�i	�O&<�[���A�-R���D�P� 	�7�-{�[�A��j�W\�&�3�y��Qj���-[j��;��'��������?�Mc3�-����<L��1<�%�[!%��qq_.?��[�������J>:�(����P�m�`��=\>�8���F� [����F��_HiBa��5���U��_� ,_����5����^��4�
�T��g�1l�q�z!$�Q:��;�/e�+�t��c�$c��)_�
S�V��������P�
�������"���SW��;��3�A���K�����}Xpa	��
�T���?����{b��Cp�}�bb#�^vNv���w]1b]&
��+���c�>����r#������6�;��W��D�!.��7�;�[k����O|����,�����n&{;m���#E��@#sg���z��'}"{�W����3w	o��������{��)I_��'6�0��3�f�����B
����S�����4����L)jS�� A@A@A�KH_ tp��,���`���S�X���+w���B��$���3������e�1G�p��.F�7�t�����s�U2����!NF8��~��2DJ�����YVb�Lb�U�pT�s��Q�����]��:��9�a@���M\�
���U�:o}���/����.T�+����9��<@!����ry��&X�9QB#���������,9���`�Z��`���Fp��(,*8��`�R�(���9rm�cG����5\W;��y�5���%L�f3�x2�l��"�8�K�Mej������l� �(�!��^���v^f+\�-�/,�s���tK������c����1�o�����!����?gK��<��
�ng���e|}/?���L����Q�����~�o�� ,S�2:�a�c�G���^��M�=w��O����9��%�?U��*~h��0�:��{��_N�sT�X��@h��*�h������,�f�+C)sY��F���"��>���9���M�0M��C��a� ��KI����b4x{���ep�H�?ZO����GH�9��Y&���7C$��(���o+�x�}<�N�z$�9FZ��u^x�R�����m�+tN����w�56T���Kv���%�}��k���������B��K��Z�����A��L�k��������_�&7�'�%��%��:��R���?O�Ax���3$��	9��tG�e�A��F����n� ��?%Z<���~^�����]��Z����61(�b��Q\?����O����Jz��f�O�3o����":�8a&U�q5�q����kOndF�6[N����l���}Y�L1���� [K�h����&���������/�x���c�._S���9x���L�dIUy���de6w�<����3���-���M��IhC�I��=��Y�P�a�7��DK�b����Sx�B}+Q�;�=���QaHo����{�#p��h�}f��	���/�e���x�D�����l�����g�8:�l�m��8RA@A@8H��u�7����N#�����P���5^���Y�l^�V�S1���u�R��v
����1���nlW�O�f��6+�5�:��Zo���������c���,\��}�n��yx�vlND�<H[&n�T�W�I�@�FD����E�����1��Z�}s|Y�,E�JkL��g�5���:gO[�M����jY��g��)��1!�93���)T3�'�UNW<2�
���c��^���Q�[*',C�#�>5�0�7��Ef���(P��L������m�j�x6�9���bW8
/>g�te�34<���tF����������I���xC�����v���Hw�33�:����$A
����\�aj�5�=��6��eu��A�7�p"��q155������%�X���)Fc����9i,<U�,�)h���>����{�s������c���e����[b�1F����9�Kb�0(�i����C"f�e�
���i�Q���zxc���F��VZ���GI�s�5Z���s��5�����wAN)�l�/u�o�
!j��j5�b����v)�P���}X2�4��J*Z)(��2{
�7���e��<&����[��f�D@[�q��X).S��3m�.#�0�q��{�g-]��%�M����^��)G�Z[�tF
����l{��>���5"(g&&��Y���vG	��9��05�t���M{*U���'-)n=,��l��(w���A@A@!���-�E<�x�0�x`B��#f��6���K_
X��e�:>���t�%���.�"R�6[*F��m)�Xa���5�mUL
�dY_@�(�B �(�/6��Gf�M<�����Zx���E�T�%�a��KS LZ'�6�0�T"`�B�A*��t�	D��a�=�leV�T���5Jw�33���{5j0f1N��9��rVR�D��,�0���A�H LqMj�	��U:�}	�����[��W?�X7S�5
G����zAi�f�-��'�F/��� ��a�Ux�K��q�����
���0�����[��n�m2{�O%�`#X�-��d,ZY3��'����y9�:��A���T������nlb�F�88��!�01�|@����D�+�@8��3Na)��KM����f������
����G{�tX_^�Q�����4�\A@A@���+��>w.+�s�u��@^�X�P����Z��������!Z.+-�����R���$Q��0�cRq4�yfx�HW<2��8�0���d��L�A(�c�UB��Pz�:�)���2Z�qc�\M�F��q'
�F�^��[Z�u��X�Coy/;�ii3��B ���IB�����bD�]���;+k`e��G���.� qV��/����p���$n��\�����2E
�VB�_6��C����mB/���*��F��!����Sm�6_9������^8�h��b���.	|�?��V�(��@��W�8�pZ�'F <��������h�&_�p4r��WCp�3h����nn�����t:)F���kzq����R�&r&���ya�Ff~�+�3N ��^<�S��� IDAT(6���$Y��GR~^^��8���s�Ew�>a3���;Jl)Y#�'K1�����r���t�[)Fw��/�_�#l�?<~�Ci@a�������?�h���J���������O����������	R��R�63o��1)W-V*3��W��,|���.c�su\~�q�
''�4l�^A@A@���%�z��/����Y0��Q��[�<qZ6�Y�|~�]����y|5�� ��g��2b����Se��
��� ��N1������y|v�������J1�tN���ss�a��v��Uj�v���e��l	6Y�����Y��vw6��J
��~�b���J32�bLr�d(Uez�d����Gf�$Z�g�-
f:a(���?��qi+[z�����Z�3aN�9�JG~t���n�����E�0���M� \��gB�����u��K��y��
� �����qQ��_ ��y�bQq�$��������E��J�0�*>�h�����/�t��b�B4-���n�d��!�]�Jg�-5f�	�1������g�=
g��8�b4Ob������}�w+u�$������P�����������i����&���-��t��1��M �;[�_�ag����AV�����]k�aWs�C�����(5��l�R����<=1�-�m�����z�,��j���kKw1�=~��b	K�������y\}BGX�TV-����$������e�_=��5~$��U5���]����)���'���������)�
����20;4������
<]������i���� ��������l3M�o��W��d�;�TG�{j����l>�/�����^J��S@�����W{q/FZ!��A�����\?V������;�[{1��{S��E��R�M���"�P���O*��3���X{}��b���vy5#�����!�pN�i���k��e��{���(����o����R^�����BZ��}J�����s�6���T�ib�'��.e���<Pa;���:�X���2����qNn������a����  ��  ��@�a ��#�����p��G�����<�YE��Vy����c��{�B>:

A����#Ud,������p��\n���o<��d�7^.���gF�3�A/f���ML�Re��9��_a���M���&��~s����|������y�[9Lrk��i=��Y�xrcZ��DDU�R�Z���&�f(���B����_�2y�%����*eZ
�<���D���9G���~#�~6��{Y��g
��$�7|x9�������`������b���E��L0�O�L��eu7s4�)F
q���������O�����4�t�������������\�q�5�-�tB���f��z3����%��H�Job��H����%�j��e/+��2���F����z?�����k��������%�A�#�,[�c�V��z.2k4Z{N��&�Psf�Q�7�l�fi>��� �����#�����'�;��bO]�r�Mr����M��U2'XKY[R������2�7���?���9���RLP��`[�:���|�9���9���M�$�s8�iQ�8q�&H����]�e�@x:���p��Y�u�����:���;�J\m������/��01�H9!���P���a��w�P�;��@K��a�~x�����C5�%qc��HX���������l���u���&���
���2�{��@o#1���}^X\�s�v(�;�-�0��-��f������F� L� �\���Ah��W���y�>�:�([���V���dj�d"~T��g�n���9��&'I1j\��Ws���|sti���k��:L4�Mh�:a��)q1p��r�@h���W�q������m�=^c����RNn������DNQ���nc�O�QE,�q ��
t`B2��4���-��=f����o����d��jl�y��v�'�s7a�!}x��R�b���JEG��  ��  ]�@�aU�h����!6G\ �>h�t��<Z������^�/&����M �46��_4rsE�2-�y���p��*���u������G��W/�b@���K
�h�Q739���zKN�t�@h4i���F�$A�"�d"vP��zu#�e����)J �����G��t���U���$�2�q������@h��������B>^nb��i�f�����!4&���&@y�
��'��(����[y2�^eo�
��*���W1��3����8��5m-����z�	����!U�\�+�}���a���X����=���u&���!������0i:P,<�m)F
��~�sUe��&���G�j &z�B�E��5�)0�_��G����_#�pm�~`l=��X LU��@;����.� �6H��%����55��a��9����������|�`�;�mJ�9>/�i!� �>���k�y{��j=���W�����S:�D��m{���}���o�\�����nv9D	��T�����������Cz2���\r|>G[OSt)���^U����xu����~6���Os�������Y���c�;�x��F���q���L�v!{+���Jb�]c��j������G�Q�X_�������n��]c�n&�.f���8����3��F�F^���?J�Qa��ym�nZ���zco����}�>��yz�>^�Xg�w!&=�`\�DnH��=��N���<������W�/��'����
9:<������  ��  ]�@�!Y���x���g(�sq��n��ZY_l��5�yd����4���L��fF��]��%���S��s��7����qpA_7���x��F9����)��t�U�X�M����������%N��8K�yD��
�m�s����o�
��a���u'���f��Ar1�*�9�]|+@���V�<��������p��K�^\$�Z��LB#�v�knE���Nn2����J_������h�����U1K����<�\�V��b�c��'��Fg��f���(f-�����s�+���xR��e���HYL���H����K/���rp'hul���Sz�����gL��A�]e�V�|��e��yc��m�;���6�H���9�J����R���s���� 4z��5�	�f����T�9Jon���b�h[��aV���+��������i�@����9IYL��������	���Q���'���A���=(�3Y)����n�Ps� ���G�}����A����:�W��f(��_���$:
gG�����:�kBc�
g���|0�og���{��]:o��4�,���I\R�l���`�v�������Oh�s���Ah�
��x�V�������O��b���)F�kI����|��mx�]Q��M��$����DXq��>:��C5+���M�5v�HvC	�M�Q�?k�������`�����:����AX����^��j�E+kN��
h�"�=
[�A���+�Li���A@A@A@��Z���D��@��)"X���j{��2E1H����$�A(�m!� 4���y���������M�E)k���!y�/TW0V <s��?��'�?����5_G0��AXj� ������K8����`�-b�C�@��}��gK�
b��BJ�Y�I	��  ��  ���N@���h��3��}�LT�F9�2����0D�J �c�S��&����,T��p�A��W�E�e����uM�>�kHZ�����k���5�A���!', �c�A��9�L�L�~��! )(jg9�����Zt��7�-{Z83���4�h�z�$,�����T�������)� V#����.�r8sG���w�C�a�����&c�G�	#d�>��8���tB�KA@A@A�0%p��A���FIs'_��}��i#�%&��@J7����z��s��7
�-�rK�����C <h��@h��M<`�EM�dn�+���x��5;NH�Dl���V��n� ���9��t7���O���6O���	�F
���w����d��|'����A[O8���#Ny�GW#P��������A����q��n�]-N�  ��  ��  D��;��&c�	�uZ��u���(3z7k���J� ���y���@xp���k�4A
������n�tx��)F��'���a��	��?�p��%��'��A�[����-D���� �����7�����������'����cQ{�W�����Q4:������^c"5]����w9S'�wa8*i���6n������� lB��'��y�}[[N|.��  ��  ��  ��G����=��������#�	(��Nj�.���fhj��PF�����  F���t�q��E�t��p
��
���)h=�x�_�{"�Q��I��o{4�����\����02���D/��]R�v����To#zy%MM���n��^V�����	�������B�]N��(���R�c�z�T���l�1���	��
����k��V���?y��/�G��i�j���_���x��  ��  ��  ��  � P����"kW����_����wb��O�u�hz^5{��3���G~aU�����  -z��8�D��;$B}�D�T���X�n@@��]����0�(����Qn�
�*�?�o��v���~�I
�����CX h�����A�z������@��BR�h#.�r}(�N���P�/�?{o&Uu�������z��QTE"G� Q��\�&"��sD���F%z.����%�h��Ac$qhTf��nz����i�}�]CwUuUuU��~�y||�Z{�o�k�]��W�����)f|�3q[�CS
hza-����V�L��R�����1���.i��:�J��+q�}���ilKV1��/'����P�!���i�2g_D����B�����
���AkC�.��&��B@! ��B�x'��P�Gb9k�S��^�O��y��p,{����'���s~��B�Y��3gf��/�r!��/�����:��t! �����������~�����u\
�����N����@��-�������Ee���B��?��!^z1����?�/>��Y��������/�rr�'��z������+*��?��}��!�����(��� �����(����(����(����(�����~ ����|?����@����~y���G�R���^������-���s�����������k�4t�-��bn�c��_cj��RTi��j�8��4��Y���+Zw"��fL��*g��o��a�Ky����\����mP;�*��J��L�[�!���v��9x�Am�%o�D���0����~�F����'m��X>|o�8}�nt��b�F�r'|����c�3�S��B@! ��B@�p(
��2�b��c���hYexF\�����3tM������N@1c*=%gZ����
��-(�wR�)g!��&`���@���������l���s�w>�TS���#��3zV&������Su����gl
^�>p���D��v+N�l�:�l,e#���4����X��go��44����BJ�a����j����O�~���#����5�)<?�)�s�o#�Aw�;Lh��B�����{�a�[7Yq�9��t�sm������mT���'�6��Z���RekXO�G��C���E��_����ZT���������M���(����h���F<?�1��g������%���u��?�1������-hs�Cq���AHB@! ��B@! �s����������<\g����|�-=�g-��I�Sf��b�y��z�k�K!��#p�M��o�i�f�s'Q�H
�-���8���,��L\��m���8:�/C l����K����4~���v�9���uU���8L ��
�q:���q��~����*,;�Z�������L���G��_�y�Jp�����BSk5��q����4g������OIK�������h���=k3h����%�<�k\���o�0Z��i��b�9�!�<+���x�z�ZMe��K33=|����_�Q��"��:�q;��h���Q��B@! ��8����S�5������X]	�����|�G.�M�0��9��\c�\G���j�4�f,�>�v���t`�v�w6q����53���L�c)��	�y����QW����r��O`cM��������=����������a��#�}��>[:�	���}z��g����}�~�g���7b�.���}�+=�b����WPU��d*:��r=w�u���{,+�bw��*����]xrM?�����a����B����6/��E0:q�Z��U�p>Y�O�9���ys^��>������]�*���00��
���/j&���	������9��?����{v�������&������;z���N
�Q,X��^
�)^�Og���rSjTz��Q���&-=! ��B@!��@������_���;�������u,�i1'[k�"�yG7=��:�,ab��O�4�K�o���Vo*���g.E|38����q,�n�R�)�����z`o�����\�8�h�-���A1��S����+w�����������p
���I���w/*��>��0G@ ��i����rv��.Ugk���������#S8{���aB��^�Z����z���u�|4	�pD���\�y
��S������@h������X ��p�?�^�����H���E LfoI! ��B@! �4��'/%��!����w~z���#a�����"��������E �k�Gl<�@8��A2����^�q�B9���/�a��� �y��y���A(�P
���A��m��q�wj������m-����/��B@! �7����7�D����@������E <���z�##��,�A��8l�#!��U�B�� ��d<��Bq���C��S�q49�u�e��*]g�#L&f������9����RMc����T����JD��p��T�����0X1q������y�����{��H8V�|���7�����1��tjQkR�������A��>��I�p���o��s��SlQ5i&w�Y�5�r��������yb�bj�s����G8w���"F�f]�Ug�����Z�f6��c�y����H����U���S�n2�7�BJ��u���*k���J�]�xZ�Y��~���&�G�9
�(��L[j�6E����+3�&�7��01>G��4�����(
s�c�-n4�E�����=kN�E�`����y����tS+����l)|�JS! ��B@!�'����v[K����K��.>7����me��|�z/�m�������������������sg#�wboc��B���(�8�D�����|J�t��{m�����e��\f�\�v���^m���*s<3��Z3�c�������l��O=Tab���,�������un�/�c19�� �1����q��F��/f�O�����G����v��o9y�CU��M����l��R��P��R����H����^j-iL/*��2��6Vr�����>3#r���"���Yg=��q���m.6z{cpf6�Jr���|�+����'�zY[���W�R)���I^6s�2eB���L�9y����>Jm�(+��<���[�<R���VX��X���eY��^�dc
����y�@3o�y��3#���9�Vd�4�������9�����*���ls����XV`���� �k����CW������-������L��f3>�2V�Q��uM,������_�����,��aKS�T��*x��b����7/*
m��iG� IDAT��Fg`�Y��6/���Y�x�}h>���Y��?�vE���$=��#�lI�Kno7�8Xt(x���2�K��b��� ���N.��ngiC+�]�����>s2b�[�vU������Y��������o+.�~3���/��B��l��
�>-����M�c�����xcX>c�?��V�W%� ������%��[����<���l���gy��M�zW��jm{H���B�����a�B"�Ygp��K��Xn��>a �K:�O����b�z����l������������[���E��j���)aBhHw3?�����������~�&OJ�e��[��!�=��2���mb��}5,?hp�ri�~,V�h����x�����`HnN���dx2Xw��>�ag`� ���sC�����D��O��C(jt���{,������|����Qq�(�,����Ax�D��9XW)FC5��/n
B��q�Wcc�/���XPT��z�&3oX������N��K3w�UAAju�]&~���m����8�w��G�X��xt�Bb��Q�P7x�����V�T���W%��T.�����T�;��!p��y5���|d		�:����a2WI�FU�25�B+
�Y��D|�p������-�X#���q�z��{%�y���i&&F\�E�G=*�"�=�^%�v�5�����/epr�B@! ���AH�AX����h<��tD
���f<�`S�v&��k)K�6A�	��BUm�<���T����;�.�f�A�q�s2�?�T]XH�56r�/����nl�!&��a��R��i]J�-�������K:�@���8}���������2�y����E�X���(��b�!j"�����5,j��SYue���`���_TC+w�X%H�,(^4�������\F���b�@�-\�m}�����(-*���A!KgKm-U�cL �CX��2wG��Qg������6�v�.�@t�5�����L��)	���GwU���Bb�5)-(�����k.���������T�eU-1����}b���������^O�^�;�a��f4���v����������y.��Ef��Y����u��X������s�tz�eRB+���u���AD	�{j�p�7
�-��b�������"��+{����ho����Gpgn���j�������G�!����!����a-��q������'��K ��RDYAC|=N�|������i�x��2��������/��@�HLZ���������F
�c"���^�!�B)F��A�����(
w���h��K'�!����Iv��U��U��f3O�;uV�s��-fV]\!���d���)J���x5�bfy�����>x(���,4��R�Y�+,7��uc�m8�����rV�>�T�XL����TU��pSZ`��ci0�l��0cJ��`���N��D�Ha3�Gg����a�.���QB]c�W�K����\y��r�Oe�bfW����-f�3)lQU.����3��P�����)�����h��46�L|j1D�0�`�c3������l�;[�=��i�4����L6�x�����P�&�~O>���67{5�-��?�[�����c�p�z4��w_��up�&����/Y^�N! ��B@��.���\{O����kr�60����c�_k�-�]�u���z/��<�������/5��1��~��i�z�a|���������`,:U���Bcf�������xst�X�j��m�������\P�#�����/5�By!�~! &����%,k��7���O3K�,`ZQwwa�]��;��0��4r�g�N//����6���7%�����?/9����~�����a�t{Z����~�ip^>K��0��j#���an�����sCF�8t���������#Cbb�4�:����&��DI�M�klw:Y�JgyI�@�5���3'�B��N;3w7�����m<���<��*�0�lin��*'
��<��)�!�Bf����E���,b�!��.��R�������I���u�Z����)+�gJ�m�������%���u���KG+K�Af�o��6��piV�����mkaY���*�-�@{��67�h$}p�v)���l��n;s����=Me{K+O;����'�&�*��<k��J���V�������"4�m��;�b�5����1�����u:���&���+��Gf�Jr��a+���<�/�)3���
\���q+X^Tj=Nfnm`}fxl��y�K�A�;u������<����b��zysx{x�<-��YOeNo�����;T�s����MPo4K�A���.N���9����BF�]�����>�� l���
{����?/eB�Q��l`��=<�5�������Uc��d�������bS�����m>��CN�uBV`�=�o"|M�C������:D2�M%���-�@�����Y<8��i&�:X��������N,��l36T>����ka��3XR��dK+X<��K�m��Y��/�����
c��6R�0������ lO1ZNn�+������8G�d|�Op��`��C�9��?*����t��� ��Z,,�k�Y�SY@���uP�S:R$&C���<&���,f������y|c�J�����c�8:�<*o����4���"�i�(�ht����:��A������S�v�,�[ML��'��Jk���h���c��p�E�5���p�aw'C ���43�Fe
	]���agghovN?�\����_ ��u^�&�;\����T*aH �b1�xD���x���A���K���B@! ��B��@W���z�Y���������3d�;�8��A�������02���^7����gZ�����]��1����G��<;r'Umh����x,��3C�i���Z�}��}�Hc��L�����N���j[���&r��^��I	��D5���������'�=�7�li�x��N����zGo�=lWu��n9�a��L�?oM ����bv
��L�'n]?+v`�9*5e�l���������k)a���;�Xu������hc�j\�T�`
^�,���G�
�p@#B����Y>nR���q%��h���G�=���'�����Z���m>�u�RUQ��~7)1�o�!b�
��)���xQ(emT@�5��W����,X�?��Rb�xo�nZ�������(}����������	S�F�o
����= 0k1����y�b�8����{��F������B�55�VN���w#ms�Lz�e�B����y�0��=����;8y������t���z7'����r����u���t���N�w�u;j������7��!�:=�v�NK�Q7����00�y��)���"&N1��v4�i�8\
��h�����]��~�,?o(3
�P�)�Z_4��GY
���E�vH��
�Y4��5P��K1$����PQ���*�!b8�������17z�L�{q�"vv�%��������bd���=:���N���j�uc��!b��#������_Ja�����Y���X�t�����D��.������ie���(Ea���>��JiO��P��_r���B@! ��a�rF������8�\	��yG75�!�0����1�6�c�Q�#�
c~Z�����;>f"v����0�77|�B��c�!���72o�#fIOi�@ ��$��L\�0�;1���D��Jgy�������&��J	
�kG�G3#���������8�O�iMg|�����L��`��V`�G����4OW�^{��Fa�T*�����4����������]	R��h?,�j��R��@��?����S�gy~�oJa�g�������X���v����
�#tCf>o�=6����.�	\t1v����K���j���:�d=�����V�|��;��I��)������@XS����Q�?\0Ej�!��}VM�c��	����%5�!���Ax�����]3�W��>��z
�Z����h�MBCw�X"l��W�;R�ikM������!�u�D���@��3����&���t�/<���+
7%�0���&-��B@! ����A��x&���A���+J���(�H/��^Ci��n#}b��|������x�C}��������+e�B���*����zx������-.6z�4;��O��h�ami���Ce���K-�\����p�n����I��(��-
6����s��	�F�:�mm���a��M���jf|�V�XIOI L��dq�����E �����M ���A1��S���3j�_aG�����.��&l#�f�y�pBW�~��h`��Q��#��o�}pB0��?A7�C�w�������1,v�
���s���E����-c��(�p��(����6/��i�[�����\m;+izo7~����M�OV��,�JG
��E���0����s�k^��A�Y���6���l��-&FDK-�h�p~��`Gj�h(w�������[,f^	�>L���'8E�|����*���o��=~��W�&�'���)Fu�.��=>�P=�$O��|:�yU�k��5:��R������Xi\��O�����/�V�2�{�m��0�`*T�D Mlj{�s�������B@! ��B�I�+���7��������V�(v�Q/�<P�5F���G�7���?
����a��1�_�?������c �hy*�����)F�e��|�]�G�����O|�{��)9{c9�NOP������u�0��.�@���,��j�i�����b�p�������gK����&��:##4�R��9C�Pn����X���G�*Mh�M����������p��������_G�����{�1�U�H1+L����,h��5j6nP�+���o�_��������5O�C9G0�h�vK]5��\t:���<<,9��k��H����AYl;��^�0����{]�u�e��V�����>;b��b4��]��	]}$�� ��<�~!��+���*8'r�0�b����Ii1���P��=�����$���4�������pv��Tg+m�y�6MNZj?I��D�g/��?U��d*FQ�f�o=��� <���F�������fn4(#��N����d���*�������Y!����jUc��3�bfeP|��-�����T*&����/����u�t��^���������5�E��Z=2��_��n0������cQ����[�7��G ��x�-f��3b���:���Z�Lv�L,1����8��:�U�q��Z��hw�~�;�I����)F�Q#}�e^��&O�;�k��q�Oc�)��e���-��E���b�a����3�e��M���Y�����HU��bn�;���
�������(3�y9x�mW����0���8_W���0"��i��r�����HbO'�_w�[�B@! ��?T]	��m�=����B��nc���m���i��5^�����rc��Y|w=���}We3��	�>���Z���v~���F/����X���_�pAT=�T��p��97����4#���O[x�'����X����4���\����"��dLG����?�U{m�'P����P���"n������[Y���iE��2�mP _���l���e�����y�.���qq�i���S��`?�\�I���������QYP���F���������p���*k�����L��\���p�Dc��������E<Qb���/'�\�,/I���KX�0IV��)�
�ma:�
g�����lc�/�%E�2��X=<�M5����2$�������mke��f5�:R�z[��F�_J2o5��E�������
��Q����k{�h^V�;�OwB�RW�E��-.���,F�����&7UdRKl���E��Z�����_n�������7��e���;�(+)����Z�=.V��y��Em(�������E�tn��1��3�Fem-W��k\�TV���4n����\+���on��O�mlT��5��dIn��f	��!(o;�2S6K�sm��N����W5��\^9%���K
Cn�QC�;s�

y�_v`�5��,:h��������[x�-���0����N��Me����\��~��ia��z���y��
���u�F8����]
�����K��1��!,.�aS��i�o����&��p�!Y�����b�^�����a��k3�y�:��iu���^�=�����jl��Hk� �H�#����[8w����O����r�r��ju
��BP�����L��N*�����/^7�4�X��_�*�kp�7���_��#?��c�������XBrr�#1R�����j
&S���e$7��j%�^Sw��������^X���f�&3oX:����u��[,<�@�3���TC��<�/�{���Z{�v����G�@h|!��SY=MEa��e5��c�����[����,f���*�kl�K����K 4~�g�P�����^_O�����%�;|A7���]����4(zn��d��4D��Z�~�b�u�UF�:�kc��:�	�B{:T�1�Z�X��nm9I! ��B@��
ad���qCt�r��������~��9�Kx�M��_���7�����3	��+����A�*U�"(��b>�11��tv��
Z�B��vr�El��h�b�db�/KX:���tL��bYt�93c3�ll�t'��P��i�[V����z��QXm�XK.pu3�������t6���������������a���^�Ff�.���S�F�5���]����l�s�����(3���fc8?<<���{�K�Y-���.&�}7�< >�/��z�1����%���_J�<$���$�;M��K�5�#�����7Vj�P��������V{=a����j���1��?DX���Z�����#4jmv��2���]
�����u��a�'jtke/5�!��f~�@�K�
���<��KE ��~g
n���� s�8����s	����
;j(&=�������XY~�f���xk��8�ea�
����M����u�o�R�:�5_����Y�Y�j��u6�db�IaF��h��V�Y�il��TQ�j2q[��0�*�0��*Mg������k��q���6���	�l�_T9�����N��W�����8���<CY\_pJw����`�����ZE�Z����6��]�Z��u����(L1����W�J��k�
��s��]J7�`�8�����|Z�Ya���0QQ��l��5V;CS4������331��06�,�f]�>_`>����6:O�4����Q)F+5��Z��!�����k2/jM�������%�s��B@! ���@���G��5k�,��b�����3�}I��U3��a��@������[��?r-\|f&���d�������[T�'eq��.�Lm%_ ������3�?)���c��~��o���+7;�@��kFgr��l\\n2�J<����Kxmv������`�"U?)�������5� �a����G���#���������F5���galA67�d3����UD�/��x��{���zz�33�e@1����^��9X�x������ln(�`�����@hl���N��o���Z~F��L.����F�%�����&n�3�gfDn&s�s�cj�xgS�@�w���Tc+�.��6~���
�����>k����VCY�06/�E���N9Of�U��T��Y���z�
�t�-�fVQc�b�Kmnsp�~+Z}�����"�[jX!z���4�T��Z�9�Plf��5,

�Ne����F
BcM���6�����������5�x_�������+5��R��%���-r��=P#2t�L�u�W2~�� ���yY]��#Q�oY��\V)���\,�q�����F��11/��
���i&��Xo������T����jyry��|�w�)�L�6NF 44������u.j�6.-)��2y��m,����Z�xvZ@@ IDAT�!V�;�jp�����b�������k9E����
�kr���5,�{(�����R��o��wb�Eu�r�~W7�����E,>��K����������@x�	�9�J�Z�pN�C2����@(�x�E��T L����B ���B@! ��B@/�cNVe��5�h*�����8^����_���#O�b��������8�$���t50��=0r+��*�{��:���+������AB��+U��5�5��}:�)b{m0�X! ��B@!p�tK �j|���Y+�(����&����]>����&���#�]�VV�����4�=��Q���f*��@s�^�9���A����q����o����������Fa��.�?9�����Z���Q��h��Q,f����m^�c����}����M�m��4����?@�d���'��	��*�j��b�� �mT��K�4�����"�D��:�'��UL�L3�����Z�5�6JM! ��B@���@2��u����z8u�b��;V��g���6�����In�;�����"&����%�3�y�i{-���X8t�w0}j���j&�<n3Y��%�G�'%����m,��9'���C�eU�����a�s�5�+z��yL	����Ug+m�y�6MNZj?��=����LE(��?��z[{T=�8{U �~
������@��q���k��n3�����'�B@! ��B��$��@X����>��}��_3�_o��l�]dcx���O�}=+����'���I	�Vn��r��wZ�����q��2������@e��&pL	���U�-�����(s���������s��K���X��)�+~L������5j���� 4�C���P�rg�p]�������6u9�'�t�Y?������w��NF�����+
j�|Z�\�6�62��p�-(_��r<i ��B@! ��B@$&�����_�zM�>\B %F
B%��}9����B@� ��N�=��4h3�7"����IZ�r\O���cG�N/-*{,��_���]����9{����W����N����g���������y���>�$�2>�SK��0Z���b==i�0k��om�p[J�����B���p6|��f����|�����L���0�@���{�����B@! ��B@!��r�����2�������{��$��@�����pi������J�<i(���	(��-�@X�<��{6&!pDx�������1���g���c=���q�]�����@h:���7��P��%k��x�^E�����Bz��������?cn�t�[�[2���Z�'�=�}�a�9SH����n���il=�O�L�(Gs��B���x�\���d�/���I������'�'�f������j�5W9I! ��B@! ����2��(7�O=����p�3��K�~�m����~�xd�]PL�~���:���XC������>U�	! �@*23Q~r!��+i���_ ���D�g'��z-�����&m�@�	��Y��.�s���}�����h�M�'+Q�Y1S=����s����r���gzGq�P�C�?�aE#����� �	��}@�r�����> ���}@�r�����> ���}@�r�������@����Tu?i��X��C+-�{�8\�kQ�s������i��q�Z����b2��J��(������#��~����6�o����1}�C99����]�zn�	����8}�yx���w��lDz�y���u+1]��
O�[�>'����x&������j[���Z��.��0����r�����/����I�)B@! ��B@! ��B@! �f������Fs6c).���>_�Q���C!�(�2�b2�������/��mX��b>�Ko�����43�	g`=���Fzv&JZ�����{�,��!� 
R>��o��������gd/_�����[�{s)��P�i��4]w��XN��.�^�i����mz��g��0��,Dw�
��1���mu�������^
�������/�p7�����s/$�|�-3�o�7Z����sD��9�>�Mv�[j��}6M���qf���d�:��u�
�:I-�%pi ��B@! ��B@! ��G=%-
~v
���p������>f	��$`2e�W���Y������!�y�S!M���{���q�a��z�W�_��@?U���J�������qx���������|�?��#u\�c�> }�D���e�N����)�B��j,�:�u�N����'(;����Fq��_M��&=vN��`���p]]���q��������,�@�[���/�O�0�V'��V�
��AG�z��3��]}e��k�P���2�B@! ��B@! ���!����i��q8��uJN�=m+Kn:������ �<.k�i��{�0�P]�����z$>%'���DQ��A�W|,8{.gj�AXE��K��j�Cm�h$��]XO>���|�f�h\0�������~3�9O��"�h'(����A�+�����pI���iy�2Z�oJz���_`�%-S�h�C��S���%�@�}�~�����;�
],y��VA���>
'8&���:�1=9�L ���a�G���2�eF���'w��%��B@! ��B@! ~�4E����q�����W0�^������1�)e}4�9y�~�8�ygD����I�����u����L�����5}m�B���x����E������aBGc��^u���T��� \�y/��H� ��@x���,a�]2�:�����@�S$�! ��B@! ��B@! ��q�xL����Z����c4s��@�8��� a��� L����F� �`+���g��B@! ��B@! ����a
��A�B/�8c�a���� <
���-������m�p�^��������N�Q2L��F����&�?��!el9'�R�����_7���T������_lXYC���m�<x���Z/����4&�c���\�$��u���"RV678xl��u�\TafxE6��Q���������T�|�������l���#���*/lk���\Tq)����=����o�ytOk;�YMTnn&�S
B�-�5������<����l�[���8������KYwN6�����ob������CB'$?�v���bJ4����7����-�rMa��y]<�y#�<����)��d��<n91�� �M_�f�W1b��
����0�e���v^�����~Z1D�����[���sH��������*-��B@! ��B@! ��8�t� T�lXy�_o��/�0(�I�2�_�9��x��e�m_�k��~a&��x�%+�U�����`��f���x��:�z����1lB!�o������V5����|����U���Q������b�A�������b�ExM�V7��|�����^�3�������Ie5C)D;�s�P����x����O�Y��:�����q��l��gUt
�:/>S�K�hfW]��O��~E#:F��h��?��Z���y��<��*��	��������_�s'4n9�g�Pl�9
q&^���3CB��Cw9�i���Ax��������W�`
��hZ�_d�3�V�J��C�>�@��!�)V5��&eAQ�@8�4������gBZ^P�����i������5(������\^��������@XUU������wi��I�XP��0�@�ma��G���>���*Xw��v���b�;x�>��!�X��~��	�mGQ!�/�gT]��:.��3ht�����������a�X@ tQjS�uE��2��@����v&y@���lUN_����5��E��kma�[5�����)�%*���B@! ��B@! ���K��n����_��"��0��pn��
a�C���}73oo��S'�,xy�N
��A�-��G�>��?�����@����_me��{*�:�W,�8E���3�k����X�RD;�����l�{m��������M���y���n��)"K�y@ |8�y��O�u��q{/� <&R�~���� �nI!�����k��L�:����f���v{����%e���b��"�/����B}d����B&��}������<����K���Rjn�s��u��?8LL�p����;q:���_V��Yy\������M�<�[��qY~�*�#�@���g����7wK*�`}>��.d� ����b��0��R�A�!��:U5���nS�����j�~���}px�_]c�������
���_��l�����w��~,(�X���\��c��|��n�G/Z�-#s�,uj62��&6�X��������CL�T����29�T�� ��O��M\�v�N����@GU�2v#<8��������c�A'O��x�������Kk! ��B@! ��B@! �q��������43z�P��Wp������o��s�!��o�q��r���Y�
�c��{�zB~W���g�������������������O�`�k�y�#���� �������tf=<��/�$����p�����{��j$�O���%J1���������S�D8�_�������3+(��n��l`YMO����%�l�Y�X��f�K�	�.6����g
c�q���|�j�Px���LJU!��TOabb� �v��_`�%-S�h�C�)�`s�B@ L�A���g���\!D�X�`J�����.������g~~��u
���-�=����~���&�u��5�?�C������a@4���%L�J1Y�����yqZ1�R�|����#�����Uy��e��>^z��[2b��>�|P�|[�8�����Y���c�T���X���T���>������1}k�j|x�l�������AMS �,�G�s2a�C�=� �������1|9+�R�x����B@! ��B@! ��B@&��B7�~���4d���va��pA��7���2 �A@�J���!L
K�i4���]L�+��>��9�]��N=���d�]�������Q�.�@�v����������pvN�����n���1%|)	���u�A`�f�S��cB������q��j�������]<�u{���F����o���#�1�L���@�����013�A�m��:���9I�p6M���qf���d�:�#�wJ1�Q�,��V���r4����p��� ��g>�f��)5�"RF�w�Ma�Ed���/���N"X'0l�N���(�`
�@-���G��ef�y�y�og���H��s����f������}b8�)i����FDw?����5��V���`JY&�2��F����L! ��B@! ��B@! �>��Ak��l�);A�5����Z�M������cS���w���M�1�gBG;���a����CF��KvYR;����x/<-��l�=%����������6�7�1����[k��y�x7V�R�=i����v� LZ��{�A��a �f+c��p�����P��)Ic��"�j<1����4S��x��*n9h��q\�/�R�B^�1��9c	�F�V��Z��u���66�z���5?��c�U�G�m2�B@! ��B@! ��8*	�w��@��>���L�N��Lnfl1����������
�8�����?qQG:3?���8�CM(�Y5���f���f|&%�
�9&H$p�S�I	����(E�3��]N|Z��/����A�E#q4��z�����C4�F�����a9e����~3�9O����NPp���Qv�;_�g�G����:�LZ�WR�'ar��u���'�b�sJH�����~\��G@�����O?�@V�.�P��VV�(�����A�m��5��=��-�u����G��<�]��O����l���v�I_��pkH�(�z$��p��� ������4jg�b����B@! ��B@! ��B��$_ l����I�e(�����,�t �&z�t���(���0���Z�XL1���Q,������W6(�����uV'�-�op,.���a�:,�m����w�wSm)F���@������
u�x�
���������u�0�LC�CU5z��4���3�[O�#�>��A�����V��dr�y%�/5
���T~����2�)E�Em���y)7������<3��}�S�p��>7�'�39�|���_�q�E�,(
X��-.��i���Z��H1�f������|VO(��������u��>�����al��B��r��~������������a
�t���K��3x�4��x��:��1���<?(�j�&�Q��a6n<�Y�kZy���c'�&�����L�i��I�L���������L��Pc���\_\;U������e+�F�g��.dp=�
Y=>?��NUCk�5��>_d��}������q�x�K���@�ik
�g�1�_z;|^^�d����)/�� ��B@! ��B@! ��qB �@��i+�v2z�P��W�H������W
����9�PL��T�1��=��d �qk1g�<G����wv�]�������k��v�a�PV.,d������O�y�����$�=�qz������&=0��_��Uc��[���o��	�K��\|�I#����wq�:	����\p�~J�����9��p:�o�8��,��Wf����;y���,�����T��_����2���d���3�V������
����<p�^$�a����&��b����}�0f�����aI��,����Yk���%o��R����~��������)YS�F�A�XP �Y�R��M
l�[�n�!�����RUU�2�#'�1���1�H 4.��&��o`S�������7�;�+�1�����P��/���mx�����
���:�1)^��5�e>�l9?�@j���Nc����(������0�|��x?��t]�10_/�\\�]�A�`������;'�A)���,*d���v�q��L�����"��]!Aw}xL��ME ��NcM���������v���"��B@! ��B@! ���$�H $�.^t����q]�����f��
�.����_�c4*�>�s_�F����3���Ez�@�
o��
Lz�����;�5|"!��y����������tT��>\ |v3�������H~I �D,C�C�nto���f���\ a��� <���������5��w.�03�"��O�gz?yF�.B7�6������69[�2}H6�
gZh�;?k��z�y�L��-'*���x0B �)���z�������9,������^ n;�������m\3 ��'d117���>�_ 4�k�kf����9V����"B������<���zm=T���aY���d�.(V�X��Smn��}�\����������C���3h�g����8$�N�bxD�[v�s�v���}rR����o��8}N>�����%�O1���������[Yg���13�~b���8�����B@! ��B@! ��B�8$�P 4��z��� /�j��^�����2cF!���]�F+��F^�S-�U:�efF?3iZ�|V��ykIK^kfW]:��,f��K9e�&�N�@h��Y
?R��/���&]?�'g�������y�x�4�M(`���L�8��nfu���G��;.��m�{�@��9��8�;#����1{Vo���%����V�z��������K��zu?f��3,kX���Ak�z IDAT������2��O'70y�yq��X�������y?f��q���G�@�p�������I�N�=n���PU���%��|v.�Q(��	2�$	��A�d�L! ��B@! ��B@!p	t)��d�I1�x�E ���uG8�g+�Q�@�B@! ��B@! ��		�@(�h! aJa�a����[w9�i���)�]���.��y�����/�Y4��t
�)���(�7c��D�����eg��Ux��yD=n;����e|%�}*�Q+F�q��c������7��7k�6���"*�����������(��(���2��8��3:��?E�
�ED�qdY(��m�f���'[��i���M�������s>������9�3�~Hv�lc���B{���s;,���t��N�����A�}�%�G~<3��6���!�+�(@
P��(@
P��b
����~��;�)��#�Ob���������.�������hPQ��hry[���?���*�pD
������XA�����(@
P��(@
P����J�����I�����'2�����&�����X��Q����e�
��G�������p�V�.T�[�w������ ���S��������q_;��z���*�x�*:�'@�y&<�j`�sL��c"������r<��(@
P��(@
P��(���������*�������R��}�^;|��
V>����e��[�
+J�� ����-gCi
��>������Z�MB��%�6
�����:���T��ag�,c��>��
�+ ���NX
�k�����C�C�c<��g�����a�!���oVA���]_�GP��(@
P��(@
P��z��t���?���!��������Q@�39����
���d!P�9�7����B��{����!����k�����;����Z
x�0O<�M��K�^	�}!����&D2� M>��O�h�3 ,���?����}p�������	�$E��*k(�E�A���B���p6��� y������G���p}�i��d(�o�!.�0�1�$
P��(@
P��(@
P��@&	(N<8�<x�.h�����N�{{&��}��(��e��l�����S����c����N
B����0������l#������?0H:�>����>��s��>��s��>��s��>��s��>��s��>~����,1*����� z��m��B_m���!��z.��� ��Py<����X����V�����*�x�[���P�;��P����0���'H�^	����c<�[_�N;��
����%�y
$�
��o@9��@���A�� �UP�:�� 5|��>�	�y�C����A�	�I���������(@
P��(@
P��(@
P��B@��"k���yH�G��:(��@��@2��z�V���p&��R�w��8��|�P��*�6�|���(�`����pV����e�!N�#�7\a��p�d�d�Bq����=�|JDo-|O~��Kd�r�iP�w%|����j^�Mq�����F�/�����!��wt� |�Q�����������x��$���y�Gn��T%���-���e��� ���G��������/
P��(@
P��(@
P��(@�4j�<r+t��A�
H�Z��u(�
(��F�oA�xR��s�p�yZ`�J�^	)�P(3r���TZ-������p�������
a�T����������
��������<�
���'H�Up�}-�g��]�ii	��/p�(��oB)��^��z��a?^���%9�,��{}���H��h�����`Cs-��X�������z��]xq�~���r�]��K�*p��iq
���r��(@
P��(@
P��(����;E�����
�P@���	��P�e���|@���!e�dzax���tS�]��`��q���@c:� h_[���`�x{�o�;.�
h��Xb���xozU�I7�� ��Z�����X\8�����-�f:|�����,`Wc���
�M�F�3�%W��z�����m�y�q_+�]&S��Y��`FW�O��xbjAY(�B>^���d�W��Jm�i�]�� ��0���X�4N��(@
P��(@
P��(@��P`�'��.�6�j� ���vN�l�����~�C���P���������
��B������4�W��`�(_@x5|���3 �\b�HX�-;��%8���M���
�&9�<��(@
P��(@
P��z�@0 |!j�#+��&s@��~�XA�
BV���G����a�J��g�W�C���(@
P��(@
P��(�!� �"f�c�!+�^b4=�
N^�X����(1D��I9\���1�V�/�����Be��\����������%��k��>���Z����@��f����Sa�!���h�J����������v`�7xl�������WG�-�>w�_���\�)�����={"�����l��V;*���c\`E?�Lx���./�Tclvn��������)���R�7�|,�X����Bu.����*bh"6Z1����N���`��F\�k�����]����R��B��\��1w�g�����������+�f,�Y�����B���9����W���T\{!&6�<��(@
P��(@
P����+c�����62B����)�R�����d�C����A�_bT'��
�:L����v��}+��pe}������?fh���.%
}��������<�eQ�P�mr��/�kFM��!�K�2xQ�k�JR�g��P@��a����-mg���%��bi�~������w"ws��P�D�����_\����!'{�(�xj@!�*�	�C�W�P����]s����\-��g@(��bM%�G[�94W��3x,(@
P��(@
P��(@
$(�����������,1Q^�����
/�"3� ���
��f���a��F����j��o(�[%�P�&��uce��Y%�B�k����������
���m���:,T�����`E^����$��!���bF�e�jTV�kp���)%�q�.XF�����M���9o
����B(�c��Cn6�)@�bi�?���e%Y`���{�0.�0��:��V��\�W�;���D�����-8�x0��'8�����2}>��k��E ��W�>X[�����?7������U5@S\�&��!��_t���j�w�����8���Ake�����j��R<���c��+���X�T���l&r��=�<��(@
P��(@
P��(�XA��u�����JE8�H=K5��<��I/1:�mrVz��n/�������
���$W��q^�
�
*���e=��U6���z%���= ��Z�%9�xE5^����	f��P�`����v�"*���Q�/ck���Ru FEVX&����o=?��"B��7C��,���@ w0�Ycy�yW�B%4��������^���jO��pX~�
We�FY�����JJ�qV��pL�	�
�^C���m��Wb��)@
P��(@
P��(@
P�	��0�d���*�'�� �W@�h:4���-���e��� ����p�L���	���0L����l����04E����2���1�) D((��?���Uk	���+����P�FR�����07p�����1>D���x�K(tk	B%7^�����(K����%vO����/��Tc�:���06K�1��T;e�>�E��R��(@
P��(@
P��@b� �]��
��� ����|�rb���S�X�"�S��FU����N����_Y�����|��R�((`�U��B���H@���aV�r����O�8�P�Y=S
�*��yF	<�= �����_9]��p��@��c���)�&��(����x4(@
P��(@
P��(@
�/�
��V��$1���U*�]��7�
����/]{��}�7��]���Da�1[�M������.���s�Y���P04���j_�p��.��������2bI��O�
�c�m7
L�J-t����%F5��]�.��^�W��W�c����W��Z�b
����;�y@�>���2��ic��u{�7{�r�-�'�dB71�(@
P��(@
P��(@�>$�ZA�1���P�^�R����� 8]�����b����=�{x����79�aa�T��z�E�����u���c�]��[8�7W�c����p��_5���DTzXi�aD��'�pfU�s���<]`���^��P,�kEF�v��s`AC�;T���\J�:���k_-Vg����>"v9mX`1�gHt���@��+���_�?�d�L���/N;��p�!8p����V}��Ss����P�E��2W`�I�Y��0#+�l�����f3���Q��h��@�������}Y���aH�^���js�7���
�:�Yg���Ax G�e�����(@
P��(@
P��(@
P�/
��0����0��P�
���)s�X�����Wes����!������y8��^xi��m�"�����
���8*o^���<�2+��fk����8J��F�:cBD��m�A�\aTee�W���L�WV5v��Q��Q.6F�1��4���8��9�0��+���%F
�WF���oPI
P��(@
P��(@
P�h#�
���� L) |�7tq,1z<�W�����v8,�V�����ju7�[3>u4c��J�q�L�5bL(�d�4c���%v�s7��
�����`E������t�	K4(��S����	V�+sqk��Om�@���\h��4��M�W����M
��l�.�
G�0�h�0w=��G�~E��,�����f�c?#G�Iz=����
/�+��e���9�7��<#�j����Z�<�h�./
�z�i0�Z������vA[����!$V7�������K�!YzL0�b�^������x��C�~�-�c9j~�Q��(@
P��(@
P��R`al>��t����{���}����K�c^.(@
P��(@
P��(@
P��!+������8ww�g]�
���q<<����!S7d�(@
P��(@
P��(@2 ls���0��2���eK�*p��iq
3������}uXg�f���8N��d���nBZ���8x<(@
P��(@
P��(@
P�
0 �2 \#B�Q�M�J�w�j��`��q���@C:� h_[��3a�xG�o�;�J�g�A��w����������o�c� L	��)��{2+���q�(@
P��(@
P��(@�����K��CaZ�Z���Cp�d�����~� ��C�*�P���mr�w���uB�����<+�xd�{�	���������	R}#�gL�+W�{���MB��^�p;<?�h���7�Qa����q_[:�Fx7���|`������,��f;�%W��z��.Hk��V�B���q_�R��(@
P��(@
P��(@
�/�:�����`@�:�j��wA0��o��#mO=
�� J@(�%di(��c�'p��]����B���!L���&��n���x���U��������_��/����6�'BQ�&`3uymI��4�Y���	�������Q�?����{}���1"�]�,�
�e�y(@
P��(@
P��(@
P��#��OF���QG����x�,*���������XA(�%dk$�����k��*wr��	\u1W��'`wg����b��B�f+ ��
T�8� �n���������=@����@�u�6h_<��7Cq��������<a�H�����K4������K���/��x�-���F
����~�FH��QB����\U
����Z�-�1�,
P��(@
P��(@
P��(@�6��B�L8�K�CYRU'2 ����_C��W��U2/�
��#���w���-���0�ax�S���J:�>����>��s��>��s��>��s��>��s��>��s��>~}���T���S����_��)�v�����"����%H^'�
��bA��s�y��
huPV�E��PH�LT��C�;��6'�����e��.��*���H[�"Q�4CX�RsD�|��9'���p���������6��A�������A�z�:�(@
P��(@
P��(@
P��L@�gCy�i@~A����f�
nK�w1�������
�=?�7|"���s�JHN{R��R��#�|h��v$��R�T��>IhlLC��V��wlk_��x������f����nB��'���gf���Z�w���7d�g�p�r��Q����7���]]�Q(-����n���tlw�p�?�w?���qp>��P��ePH��<��,}a#�(@
P��(@
P��(@
P��BvT����y�Y�(5mx|��*��M��1����Q��Q�So���s���  (��3l���T�L<��m|�_��eL����P]0M;�����L���0��8FQ�t��B�^���aQ��n�S�5
���
���5�nk��z�0�m��p<o}8.��3�o����:��������&�B6�EIw_>��Y����;�.�lcW�x���f<t@.��U]��)@
P��(@
P��(@
P�H��b��]\u����{s�������&x����B�%���ft�,�nm$�>�Sm��:�(����g�����
.��fkK@���_hW=(kg\'��Z}#��?��]c�(�� ����)g�dm�^<K����IGC��}HZ��W&�q������Z1�'`�q\�m7/��.����%�������;�mS��(@
P��(@
P��R((D���PZvFmJT�95����L�r���{��2���1?��]��]S�WK@x[���,�|�cD@h�?JC@8�����f@��27�b@(�#��Q��(@
P��(@
P��zT�G��K������ ��%F�&���@5�!�D
P��(@
P��(@
P��@w�h@�+*�H���/Aa�����=M�G� ���hp�A��tT��%F��R��+�n�p%��[��e�WGL�O��*;���`e��T��6��Bp���>xQ>`�������%6��N<���WE���
�j�{	v�'�A�^-A��l�48�l�+�E�da�`=.3D��^/����V_�	!�~Y��5JB���h���Q�����?�<N<���7-b`~.��{����_�� �d_���-�v�����(@
P��(@
P��(@
d�@���*e��R��"����/�AMhB��~��}w��U��|^<����,�+)5`y ��9\
d����0y�U@xO����?A�������V*�y���!ax��(�%��`M P�7 ,�)P���/�<����p0g@��|O~i�CQ��T�+�G��Q��(@
P��(@P�9x IDAT
P��2Z�G�^YA�~:�'�q����Vf�������vc��P��������3n5^� a�..����a���H��xM����v��ad.f���(�0�e�C!t�;4�s���uo���^�����AZ���wDBm��lv`������&I��*��)���s0A<*�%F���br���v�)�E��������o�0����(@
P��(@
P��(@
P@f�
���0���
Qj���0����}�.��;:m!�
��+��&b��[�deD��[i��s�<F��x�K3V���e��d�B����3��e��O�yV���K����=F��)�XS��Mnlv5������
������+�D���	��r��0mp��x�x�[�9L�>�i�(@
P��(@
P��(@�^)��a�� '�2��AX����c
.H������P����T[���e��\'��=#����06G�Q�j�����p�^�%3[�Z�#�? ���e7�)V�_0d�g�EZ�e�W��"���"�Oa�����M����6K�v6�e}�1
P��(@
P��(@
P��C=FV�����!f�OV&�Y���0�
I��+M^��x���C-���\�-R�����*3> ,��-*���M��Q����1�@�R`T	@��t{@�|3 L����(@
P��(@
P��(@
P���h@�
BVZ����5���f��Pl}��e��?�^�h���c�H�����<4���d���B�R�;\��T���d����.��u����Y���l�?`�Z��1\���O��?Z�f|{Fu���r�ab�<��(@
P��(@
P��(��z4 da����M���]���U&(Q��r��|>�����ZB/	+w�pI�3���BJ��C]n�7��V��l�!D,���kj4�{l.��Hv�Q�"�>u\�3��K0 �p��L7*X}U����v�Q~P.�`���o�x(K�w��a�:X�YaucQ���m���a�V&���S@��4�����s�3��(@
P��(@
P��(@
�n�
{M��sl3A�T'�!�5
��;�<(p�a��_����0����������V���|Q�	�j�c�� ��'7���%zC�}J}_�`@����F=���Ph�����~�v��h�
k,�6!.��{VD��1a��-�v5�|��(@
P��(@
P��(�k`@s6�a�7��{z�XR��{�n,��]��E�u���n9QI����R�z��B
.+���"B�``��5����*�8�r���pYv���* �7W��
%�<H
K�o��t*\V���2
�����%l�l��������hz��.�_oo������6'��K��Q�,����q��Ls����p
P��(@
P��(@
P��@�`@s�&yw�&���vZ�{�5	���(@
P��(@
P��(@�Nv~/�������\b4���rL�����tM�o�IGC��}H�����w�}x�9t
P��(@
P��(@
P��@�]��b6��� ������q���e��:H��6KF��D�������
�.�2����0�~����St��QW�u��b�3�s��0-����i����(@
P��(@
P��(@
P�O
t�9'�a=$c~���gg�a���5 Lr�s+3f*�
P��(@
P��(@
P��@�
0 �97i
��������
��W�Au���Y�
�������f�
���)�����7���{P���i�;���k�����P��i��i����
����|�M�8��c!���K��:l��(@
P��(@
P��(@
D(C�5�P6n������I����!�����<�yE@S�W�� �U*�O���
����uI5��k!=�T_���6�oJ�����P���3�<���
���7�*O��V�������Bx����JYZx���_ ��c�z~i�8���P�5|����6F
P��(@
P��(@
P��(P^�;d�-�n�3QI\�����,�����,l���"���{*��C��M�v�ZA�6m����P����������fX����CiSA(4�B���Y���kILnB���pw�~G��7~�����qT$�f�,�n ��.F�f�P@x�
�.��-�J#�p�Up{�����'�y��� A���P<������*��M�?<��(@
P��(@
P��(@
P $`0@��	P^qu��]�@[���������B����a)|c�C������2H���'OA������{������}��P�3�U�'�n��#�����/y_f����%?��\�s���|�9���|�9���|�9���|�>�SA����\
�6B�g?PXa�I��_��,�Z���}�(��PMQ�������
�a��m�r�D2��c���,;��^3M�>kr
���4 ����r��(���-$���]cP6B�7P���C�@�����
�dO��<��(@
P��(@
P��(@
P �� ����IR���<��WB�:�F&(T���:gh�5D��FA�J�uSiXhl��A(��c��V
|�5�'����?�i�=T�����>,���i'T�'�+�Y@�
��E��/�^*<���Cp����[!x�����$�(8�_U����1�2���Cp0����3`�����JSS�/��(@
P��(@
P��(@
P�}E@PdC�{�v�|������9�
\�g���P����4�U��oA�nCB��4����*g2�Y���a����n���)��P��@\���Q����������s���7*�������������Y��'Y�����[R��s�y+JpA���'�b&�%�~����*<vS���/��������c��l�I
P��(@
P��(@
P��R@PG�~\&������z<����X���H;����P�4�!(kM�����w�wPf��N�=�7�G�A���RA��;!m}^�YR��Z�+\{em��k4TJ/�sw��X>����v�3���i<����R<#��c2�/���F��/o��o��G���T�����������g�Wc���(@
P��(@
P��( ���\�w� y��5GK�<�n |��1*���Z����i�����h������a7Wg�0$I@&�eR_�v_�l
P��(@
P��(@
P��~����JW��+��0V�XA����C�eR(�I}���0���X����3)@
P��(@
P��(@
P �����& �"���cM����>�������0��������a�S�p���qb�.�/[�0��v|���q��<�}C6��Y��nLz:�'%�����'b��F<��;V{P���qF\w�'E^rrA9�7Y���V,��E��l\yk>n>Yqp�������3.rao�\Z�;��a��m_\�,x����rbg=0hD�����T�����q������h��'M��s��T9��	y��E
{F,�y������B�"�}^�sn]��������%������&;;��`���q��A����]|N���S�����(@
P��(@
P��(�1=��-���2�o2�#46���0�yda.f��������6D1n	\��y����p�b_�G�c���8B+w@
j�E�[��.*�����BATq�u�����s`����/ )Q\�����%X������6�q��MX����[���
/��6y�M��h��.���O= ��+���>��p�����,����%���; �k������5O����(@
P��(@
P��(@
P �z6 d!����
!^~ ����2{t����Y>:��&���� �[��oq��L{�w�S�+�;����7J�1'��]T��q�������,�W�DlY����X1��r<2.XM�r���G���w\���A��_( tv>�������b����g����,��G���z�~[����p�����J����v��P';�O�0�/�8'���������c����
��V�������x��j<�!�6�cd��v������1���_�\����PA��!�n����%�������87�h^h~�����9I���Q�j�(@
P��(@
P��(@
t)��!+�'�k�Zbt]O3������)j���^,�v��+�����������pKQ)6�ExO��DB�;�~�����@�_�V1g�L�{^�2J`�����W�-+�e��#�����
�����a|p ���B0r���>;G�{�����%���v-����N�/�������v�C7�eB��H}�c�o�y@�!����}�w���(@
P��(@
P��(@
P �z6 �5��Y�����\b4s����C��8q�G���'�p����z�������HS�B���������(������0FwZ��b�p���Ux*�v��YFP��n{�XF����S���X���s����5�1����U�Yb���	��f�(@
P��(@
P��(@
t�@���
�bJU��)�o-���y��0�[��k4TJ/�sw�B���������'�~������J�x[)^���6�tW��Np)LF^^�;.�ax����th�,F�l������14�U�f��k5�jH{��a@�'�y�k�c�o�a<��I>�x(@
P��(@
P��(@�_�@�����0�4���c��Vi�Cq.1��� ����y�p�>�Yf����tChY�a������^y1^-!L����}��=���������DB	�����������< tm0c��M�t9�?MZ
��G��������y���)��
8�&;ncP�=��[A��\��F�J������%���y��=��c�+�y�{��h��9�S��v�	�(@
P��(@
P��(��=r�8���a��F<Z��ugg��"0���D|�B5�>����<8��"�}���c]V/v�v��z-���nYF���s��y�T]��}b����T���Kq���r��J>^l��O��7��S����`���>[�5��9V��R��B{$��c�m���X~j����aA��.*����:����x�@#�2'�UB���-�����������sJ�C`���~��!�����	����\��xN/�;W�������B�y��r�*$$4�Kj1z��K( �g^��-f���������
Y0������<��a�������c�(@
P��(@
P��(@
P a��
{Ma���O���XK���co��x�Z������
�,�c?�p�9;QB�.g�'&]��u����
.��Fm�(��(�e�wc��������	��x�/U���7u�G��� /�`�_AW�IO��uY�A���
m��\a"s�r5c( �k^��Pp�T}D�-{'&2��A��D�{���(@
P��(@
P��(@�� ��0�,q�$���� t���3>X��g�=�������sq�85�m�%b�
+��:4"�\��KO��`�]�eYg���Z�p�(����������Gi�b��0�)����sq�����R/\�vS!��-X��uEj�>��[n2`dD�Z+�q����V8��^��O�����1md�Z������X��;�U�m����q�e(DM$���.b�+���]�U��?-��7`l*U{]��k�3�r�S�� ��������\>����
��l��Tr�YA�>'r��	Xx(@
P��(@
P��(@
d���S��0�[6��0��f�i��u���y7�����������(@
P��(@
P��(@
�`@�e@�Z���I����
��
�����Axt��^Y>:��F��O�`����>/�=�}��W!����n����&�g��z��i,� �a��[B��m���(@
P��(@
P��(@
P��"�s��P��g����R)_�&c��
�$o����P;=�4��qu�8�q����yD��K(��Ea�� ��_�f��hs.F&4/	t��R��(@
P��(@
P��z�@���A�+��T���R��ZBcc��0��p%a
�*�!�{(�
B+�s'+3�6������^��yh���������(@
P��(@
P��(@
����0\Ae���)�w����/�����������2�Y������s%4M;��>^X!��q�|Y�a�q�K���]�v��O�R����u��4drv�B�a�
�^��Q��(@
P��(@
P��(@
D��X��!�L�JTv�kPj{Ka�&�a��{,�
B,[�W_���p�*�e�f� �6��-��Qq������L��e�f��P�>������n�Q?�l	��o��+)5�FL�����x�Ip���z}6F
P��(@
P��(@
P��(]@��G���u��`4���7��`p���2�o-#I���V
;��*e�~w3����������|pP�
BEcz|���X����&M 
�Q�CQtX�
e�KP�xSK�%�`�����9��@a���U����W)�G\�[�w��v�?���������+�,��(@
P��(@
P��(@
P�������Jw.A����U-��kN��PAWx
��*'p�^�a�$�8�������k�F�������>�|������~��#�����G~?�������+m����}���G���}���G��Q���(-�>�bUF��\;A
1�HH�r�C���VA�J�|gMHJ ��p�k��nF)��Jyt��[Ov���� :�k0��$����O���9D�	��!����5�WE&p}J
P��(@
P��(@
P����@VTG��������o�'}J�C��Wvx�5S�/��	Vf�Khl\�|
��h|&�}�����F����DU?�Jf�\����,��P��A-]i�H����{6g�9���5 /{�*(w�I�����
�As��pY>GN�Px��K�
�@
P��(@
P��(@
P��(����e���'������O���v_8��.
� ��������hr�����]�z�&x��1�(�ZB�0�S��xja�?����vY���1��k�r����O�l���efA�����x�2��`�{��[�K���������Gw},��(@
P��(@
P��(@
P ��CND��PX����$m6������@������P��?�
mm�}����q$���
4�]-�sX
L��NT�~r7="k��6�\z!�d���xOI��L�2�O~���^��&`R���TB0c��%%�f�WJ7&O�(@
P��(@
P��(@
t���S�sS7_p������]A�u=% <��kC�����4�D������_�!�����H���(@
P��(@
P��(@�nr
Q`n��kE^�q������m�YV}�F IDATA��{1��L����0������x*(@
P��(@
P��(@
P��XA�\hl�/1*�oE(�O��
q_%�^b�_A�����v�F�[b���F���;�@Ip������8�b����+�aw.0�p�C����2��������%6�z�'��J�C��K0�>��L5�j	��P�[�s[��@y90{������gWI��6����f�(��\�F���Q�����?I�����	�u�����h{F������u��Q�R��(@
P��(@
P��2G�g��{f��������	��N��d��oa�������E�A��-��$<\����C|
�d
�B��2`o%P�txO�8��?M���D�F����f�C���QHK����`�o@X�T�;6�>t�+ �c�e�Cy?�l��(@
P��(@
P��(��z6 �{��0]wt�=+~�p���?.0=p�\��
���1�^��V��u�����@���Vn�p�����f�M5�kq��J�{N0�?�
U�uw���cp�(`�!���
	�|�.��P�uE`��~�%��U&�C���p�r`�4�r�����-���JX;Z��1����;���a�nj�K
P��(@
P��(@
P��=���B[���{9*�I8~0s��{�����{N��|
#�|��V�$�����.��������us�b���	��(`T��>`�/��J���4�o����S������n�Xvh�Uq�����s���"(@
P��(@
P��(@
P ]=��
By���h�3Va��Cs��E����W��X2�e�����.E�A8������}���C�p��������m�4h�a�6��0���s��H�5
P��(@
P��(@
P��@�`@{��ao�����"��
XY)aU�&����s�=@7�q-�% -�^:���$�VL?C�U�@�0jDO����7�4~�4(@
P��(@
P��(@
�%�	�)���~����lht�@-]i�8���$< �������M���f��u�o�<���'!������r��O���]_5P�&	������;?"V@�L��vI�D�+����
/��n���b�W�����_1��ta�A���p@(�&��c)@
P��(@
P��(@
P��2��
�{I�_62���uJ�?_hl�N
v���?0L�����@���'��?HX�#`R9P��r��<�[�I�-"�Z����?3O0��D<�e���*���jCKVH��
�{����)|�c��S��"��{&	�^
h|��
n�t����=�|U������
VhV���Kx��vK��,��e��3��� ����0���mS�~�J
P��(@
P��(@
P���=�� \
�i&���m%����h��X�k�H��:��'�-`�A��<�����pM�c'�%�����*~�p�gm���w/��v��O/J��}�+ ���R��F�B��$�fU�#G�kk��0W�!aE������9���a���)@
P��(@
P��(@
P��>��7E�
���e@��9����X�U���KB��������^��S"�r��W��;�`�P�([��W���G`�(/f�.`jA���* �7g��
������[�S0s04�G�y��?m��ot��U���$�K-U���nms�1^�/����	��,s����p
P��(@
P��(@
P����I�a��d@���=�=�����t�={���o
P��(@
P��(@
P���+�����0�ax����LV��*�u=U��M|����q$�����p7��ja������A��Gdm3���+��qS7d�(@
P��(@
P��(@��&�s�B�E�#� � H=��V���	a8`�o�����������b����l;$��?M����/�(@
P��(@
P��(@
P���=������������\dB@���uW�� �.i^��(@
P��(@
P��(��������z��*����$���������)P��A-]i�����$�+����}/[|vY�����~����E��+�9��P��(@
P��(@
P��(@
�  DA]�-%�����@�-�-��
���}&�}��V}
M�gm�]������n��
���r4(�����K���R����P�}����:G��$!�C�W��>���k�\M�
P��(@
P��(@
P��(@��3f ���6����|���U����]��Q�W8 T�P�����-$E.D�������uH;���6���P4X`q������T�A�Zy$�����F�PT�����n�$���8�:8�f�,�#7o<���m%$�7�1�$
P��(@
P��(@
P��(@������B��Y�.h��M+ x���S��=�T�/�#$�!p�^�� ��%���}���������|>����#��|>��M���~�������~?t��#�����G~?������~l/����Bc���������u�!PjLP���T����Xrl���r�
�c/r�N�h6C�*�`���~����'c����=�X�d
�j;|
�!�������.���h��,�`i����jw@��
H�UE�/�f��(@
P��(@
P��(@
P�W"��Ar$]^`@��
(�m����R������ z+�BQ�ENNF�
���*���h�����������4���Y7�v���q�m(t��vA�K��0m�0��;�0�Lr|��pV�*K��6�*��h�,�����X�0�Sy(@
P��(@
P��(@
P��@�=��n���
��^N����s�x<��8��~#�����T3�������h����{(���j��6���S���������=���A�B����o����O���	����_��?���8;[s<��m�;��wU�����Z����q�I�W~J��}8x��_br�@�/��.�7~�g���o�c����(@
P��(@
P��(�G���������m#��:���\�7@}�Aa��[��]��i� ,i�����e��G��M{����n� t7�P��9Y��������^��1�;)K=�5��6v���Xsc�dnx�H���6���~���H<8���#��Q��(@
P��(@
P���L����*Sk�7�����~}��0��0XA�e@�}qdb���'1 L���a�7��(@
P��(@
P��(@��z. |0�������0��8����$�!}5 L�a�t<��(@
P��(@
P�������WU�q��p��@M�w�j�����e�
Bs�� �4��e
x~�
�,@i�\pl�g����ipy�lU����/��@��N����!������Z��t����'/��r(�7�����XV�Ei�\\~	�8L�z%��>��'�`��	��36��������G>o��[\��
�a����p��,�Fk�k�#+���z������K0�4��b�x����e�<%���}���fQ�������������a���<x�����{�ZL>������{�W��o���o��1��|�>-��W;��=��5�]����\3�����m�0��&,��	���+Gb�P���-��G�a�U�(�Wd�yO$���(@
P��(@
P��(@
P�/
�l@�?����=���Z�1���a?1� 4Yp����N�����z�.�3��>�������cb��F�v%8�P@XjT���Z_*\w�A�{DW�PzR9�^�1�A��J���5Q�s��[������D;N�{n;wJ&$�u�8���/�[;�v���- 4�Pj�v�h��P�{�ud��w�-e	�Ej������G��O��!+<W�`m����������#����S�"�{"�;��S��(@
P��(@
P��@����W�A��'����6�?{wUu��=kf� �&�"�QQ7�Z@�
���U�Z�����R�V�ok�j��J���
~��Z�
��!��@�I2��1[2I&!����w�=���9s�y?>�T��z3*{�����SA���=�X���ziN8���tk��=Y��W.�~�z{��z��93{��3mJ��=Z���~��G���/)�K���M��,]�g���Q��b�t�A����5ww���^.>�6}|H����W������n-�����Z�_�5<!p����*��pL���m}$-��:��gt����E�:����%z��� ����H���~��wQ��,P�46��q����I]5gHx\UZ�d�n�`�����	�������j��~�yi$�kl���E�&e��c��;0�~����������I�n�~y�=uaO��Njhn���/ph�J��O���N�\b��s����� � � � � �Nn@� <M����������z*����X��
�5��n���>���f��~Zsq$��Z����w�������? �����5l����{�����n�>��TK��iS�t��#�d�d�
��z�w�G���7fjB04��	�o�+giR�2��Bff�����X6}���/[[�+M��\����- �����h��z���~8@���Y��$�\K?/��<�J�.�
.�)il/����lB@���c����&��=�TA������O�u��p�k�1i$ l��h�s& � � � � �!��v�
��N���AX����K��,!��
�-C�2���fS�]b4z���v��i��C5yP��w
����C�zP����P��(������U�����Zk�T���@������8�O$������vM��	Cl�ga
/�y�����=#{�E�cs������[����������c-�))j/��C���G�����a���f
��19)7$INK��S����is���7�C@@@@@ "@@��\���
+��������M�V����]���T�,��_}��0����m���S����@������;t�V����\*����4���e%N�����o*���
�:���v�+?�
.G����yC�A�p����BK�V�8��������_��q��JH0�f��<-� lM@~����6���*��o����9 S����������08����g�F@@@@@�
�����	�����u��t��?���
���6M����Yn2v������*������0�:�>����J/���=�
��R������K��������35�x����	��x@����ea����?���wg�j���^����il�Ma�!��~��.*���3D��o;]��;�������3@@@@@�-@a�3�PX����
}e��x�uus�[���Z=p�����?K��$yG[�5����-���Xbt�������|�_�:u�5Cl���|�N-\��n)��k��;t�'	�mJ�n;������U�N��Y�O��tw��PUz��]z0�����������Y/�p�7s�i��Y	Ju>�6m,�Oj��^�,��]�����������k��R�������Zb�RO��P���4!�j3���b������K�h����A����3�:iLg���/���s��k<3���7��'7���[@xn��LJ���%z�������qowMH��7P����}��hK��*Pq��1=�~�V��V������6�K��]xQ��e�e3��*�V�o��hu��}G��wG�}Bo�sBR���j��h�Xs � � � � ���.p����K�!��M��?M��@x�����/�m�i�����4��fv?�yD ����z�O��`^��N�9P��87&���:�������wLM�����5�C�nZ�[�/��?��}��$��V@����`�FS���O��K�>5���:������:���v�����[@���ff_���p��(����u�xL����J}�66�" ,�}@o�z�>������%F�=��=z<��{'Z���s���� � � � � �����+_jw97n��?����8DUF�I0[��P���XE����J-�?��*���X�7���<�$����ic�j��p8�����5G�����f
:#U3G�i��veF��t:���|=�i����5h@'=�_�4��+��R�6���vi�W��'���{����kU'z�����������P��\�Us,�J�����P*9P��>*��m��}����I��4gBg�K�MTr�TVi�V�����v���IcR4!������@�M�o��s��w�
-�2<^�K24�_��+,�/_/���8Fw�m��i��H�a�
5�h��P~��(�S��hEx�2{$i��t�va�E���������gHM���3��U�R3�D�K@@@@@NQ��F*#a���P%b������ l�F�� l���t{h8�kO��/ � � � � �tT��v�
���}����aG}m�u�	�Z��@@@@@Nm���aK����?
����L�w�E]s~ ���r+V�W�%St�d�&p�!�e(\&b��lj��C@Z��v
=AK��k�H��lMl�f��&^������/F;��q���C@�b�E@@@@@��'/ �p{RA���u���C@x�>���  <��@@@@@��8y!��p}*c��Z���y��>�"x,�����4TW��+td��q� �<��-�/�}�g}�e��?�Z���~on� � � � � �����L%9��_9�w�%����C�A__Y�*^C�;W����Z�W������������.R��������S�6����v�����l���������l]K�i�uF�R��e�l��}�e����v�
@@@@@@�Y���e9zP��O�u]kN��S����������q�D���g������?���&������$��1�J��{:
?�� 4Ys�Mt������������~Y3��Y��f��,&���S�#���:[�fs.2&����U���/����b��eZ����7���6�)�E@@@@@h�@B�|.������u���%�v*��
W�4���u�\cg����i��= l�q � � � � � ��XCaa�+�*����er�|���[�+��"�}�L	}eX�F*q��C�|��e-/c�!yG��h�g�Fyv}%�[�]�r�:�<��\2�5;�)���T&f��eT�H���9pbG��!� � � � � �����,��,y-���ep���*�B���c(,l�
BW�*�S��I��.���d+1�NYf�S�z��G����l�I{>���K�Z��*�/��S��W)�������#������F��*q��Vp�L���z�!� � � � � �t@W��v� IDAT[�N�*�sC�6�s�{@h�����_W�*�-���<��j
a�c���-x����.)��q��g�<gU�onkU�`���r������V�?�S)���Jq������
��7�\soN�s������B��
������DM�<����E@@@@@�T�U��
�`��,W��^����-^9�j�
B��w��^Tk�-%g*yzY���g�����������*����0i���LYqR�^rf��,kO��k����d����� � � � � ��8���?�g�yjW6= U��% l+���K@�%�A@@@@@����*� ������[�����j@@@@@:�@� �3��T����W�H����SA�����Uz��T���JO�����~e�����l����������zs�O�]���m�����>QC��^��}������7���V������?��d��@a�Gsosh��Z:���Y��+|Z�w���?�V��K9&]�=���2�Sk�J�OK^����}Rf���n�~q�Y�Q[#Vj���}U�O�aV�y�M?>=��B*���G�����
g����iz��v��W�.�,%j��&m\T�����{������N�����N=��[K6���8����
N��/aK���@@@@@�X��jp�p@{J�t�h�Vn�����������$m���{6V+&=�8Yw�|# ,�j�-��[/�j���5ws�v3�J���,�l�o���v����:�j������u��������e���n����I�-9�5����p�YUx�r_��,L�}�|z���5�����a6�>A#B�6���@@@@@��4 TfE-�?��Y{��0��G����ge3IyoW���]FM���G�oV�@%�A�f����]lZ�8A#�MT���]���?���u�HXW7����RM��Q?~��.6�S �r����
�|����
����]T����4��D-����@��^�:�R�y5��-�l�
�������J��A�T�s�C)Z|U�\�si�w*��8������U������p������,-�|��t��v=z�E#��'��:4h�7�<O�������V����4���z��P�#a��	g"� � � � � ��j�A�#�AO��j��_o(,\�����A����J�(k�G��qh����4+j�MI��-��6����s�.)������Z����
�V��|�IR�/��`,* �V�w�Cs������	����Z_���WsG�k�]�ZU������_~\�����Gk�eR���m���L���C��
j�����d9��|��ph��F-^i��:���A�m���vMLiZOk�
��Dm�}Muc����d�
}z��e����������� � � � � �@����q�& B6; T{��r���O����E���vYM����(��6=z��{W���;��.��`��|��M�����AT8�����s*V�{��W�l
j��ix��H��/I��~��A	.k�������	�&�Y � � � � �ttB� l���jC���������]�e�-� �)V�Uhe�6���4#I/�k�]�G@��! ��/a�� � � � � ���  $ l����*��DM�U	�H@xY��=n��U�������d��c
�������x�VL
���_NxIN���9�G���%FcTX��5i�z�.
�k�Oca�y%��Ns�_���0n�BC � � � � ��k�~���1��a+���T��m��/��{Q��������e�c��u��;N��K{>��{gU<7O��o[��)7�2���2Y�r����*��n��?���W�!UU�O+�9���]Z��_��Q����Q���h�]IZ|mMH���RMXj�?����L���h��O�6��.���.���}r�k��>]4'QdVf �+�����������h��P(��%F������T�e6����s�����W^�G��u��V�h^wU�����j�i$o�C���4�N��jV���Ue>�n����&=wMh�D�f�s � � � � �R�2���92�����K2��N���d���p]����P�aP�-B{t��z�h��GW������^1� �������O�I�KW�++����r���w7��T-����i7��D��!K�r��
%��+"�cv9z����K� �����]�Yo7����& l�;�"� � � � � �q� ���m��o��[�>Z�E�|��1���$��&m|�L�jU��� �B���Vh�"��{<I�]	�~�|�JZ�����`�=x�E�M�j���P�_K>>-y��'���u����f]�����2C�v�Os��5�;�z��.-Z�Qn 4
x\l��)M���@S�w�+������w6�(��j�w-�n�Y�;�yZ��5 � � � � � �^� d����:k� � � � � � �@�:Xa�9#k�����K��a�em�'"� � � � � �t���9�)1j�V�o�������
�T�{��=OD��%@�M�UK�lb�M:-��j���Z��b��4@@@@@����0��(�T�g��y*@@@@@@����)��U�v�_�&��<%N=<��)M�i�?��|"WN9�~@���6����SU&���W�6��PJV�?h���� � � � � � ��*�_���U��m�'F�md(,�8~i\���{����N����f*��/e8T��'�=��l������KT6�.�����1%Miw>���w�<�&.m6��U��^�����|	'"� � � � � �tL��lUe<�|	�F>�����w��r�?�p�`�����X#�1M�Nd�t�L�~M�wLU����:* �T������|�s?������ S�3d�r�,�{I�*��U�N~o�W�����{��iCd���,�����k��p � � � � � �@;0X���'W���7&;j(��?��dJw<�[k���������]C@@@@@@�T�����X������F��#e.<$���$�E�ag����<�/�u����S�2;��T����s�K9C�#�d(�"�Z���7}�|=���k���.��-��uR��To�@@@@@@ �f���G�;h@u��#�K�$��� �����EJ1f*���Uc�z����_����rW}<nM�^v_Y�,�7c��Gv��������$9
�T\�����K�4� � � � � � �@]��?�g����or����/��
�������/-R��S��}�~��������")����r��-�����S3d���Z����rd�"��V��{ok�:e��/.Q���_��i���e��@@@@@@����r��o��(�7[���-����P
���~�����
�No���������*�QX}�h���`�,&�6����"��~�.������R@@@@@8e�/?+��S������`!a��}��q�W � � � � � ��BaO*� l_�%�! l�� � � � � � ���&��*�;<i����*�;\�WK=�7�5!���f�n�7��so}���O����4&��]�[K�43]��7�+����#UZZ�V���j��=�4-!��l�S�P�N�������J�+���2����W,e�a�]�I�EQ������;�5�w��M������:����R��,tjE�O}R���{�f�k������P�6V����_�D�fe���4�������=Gu�R�u�Q+�+4���\�Ic���,������8-C�=z��J/Vx�i�jzf�I�>9t]�����z�,�n�7�H��IMs�������q � � � � � �������%FO��U]����1�2�Ua^q����R~��-�;����p�@lL�Uy�����R�;��sv:tE�����������&�8C��[�f��=u/����a���n5&��>�j&��Wyu�x�����.�n�Iy.o�����?]�&�
��Q�zw�_���6s���8@@@@@�%@@��B�Kl-������)�n7��F�z��h��lM��RW��icj��������@���J�>P��	i:�����m�=�3Y7&�d���<zrG�~nN��}���i�n��D_����3"A\�~������I���-�G���*4}�C��t��lk��M^b��������(A�����.���.���G�%h�� ��Z��H�Y���i�f
����r���rh}t�fd�v�%;Y�#c�v���z����$����s��8@@@@@�%�����R��~�����"U������\�wU���z"������,1�WX�������M�������}F�yF�&V�W���Ms��O���b����Gt������c���;iv$Xk���������
teA�r�
���hE�S�K����+�<��j�c�y�H@�:���06<��U�M?@������_y���c��T�N�zW-�j�C��8k�;�s3�8@@@@@�'�p@������]������=���#�A�XU\k�P��i���
b�������u���V��*�EU!�`�{�P���%Vc��WZ���*��<k����
�9��7�Q��P���>}u�x<�r	 � � � � � -��*	�YjO�v*�����`E��*������~]47�v�cs~����
H������wn���V��N��d��
����.as��s@@@@@@��
��%FKJ��#��{cm���MG����	QKg�'��J3��ji��x�%F�z��d]t��)�m�:wG����kvy�fT%������H�a8�s������gVN-J��Z�9�ah�QO��Zm�s-�@@@@@t��0�ad�����{*�%&�����`1H~�r�+���r-uE-*)��T}�Tirvg=���4�UR��sG��@�W�
�����e����g{�u�� [`V����ri�1���i����&W�eE�Fez|��Eo�	����y~=rZ�nL4�2�>�,(�u�]���Yv
m�X/��8u��%Z���{��'xm��JO8�b��zyR5�ak�U���E[���H�d[�9��w����-[�$MT�6������ � � � � ��8_~F��=e�&W��^���
�����-����{b��b�{�����������5�S��L�Iry�J�+.���U��a�>��1M���b���%�\j4��gk>�������%i�1t��U�~�8pLW����Qc�Z_a:1���
����8�^����.\� � � � � �M����/1C�6�����Z���$�5+���>������+tv��Jt��2�2mVM����R}�#�����QR��s�Z\�Rn X�Z4;����m��^{���U�j�['m�n
U$��� �=����*�X�
�/=Q�e$hp8�"�r�~��.�E�g3iL�Mwd$jxU�N�m�v{V?������z������l��4����49P8����0�R@@@@@8�� �a�I���|'oRW�;4dW�f��3�%�j������@@@@@@��t�
����KU������
��
j5�>o�
gV3�)�6M�=�$�Q����+���S�|�l��Y��������
4�*YH�]��O�{�V>�#� � � � � �'Y TA�-�7�������x�{�����
�G��\��:�L���7�M�:�>��������C��jwo�T\����m���N!� � � � � �F�
B*�6Y7��C-:=E���i�����y,ZA@@@@@N��.R���rxW�A�\z��M�S}�h����������5�K��]+���*�N�d�� � � � � � ������Y���2�+oV�����G�xGd��CN%���Z�;o�Q�#����<nI��D��`@��M�{��%[�=�����������I+ � � � � � ����3J�y�
�{B��@N����"U|�H������}�?�@��t�.�$���Y�D���d$��j�����/���_<'���$��e��`���P�G��'�J��}e}�u��])UT��M�B@@@@@@����,��������OI
�m5����o����
�_A��@@@@@@@��)�S9I.}^�|{�����h+��t�d4��i���2~�N�}����G���d��������O�����]��;�b-�l4���i�Hy�v�hO��������T��yi�xV�2��O�����q���'�O��x��=�{�����x��=�{������0�,��%K���+vz�{e4u���(<�j��mRA�������d+_��5��=S�!�e]�@�/��{�����K��w��I�����lY\�T��J�����
�o^\��@@@@@@�
t��v%��<���R[_����N@I0#}m��#�����-���r��]$�����s��j�*�i��s� ����
e���*��wy
v��]kC������[g�4'�
�C� � � � � � �@��$���-F9+6G�-��j���������L�DG-V�9G��VT�#K�by{�s�U���4N�j�0u��my!�m����~������tR��8=0Y:������F@@@@@�����%��s:Ja|�>�z�����O������zs7@@@@@@ Z �
W�;5� $ �G�"*[��E � � � � � pr:Xa|�"{v������E�zc�Od��a&MM�i�Z����+SB6��<���������^�S�W�qV}x�1xN�~��^��{;�:nk�h�.��Z����;�qK1����J��65x�.;=zi�O����g�����=���Zt�k]�e�����}}x����
��H2���f�q��?�z�>���d�D�vm�.����u^�q���&�o��~�?��|z��V�k}�?���F]6��9#���zcu�g�i�<�������k�B��9������7p����^���%���#��k�Q���]���/GH��x���9f��ZtiRK�@@@@@��a��~�Q�A�~���M1gR����1��Z�'P?X��X�|���oyu�^[F�:��9Y�/Z��5�����s�d������=�v��O������~/����#
?G����]�:������b����!a�
����1���H���P��B��>s8��*E IDATK7(���u�r�UkTB�����������:���_eR��W�v��S������o��_d�
@@@@@�.@@��B�OO�����m�E�z�!�������>f�W]{�u��&]�:?��������(��G'�uef(�:������h��z�&���e�-� �i��n8��1i�[����y�i�UE�����>���k��j�U	zbh8�+����n����N�_�\���8Tf6��sM� �W���C���N�Y�]g����5u�Q�_[7yt�
������Xw�b>s$��A������Fe[%U����� �v(�~�S�l��If�:�����z�Z����VI���jVf�!�a�v���L@@@@@�`( \��Q��+>��r���-K�y{�s�U���4�Cm�:^���P�Mg�G�z���S�����-������EW�C�H�����3#A���}��]���_��LU�( T�eG#wX�a��_`5��h������?���K�
���]��\��:��y������ �u����2�|��=zS���������>���Un�TP���`e��V���Um�3�Z
tl��D��i'l��x�^Sw�Q������K�zmt��p@8��//�{n\�8�!� � � � � �@;�	7�b�'�1��X|��x�c(,���z��&�{6"/ �:5p~5b8��4��`�@E���������{�����B�����3_��:<���|��|�����Y
IG�
�m����a`�c�6����2���]�6�n�`4� � � � � ��R�VF#��-�G� ����wO�j�������
�[g'��S��~���Ssw5�J���fP���[����
B�v�n�S � � � � ��[�������>U�<������ �uk�_���Q�������r�3����j��zK�:���i�vW/EYs~�J�Tk9�����;K��������SM��c��Ye���i[VnZ����
z��.��5�\g+C�f��%Fs'&�������&������� � � � � �m$��*#
�5P[���PA�"�n����~f��������u��O>���=����BU����]Zd5��)fM��GG�xt�[��Y/�d�s��O�v��d������.�����y��+ ���k-��ePB`;;�_�7�u�*��/�����w���s��h�"�2FZ4o�I����m��j���_��=�NJ�]
J	X�������oyu`�UoM0*%�s����z��f�	��O:p��~�����=k�6lN(���P��UN]���Y��5��Q]F���6��j���9�=-	���J� � � � � ��[���}e4*����<t��;e+���5_�I�5����f��|���oyu�^kF�:��9Y5_l���5��{_����:P+ ����Yf�<�������K�J~���K�n��-�A]��:�h}@�A�Lzt�EW����Q�L�e����J�����W]z�`�!��k������� � � � � �m%@a�r�-�)�Q��l�����������[��=������}�11L�c[^h�M�6�����^m*���L�z�Y�z<������gM�e��zz�G�����>����M�4��r������W�5�elw6���_�.1�l�Wo�K�FMj����*�HZ��f�b���n�����_���c��{}k}@8������`�Y7^h
Up6���r�g=�i`���m��Wg.����0�G�_������|z?0
�g��&}{�Q]#���\z���������1@@@@@hoT69 #K�����6�VW������8]�4��i�)��y��{�}8���-�o���%�p
 � � � � � �^:ha|8;���5��]�G������V}�/>,-j�����n����^e\b�k����4���|@@@@@�`�����_�b��A������0�������x'q�Nt@�im��]����;l�-�S����$�"n� � � � � �'B�
�&/1z�� ,������5���Y�o��&�I:����8���-n����o��QT�~��L�g;~<���x\� � � � � �A�
��'�9���ZPk����e���c���eM=(w����Y��w?����]���^��U�vi@@@@@@�h��7-�)%�#,1������A���/9��j��y��\"����q�e�d�Z'�i���HV����?t8.]2�$�x�����*6��K�4� � � � � � �@][�1����w�=�AR '���H@���K�/�/�\�$��u�L=/����e^��|���{�L�������s$��{���x]��s%��e��`����d��Ty�*d�����U�W����hY�\� � � � � � �@��"����t��2���y��b�,�~2{��'�g;����W23@@@@@@@�}
���&���C*_�JI�.��*_��]�Y���e��KU_m�k�Yz�V���Uvx�,��e,I��+�;�C����U�-�3f��r���6�d�������R~��e
r � � � � � �uL&����{��;
�G�OM���S����wPG{N]f�/��[����C���D��yY�_�O�}�
}����qJ��E���r}��-C/�XWY���Z��	� � � � � � �@�X/���#��6l�%N�no�{���6Yb4v>;M�=j��w��:�z����']t��{��.{/���$9�[�L��7^r������������r=5o��`���
�1�'_�����?��_SR��"m � � � � � �q��d���S< �:"�����7{����������T9�K�3B�;;���-��a<{�k��f�$ ����# lU�D@@@@@�x�o���= ��M�O���+	�=�N��O�a��@@@@@�p������id����o5���PX��/�|���[�o������E@8\�5�O����-�	�B�6@@@@@@ �5���0+j���o����v�v6��hU��}������"I���1���|�����s���4yu�����Q�����nxf����o�& �z�SO�:�7vV� ��)g���s��+�l����X��.�MW$����Z{�5����������X�e�R�]��_������W�o7����zr�-)p+c����k��m�����5�\�'�X��OgN=K�45�����%���G�Y����zi{�v8t���g\���b�1z��"���#�2�44v�zeeh�9��=��������a�����j�K�������tEE��;�E���H��z�W�=k��=������k�,���n��)]�;������>�'�����������te4S��@@@@@@��	��,1�2�����BO��+=v0�3��/�t	�6���a�E�
�4�1r�VMH�������YL��V�N-y{�~��]���l-�A����P@����^����9?|�-���ti�����67w��C�}������	�b�j�%gh����W�{%������sE�X�	y����(�#�e���9D�e�{����o�7�R���=H�u�nwT�T��SZs~����@@@@@@��
��%FcW:����Wk������&��\n��_��-���PUW���t�3>K��%*5�UVh�����-���3tc�$O���6=�.z����������O7�iZ+*�n�����������Q����y�%w�~��
�
�Ba�2zw����tE��V����+�&��>�&C�"���"���N}9�-	���S��v��q^�zf���>X�S3?.��������*���h����5sK������������h�����t
�%�SK�B�d�������8�Sz�~51G�s�`�J���[t@����$�N.*���y����xYw�M��������]z�k}}ez(n����q. � � � � ��o�F� ��}���-��p�w����-�#k<��=�P����	C����KR��}��Z����+*���yJ�j��������Qz�_�uGcbM��S�^���S����Grj�������:\�X��hTHE
�5����1��jFzK��f��H�fu+�R���i~T�`@XY�v��]��Q�������O7-���..S���"cp����-+����@���
��j�K�n����'�����
{m��[v���-5��{���uO���q
 � � � � ��w�
��!���#{�
����o�l���0j�$��f$k��]����v��S���\`J��A�l���z����y[�T8<l�*�ua���B���<nu^�Z��si��T%D�
3c�M�Y6*bk��X}��s�n���&,����b��
���	����9j���m��!� � � � � �@{����K���
���� �D�������Z�@�h���Z8&1��g��pe�����&6�lf�
�o��N�J����9�
4c�0���v�c�����
�������2��<��gM�=zjJ�u2��j��)a���3@@@@@@�5T6; �����`���{�\�S�����sk�����FM\��de���c���]ZVk9�F*�k�_��[���Jw����(�m1��Xa��^
3s����k�s�
�����w�r�f����s�����=�Z�3i$�����-���^��(�\kt�?O���%F����
�5���
��L�A@@@@@�C	t�
���z���c��9��L��Z�{�(��O����8K���T�7�
�@@�=��ih���}z���9�S4 )\	��j�����6�:,����K�E�U�Si~�^�x��uJ��}���.`�[�L����K�i���uq`��H�����W���)F�����z��}ZV�<e $kF?B{V���}�����e��Y����%z����G6�������|���[��p��+�i���E��
c8������t������T�W��c;�������P�B?G�~��N��OK'vQ�@8�;��j��d���������Bh��<�����g�[i�P ��j��R}��[���-���wMc � � � � ��G�Pa��w���C�B���9e� �NE1Gd��3����%FUY��~a�^t�>5#=A*r� V@�U��L�?�	�|���6M\U����6�Q�+�j=��M� �
K$e���U�9���~�Q�kc�������V|��_��4`��,�>;�pb�����4��c*��(�Zz_�F�# lo?z� � � � � �@�� l��������Z�U���S�Y4�w']=*S���5!�������]Ned���#����n��O��}gL���J����pH��u���zjvOS�i��������G�A��++C��������B��eM�G�l�vl����
��j����> M�_SBE����u���ti���Haw�*��w������q�����1��sj��=zlK�8A��u��Gg����:��h�6� ?diA�^�pDo~So�'k����bXg]�%<��q~�� � � � � ��O�
���a;�GM�N`�{��E�c���w������q � � � � � �.� l���b���D��Z����!?G����S�U�V�q1 � � � � ��K�TA��/$�U�o���������wHG�]A8\�;��H��5�=\�5���f[7�������']9L�E�P�Q�l�M^�D������T�]�Q?Q��]���K9
@@@@@h�5�����%�mq��B9^����.�zCaa  ����wPG� l1j{���J?L�������[�p�� l�W#� � � � � �@{�����*��}u�i2���5>���u��W��|U��^]�\ �v����:����%�G;�w���.�!� � � � � � -`���2�t�a{��0Ry���-va���WAxi_��\Q; 7U�>�@��{��S�3]����������i��+:��dLL����d�R*��O��&� � � � � � � PW���t%|���a��]�����qY�1��u���E�O��_�����d��V��^JL�q����W���WU��o����)?�����e�8%��"������My��J~_��`��G?%��*�e������_�~���.g���*@@@@@@�
��2
8S���/�-�����ey[�m�}��A��@@@@@@@��)#�c|�z���X�/%]t��9O�O���`�q�(����g�W��#S�2���
��{�i�2w�����{_^0a�"k������x��=�{�����x��=�{�����x��=�{�}�f��C���w@0�2���_�/edI�PEa{���I���H����2&^+�-��F
�e+��{Uu�C�woO8�2��e�����0E�K�r�]�>�Tz� � � � � � �@���nP��e()��x���$sb��j��0�2X�7��zp�M���F���4��@���q�U�v��?o�P��TU��z�D:+C��ml��-����b]�����:�
[K[i�un���zfPg}1����8@@@@@��0�����2����a�F����`����>�Q}���+��[e�����U������	����
����Q6uia+q���0n�4� � � � � �t��G����B@H@HaG���G@@@@@@��( \����������T��hkH\� � � � � �t��G����*_��Kf�������6��0����z~W���������i������iV�!�^��q����>]:p�LO�u+�����L�c���uI�!���}�������8�d�[�:�g�������W�=Z��\��wj�S�N�kJ�A��PJ��n���U��{�IYh�.)	��w�n�cU��v���$M�K+�|n�����ep�?�����J�ou���I�y.����o-������k�u��n��U��;+����.)6M?=Y�w3�����������L�[�
�KR���]�o`��N����{%���)�i��{�,!��P��l���C��s'��g$jp�m=^����_�9C�IM�����fQJu7��fV�n�����n�Q�\���;u�W�D@@@@@��-�+#d�p����������M��*6���������k��X����������.�V�I�o8L��n���zi�E��!�[/�+�3E���%�ax_��� IDAT��8�K�Nz�����*zuvW�>9���bPg�8W�C7�)����Mhu@�%��c�P�Y�1��Q]��,c�PM�Z���1:����/J���p@��d�G�3/5���;���Hjn��I�����N�Yu��ld\�����#��T��-��l�K��l��H�|�_&�������GU���Y2�	1.����n(�� ��mh�j�_Uh�<����Z�V��jEj+-E���hUPpD4BB�}��\g�d���,H�=����s����=���s}�7 � � � � ��C�! �T��'���-T9yul�nPA����+�]��3��jP�x�Te�Oo[��>Y����^C%�k''�����������i��
UyR$l�k�q����$�Y��x���R=\���.L������m)�
_Hg��yG'���������u�{��+cM�4�eg���l�w����p�]( t�wv������FC(����J��\���LM���>�����
�����y����cS5:|���Z��B/�2������Xal�����f��N��0�B��4{h����+��*��5��*m�KF�#��M#�g��c]d����%��[�������Y���5��L������A����5����P8����w�Y"� � � � � �@��������&���������T=tj���nQ����; ��rku�Go��Ti�i����~Yz��z/~����T�7F�o��X�����Nl��N+\-��
B��@@�v����nm���Vx�mI���������b4z5��go�F�W��G���C�;�?oB�&&��i�n�=B���q��L����n�^��Ku
-U�[���HU�k�CagtU`������?�����3G��U#^=�B����Gx�@����v�q
 � � � � � `	������=��i��L��N��'Y�s����������?)�}���:���6�����Ub|������*�v����YE��l)�k�g��j���J����j�����_?*�����I�W�H
��Z��e���8-�r�7������V�q � � � � ��T&0 �&�O��z����M�^��Hi)������k����J����r�5wP�F���rJ2b��Y�_�z�Hw:5��tM�q*�&e8��AY{C��� U�����}t]�e��*��2M����Ge��~.
vr9���1\	�J@@@@@�>	PA�������,��O����"����`@�u�������^zm@d���D�U��F��������UT��uu���>�.��b���BS���h��$=t~���n��o��M��Ocdn�1 ��:,[:����*�O?�� � � � � �R��U&��_V����W�Z�z��=����3{��n)�����oT(?* \�a�~��Kw�������+��\�|�����4�0����D�"1�F��V�zG�u��
B5T^7*K����$S�U�kk�|����=}z���z85]����$LS�en��U�����3j�X{	V���^���=���)i:3����.����Tju�K�vp��O����ao�����4��������k���^]����N����C��S���o���V���G�Q�Z��H7�r���34=�����=n�>.rk�=E�b-&��~�
@@@@@��W TA��@�vy�$GJ��0JJ�6=�P@��=��]�I�����z�[�J�������Ks��5eZ�t�?�SC]^mJoo@(E��}��iv����Bi�W%����� ����Ro�BY�|�����X6��I����1��+Y���CS#��S+j4��Jmj:@O���z�i���|^=�^�.����[�D�@@@@@��U�
��UJ�n�kg�^����jIiN����+�j�+z����e���Y<.��K�
����j�Z�I�V�T������i�Qm�T�������zeI�|V����;��s�L����_].E���-_Wj����l:*7YS��i��V���jC@h]5�^�:�]������3�*��k����3������jU>��bW[V��h�'M<�Z��D���0H����G�[�K��BN��%kR_�z�Q	�i�@@@@@�DPA��
�D?�kQ �~��!� � � � � ��[�
�V���#&@@�0JB@@@@@��K�U�5%#��2Q��������py���e:���/��B���0MC	�@@@@@"�P@��@�2�I����o����q��+ L��_V��T&u�F@���� � � � � �|�� �u�z�L�6�h�h��.������'������b��V�������� � � � � � p0��?�aK:�+{u���g����w�<���/�|?���e��/�~8X�����7�����-�����G@@@@@@��.�2�$���]Fy�e��Z�v��wj���������-��o���CI�&�y�e���ey>X%�!��<�|i�oe�1D�+R�{�J���<_m�zZ�@@@@@@�(��P����{�OeKK;xBV � � � � � �tM�}T��f�s��\�uo�7r��%�*���dw�v�	����������vx�������f�������B@@@@@@ "`���2Pv���|��_|&G���w����N��������b�
S��U�e����_����.��|K>�w���@�gQ��7����rW�gQ!� � � � � � �m�z]!�"W5+���C')i�iq�K���4P�m��>{�s�}���w�����>���T���i��������� � � � � � ��[�0��f�N5+�|p�������Fb�q���S�v��^����[<������� � � � � � ��4���\J@H@����) � � � � � ��N��o�Rd3���kTT� �]A8X�����@� �v��	#� � � � � �����U��J9t��&�.#5Y���1^��A�������i�5���:�t�_T�e�<�sLO]s���yfR����b��
O�k������N{����5f\���i��uz�ke����k�����h�WO��P�XQ�o��/��y?��a�&����,��'^�������i���<������C�z���h��i�)���=Z��R}Y���������!� � � � � �t5*YA��d;�����Y;u�oj�g��Xa8p{5�2�����=Lc�����u��I��O�R4{��jk|���t�75��<n�-�i�\�Qj������`S�y��|���Y��(<�]:��R�Fm�8{�p����;�A@@@@@��0n@x���G%^Qk%n��Hh7���^�Gc���[��b��i��������(TX+ �� +���������!
c�Z:<Ks����'%�e�*������������i���R������<C��J*��#?�������jx����}�3]�D����f�KQ�un��}z�f?��uK������@��������z���H(��@@@@@@�+
t�
�x��=	���QQ����'n�H���s��s���m��UG��C�y����\����+[=RQ]�����~1H�_������h���jnF?�����J��ad��^9J�ZA`�����wo�����U�Z�'n���y�B?�Y�S�u���@@@@@@	4� <MFj�L�:�}�[��:~~���0V{�
�u������f����VO��R��l��4L9s���4\7 l��5�����is�����T7��C���E��G�k�
C@@@@@������i�����g���|c�f�}�f{�6�]����Y��W��Wd��l�\V��a"aW�^17@@@@@@��
�� l_��p��?��W����t�#�z|�a�; ���
�����#��1 �������$�c�RA�e��L@@@@@�#�M+�<;������k��$e�$������u������������N�y��������4���5s�=x���j���P)������z���t��d];'O����>�!owQ�>Z_�
���9�)��01+�Q@@@@@@��'��=��|�]�()y�l�1R�h�y�Q�%��s��B����8��Y]����1X����9�eT{���?���yRvO-X�O�4FU��B]>5_�04M�7Uk�~	%�T���i�������C��D���` � � � � � �@�*'*i��2R���l�1���������~�-��i�3���"��f��f'k��l�4���gE�&F@(�����_��m���1�:���t�M�N���+ ������������&�$��>;KW��S�KS�:�
��.HFC@@@@@��@7� l�8����� ��x�f�O�K � � � � � � �F���)�"����bW�o�nUAH@��U�� � � � � � ��M�V��u���� �o��!� � � � � �t��a�=����*{�]d�0
@@@@@@��-@���*92]���=I[�I�-�"��5�����v�'��@@@@@@J�4����w�:QI�
��1����+�@����s��
�)n�0\O��*��"�sP.n@@@@@@�{
8S���;N5+�O����%o�S[�V��y?Y������9�����I��S�������4e��+���e;\����.{Q~��.	��@@@@@@��C�����
y���!K@@@@@@��@�TFn�W�V�N���x���)eK>:x\��MRA~�cV � � � � � ���]�1�d�����/�D���)�G�H��NFI���dDM�T"�����;��i��;���u����R���s8@@@@@@�� `\p�j���b�|��2G��gF������c�uJa��k�w�����6=�$�4�Vll�9]���=���n�~�p���eg��@@@@@@!�r����b�}�=���>�+W�eE�	}��"�|~Y��L�?G���m�y]��.�X� � � � � � �Y���U����m& l��"� � � � � �t%�nF� ��*������
B*����� � � � � � ��.�F� tI2$u<K�8��a�[�F����{��G��[�G9�z�����G&5{���{���"���N���:g`�F�w����j�M�hD;�@�_��K�N;L9_��O��Z�WG�G�G���r�
��
��z���z��NkK��csze���s4{H�rl
���Vno�<+O�F_��"w���0_�Vh[�S�e�'G���{���!z�Xgp��yL?J��G_�Z<�M��O���S9Q�k��b��^�]���������=�n0��G��Z��PO��H��G�ku���������yQ��Q�G�n��}���qY���{�@@@@@@�� L���0)'����P���r�����cB������M�w�
c>�����I������i���ctk$��V���O�����r��svf}�
��uDiWy�{�����6�bo�X�M��>gr����}F�ehg~e��
�I����t���C@w�n�7<l��"� � � � � �	����#����N��	�vY��E�����\j�^�����X^������r���1}uiv���]S�g��\�t���Z�����������-�����r��9Z�#!_k[�����.�{���?������
�������<D�������4��R�<�x=9�y����q@k��L?�P�t���������~���P�<��
����~}��Y9a�9�j��z�WT��Q�,�56O��M�+�Xp��-��~���Q}��_+_����z���a�@@@@@@��@7� L�I����m4)������y-�m�>�������iG��)��OwM�����Z��Mz����Q����l�6�U7�
�/�7g�W��*��o���J*(���`5bJ�9�m������n�#�h�)im6V���G���������pT;+C�Iz��#49���vm�Z'���3��s���}n��p��7�m��������)���i��zfp{���sq � � � � � �Z���gHF����u�q�k������&"�w��?����g-���J�,*<���
5��|���=i��"�`��}�����0FX/���0�����j���
�p���C��i}5�.��
Sc���=ax@@@@@@`�T��
B�7�:��R�~���#��0X=�hh�,��o+?�-;�t����������a����J���h������5�w�v�m�1���������:r � � � � � ��Tv���v�G���i�����+7������
h��ak�D�����^�D7�E���,���o�����QMZ�����]���R/�#h�UA�}���.F�����O���S9��wM�?o�V����g�uv�Y�Ew��U�Uk�K51[����q � � � � � ��TvB���=^w�OR+|
���������j������3�C
Ug�C����gi�S�����k�vkeyG� ����ks������j���t�{:��AZ5,�_^��V<�E�f��?'��V��@@����������=�����a��4�:�(�9�)�*/����}�;�{49jB����v+w��Zrj�rl���ROP��U�0* �����<�����aU����UZ�W7{5rl�`����0�V���T�
��'�����e|@@@@@@�����MS2$�J����X5�PA�9���������O=z��m�q�����P�T����,�r���US�+��^1���i�����������) ���{t���Zga7
�*�g����&�H�����*�/vh��KUs�(�V����5I?�Z���Wib;���� � � � � �$@�q@x�dX�T���1�QRb��}��"�tF����\P�v��05Y�����Q}4����~y����|-�Z�m5N
������WC�7i��
��yf���(��
<R�4�hP�fW+F�3�k��|�jS�����ub��u�����[@hM�;"�� IDAT��X����;����������+������[���u*���	N���6��$ �]R��|\�gwVj[�BF�{k��,��3��6 �W+_�D?���sZ�}�.M�Rf4@@@@@@��TvBa�=[�<$y��K��F[��ym9�:6��m��c�>S��]�]c��>9&� � � � � �4PA�	���~m����|�T�g���}��b	��������c^��]Sifvd@@@@@@���YaD!��cwV�����?��I��x�Q����8�	6;5�����9���������r�8l�{�5N�v���r��m���EG����uDm8@@@@@������o
��������
wm��[>����:ZZ���ei��d;_T����j��|
�8T������cq � � � � � �����0�<�K [�<��N�[�fb��d�����q � � � � � �Z�=m�����rO�#[�3$#YR�\�}�[�q"w���;���W"��?�[�J���-�(%e����J^O���@@@@@@@��q� ��s�*6�o��5�~���eLo����d�>��p�*)w������&��/J�;�y � � � � � � p@��O�q��Tl>��� �8 � � � � � ����
B�j����[�/3��@@@@@@����a�����R��(_���rV��?AfFZb��x�������d�^��()Yc��1����~����U���2����'"� � � � � � ���CYg\��tS���W��g2�L����D�p���T��ne�}B�����_����
Bo�7*~�!y�����+�U���\���KS��g��@@@@@@����G����,}�(* Ll��{�*6������
�z��1����2}���1��G���"-l�y������:��w0 ����?�j���u~��\����iu��������u�	)�~�K��Nd�5t ���Wk��Z���S����d�������?y?���!� � � � � ��D�0�>�.����*��k�J��?o�U�n�N� �W��c���+5kY��U�9�W��0K�����/�����hyI����4��^�>/zq���q@@@@@@����ta<��^�m�<���pa"����:XA���F�XQ��)��*�F�����.�O
o�2�V������*����k24�����0������W��a#�4gT��e��S�������N��)Z�������ax@@@@@@��	��en{<\A8QfF���}���� ��A�+����?0��	�j5wQ���S���(/�������T��\-8�.�!�[u�Mk�Vk��:}Ue���34kl������^��7x��+�����k��TM?5M��D�����Z�6�������W��=�Owh�	��;6YyQClyw����k�%}4�xk�M_�6�-��7=���C4o`���OK4~�4V�������j�vo@yy��v~�����'i����?5Z��u_6�?6U����I�*+����e5vv��$W��Z�i�k�=�����l<�����F�a� �]V��k���K����Z����s25��8��M-Y[���)_)Z0������s�pXS��V��G��dj�����~8�. � � � � �t_�n\A�4Ql���� ���\/�V��>Z<*J�������<':8�~���]������/v@�q�w��f�E��9�gkZn���^v����c�X���������������G�US�yJ���^Z=%Y���Gy����@��&k~��.�5�P�f]�[����o@k_��Y�j��,�O�����:CKo��o��e+�H����%l�ti�����a�<�F�����Z��%��GsKb���h�#��������@@@@@@���� l_��p�
��� l������g��2/RI'��qo]�gj��B�.I��ehXRd�H@��%g���a.�Y��Z��D�������h��V�����:-\Z�U����3��Ze���Y�sr��5n=��X�3���dh�5��:��}�V������o ��_��h���sC�\����k��t����%S���4mE���WTJ
�e������kd�eheA��*���^z��de���~�W�zj���+5�Nj���i��� �M������X�4�������pk�K�ZX�U�6n��P���N�_��Y�*!C���}�]��"� � � � � �@�������ga�B�@�?�W���k��~-|S�3��&eE�B��s������>��leq�^�����>U�}Z��]��^z{��Y��[�n�8���P�I/
�}����R�n��	������U
*r�Qc��k���o.��v-�E�Nn����*�����*4��n���OT5f���������>�)^�	����V
 ���Uc]1��&fu3
 � � � � � �\��{�_�u{�{|��d�)5Ta5?�T[?�3c\��aT��������������p%���V_�����~�}�T��{b�5�3�ma�*�W�c���[�Fn1���
�H�cKKr������&c7>3T-h����tI/)TU��@��{� � � � � � �@��VA�����\( �K�c������|[?v�~����
�F�[+����p��4/-��_�0���R��������cR5����$�\�X�cB�"�C{&: �?{&: ��P�;����f��g
4OYZuU��#/@@@@@@`?
t�
�7�1���V�Z�>�+Ksz�h������[� �����GKGY�6?���oV�V�.�h@(����q�!�G�{��K.?D�Z{'J����Y�Q��-S#-?�P�]������F'�l���i��-�Uv0���X�y��?��Q���R4�b��y3{+�Z~����z��_���_f��K
��T�GY��
|� � � � � �$R�m���rC�_d�����H� e���'=5>�������V��_8�sgj�Ei�
�n-~�T�ph������b�����IS�h�	�`@���i��UZ�N��iOj����Pu���Y-SG�����.�O
����U�+��}���2���
|m	#v_
������e����=Z��_'^��A���z��{�0_6"KsF�hXVx>���
�_]���4-�i�N
�����D��O����&+�&���Z�N���VQt�hd���t�����}�
M+�5UT����<r
����x���p$��N��HizlV�>���}�8@@@@@hY��UF� ��"�������� l�/�
.X���%��5��p�`�����zj���p;�p��y��W���6����?�hIU����4,����Zd~�f-������L�W��0K������PR���(&K���
�|VV���K�$?��IO���zjZnt�f��������+����&�d�{���Jm�y�������� ��M�.,�cU����F~�@@@@@@��-�< �$3��������o�8���T����=�7��� l��D�p�r���Vk����j��Z8���6���9����m,���Ru����6��63N;��:��J�V}�SQ�$]rB���pj������A�:�Vo���[��:�/�����u�	)�~�+X��jk@h�[��NK����/���
#�{���'&kt�&�ns�Vm���>E���#zj��)�K���j<Z�J���4�����+��X{MV��dm�V}]|FJwh��TM���y�*������S��o84of�V�.��?\@@@@@*�Vv�ha�����0�����+���3�xJ��EZ�v�{��U�V]��&�2�c" � � � � � p	PAX]���t�~|����m�Y�ioz
?��C���&���3��G-����*4��C4o`���0�� � � � � � �%�aab����=�����
��!���jQ:mB/�9�@r���Q��7���Y�8�H\@@@@@rB���+�n��N�����}��4�~� �@@@@@@�����@{���5� � � � � � ��A!`��c.���E�����2QfFZB����A�V��'d�z�{l���
S2�0����5�*_�D������q" � � � � � ��E ��QJ?�P�[U��F�S&E�����.~@������5�	�+�U���T�iu��f@@@@@@@��@����9��d�|��*;' �*�y � � � � � � �@�@�U/���2�wJ~� � � � � � �|�CF������|����P���d:N�+%���Z����J����C�F�A�M��7����%OM�&�� � � � � � �ty�S(���J�}H��R:c�LW������oi��G��U�?��w��v��9�E_�|�$�z���]��
���K�t��1%@@@@@@��&`(p�E*�eyT@��
�_'��B5��/y&�n7P�TV�����2�`~@��4��l*>�JR;��������������Nz�����������8���!�f
1�c�<FF@@@@@H����Ur��N� ���f���������.VAXUD@�����75����V�9������ir��.LFC@@@@@���ab+3��E�o�S�w�?��v�D������>q_�������;TA�����Zi��'z�Z�0���O44 �>����}`��u�
�lz����:qQ�@@@@@@�L�����b( �Tv( \c������*[�������:���/4td����)��������II����s���6��VS���$�n?��Pk��+V@�sK@w|,m��Td��Cm�5���>�<�6��Z�6�����u���djg�4��~s��#���nz/��o��l�]:.6��w:�-SW_j��B�����������FD�i{�=k�nS��gj���}
8����6]�;j�|S���iv]�3���z�KICl�x��s�n�������<���	��\��������1 � � � � �t/*[���5���/:�YSG������}	=��y:���4���7RN�T�t�4������X����?�����g-�=�#KU0F�z�M/�a����7~bhh�u]k������>��PN;���t�
����z�M���/Gih������Iz��st���3�������l@@@@@@��
PA�R@��n�*�P%���i6���V����R�^�������B���O74{��#��|��ot��������=6t|k[��+M��,�e�m���p@�BI���t�I����Z�,�{�
�<����6u�z�����������a[C�`�*KM]�h@�����������������B�4t�D��;\r5�x�����{6�u��~M/�G���o�#� � � � � ��D�
���6�������gI��%��t�.k��2n��3u�oz8*��wly��>5��wR������j;�������l��h��C��t�K��f�tY����6]��yni_@��S��4����;?	���%��4�j�g_c����
��cmz�������v-�[�M��L@@@@@H�@7� |�������0A�V�L@��������j�������`%��j�j���W_
���l���%j�����"* �O��[�FT���2jB�J3�+X-�����i�C
��(�
 ��a@@@@@@��	����P��7���nU>p�����;�3JJ��0��.>�������kF����6�vt�grR��f�0���U������=F������������j��c"{+&x
1 � � � � � ���aaD8RI����Z�.�i���2uC�~��k"na�����6�12�����U~3[����a����[
i�1������@n@�N7����5FCk�@�������e����W+*��^�`3���O4u�s��?����n��d� � � � � � �@'	���0�[������.��0r#m��W����~l�����us�����iDR���~n�^��������JS��=_���f���/7u��v�a��������4���cj��
�I��l����9����c��C������nx2 �e��16�}���}����}`��u�
�lzc�����yN>��?�$Y�0��z�?��ER���{nd������/�����^�H11<v���`3'M*<����0td'}�@@@@@�n���0�������UA����
�v~��+��������O
�G��Q�00PC?+$��q�M+�hh�������?��MNN34"����	�����������)�Z2��	V
y��gNOI�Ra��R
��*��b��s
��>ke@�p��������}9�/ � � � � � �-�A�b���Pad����}�SA��x$�
�;On�Y�Z�tg/75l�]KN
� 4t����M�/���3t��6�4 \=�n�v����x5�e_H�=
]}���N6��%�nP���T����l����
(�������rl������Y��_J9}]x��_+����V6	�����zb��e_��f����
44y��	����6 �����m�_3[���3�Y�� � � � � �tQ�n^AI�%���<na�=��{v�%?r���
h�I6�9�P���L�Z�g��Gzz�M�i������z � � � � � �@Wh�����[p��[� l���������" �TB6i���iv���<���\�-�����~��=��@@@@@@���@7� L�c�������w�s�%��F��tm����3���_7����h��1Qc3 � � � � � ���8u�>��K�~Ek��
L��K@C/���m9�c@@@@@@��C����|������.@@@@@@�8�B��B��R:c�LWrB]���
�j~=K�	��=�QR��)Y�F^�:�wu����2?�w�'�� � � � � � �t���;�x���w�>���qQa�7����e�r�j���<c�hw�g���a�(���B������b��fD@@@�}�L IDAT@@@����3ey�<;?W��uZa�����T����5��w����XMM>o,�O��S��#Z�����
u~�}���A��G~�}���`�~������#���>�������}����G~�}��1�@��}Hx�QU#c�V�'�Ke|�����;��D@@@@@@���a�<2G�������7�>�P��?FJnyB��T���I6�|��P�3/���+�����mv���7dj�u����"��hKTa$I���FU����*���q�32�j�51NB@@@@@@���~~�j��+��W�5�\9��!%��t���%�c���	����	*^�C6G���
��s{��\;���Erg\%3�Tv{����n�T��Z&��M�m��.���yz�w��9z�����c��JO����%6���.W��@�Z)�*�s��W<��Yr6 � � � � ��I�0Ts��Y�Y����1��+��������a =Y���Qa���S�o�7��'�R����*�}^��+���
e���%eijK���}����]���z�jF������t�-E7�]��������4����������P��������# ]W�B������[@@@@@@���9Q5��U����J������t�*L;Fr��_A�����u�H��t�
�gWvtv��0v@X?-[��8�4����F�v���T@@@@@��5wNR��>mU����acW}@8M�>����6�jd���{��U]��+������>��Wh���a��z���W�������V��Zk�>.� � � � � ���H�qa�-FC��w���pn3��Y��B�
��vEL��! ��b���Q��pk�)�.]bO��K�)�����"{�n��������3�gK�{�n�9�#���&S��j=����f@��7;2t���i@k�U���
���Su�*-j�����������O�5
G�ug��D�P�������_�gi�����|}Z���}a��������G�� �T6C<��.�����LV��BP�|>����d-I��)�����=��&U�������B�>�wk�P������z��W�Y���Z�x�C6
�%�6G��6y.��V�����f�L����������p�k1V���a`K��n
��j�3[�lM��Oz�u�-K{�Fk=a_`B@@@@@�vPA���tk��LKcE�����L�'3��[k�*�B��W�i��d3>5��������7=.|�Fa�;�T-wf��H��F���#�	���+:���h^��6���M�����t�g�2-�G��p@hK���5��'b��o��1��Ck�Q��?���[kw�bG�\c�u
��������U8$,�kT�����k�����a�]���D�b��u����6-F@@@@@�N��pn�
��R
�I$e�W������:=`&iI8<j{���^����@��KJ���
��V�-U�q�k�a(Y�v�+4�W�S�Zb��+���e�����J7S�f�n�T4� ��b�����T����l������sd��]����Z�+�D�t������3�#P��y�^AXgz��_�[��p����*�p@(��82u�=)*����K4��o<G����V��;��<��~�j5�t���Y�K<�`K�"�UqJS-�y�r=jD�K�y��3e�=gj��Z������cd�f���Z���Zsjq-���p_�n��T�]L����"BS+|��2����"����� � � � � � �n*cT�B8�nv��}�-�>�`@T�W^m��`�J������3�8���m� �������TA����V�~���iZ�F{��C�_9s5/�6�rx��|Qf����Kn1{
7������j�
��W$������F����t��T�1"�J[���C��]K�2��K>�{�9���[�f���&3l���.a$@��Gd8TU}���&ND@@@@@�N�F����U�d�����FU���>/g�
�`�Z�U����)���%�$
��w���pG�R3�5z?��k�64�8MC�x!�u�X�����T�Z��4V�Yd�-���Z���te���z��h��42����X�)n����c]�Z�{����_p�8u�-Ym���[���M@�H�`�^JJ�I��Bg�P��~�@@@@@�(�8 <CJv���lE{�|������0U�v��-���
��s{��������u�H���vxl{�g��Xa�^FU�����3������pkM��5�G�|*�]3����pUaK���5�4����6�z�P�8� ;+ lhG��yv���\�����x@��5�4�j�w��0�d��L��<z?�n���%��
�}��P���EIY�Z��3
&P�o�nt���`���b4���7X���6��`��"[Omq$E��M����@@@@@@�����0r��J����ra,�p�Jk����jq��*�b������=����Uf�<��g�J�4�q�7	����[�eZ���������
���
�np�jP�G���h�~��25��PP��K��`u\[_��*�����[p6���*T�o��L�>������I�%�����@�A�UdMfj���1^o�y����@@@@@@��� <W�qVar������UF�:�y�#�������G���������H�T�C��'��"o�~��_A�\��u����"���b�tk��Lk�=����������j-
8U��> 4��W��~i����;�U�u�WK�U�����~�B���^���k���~�4��]��<�[;��UK�
�o�~���m6��e���h��R�����=;J[�%��k�=S��PkO����
��{�7S/��t�_:��C����m@C�h���+
������?:Rt�aU_�����~��H�0y����C��:�f>�Ug�j��B;�ak��L�~�)�"#I�rd�6[dm�������Z�0�Cw-Z��lc�;����������������2�� � � � � ���f����(m���
��-F��=g�Tgo-
��H/G{C7G8�j���M�l6���3 ���:���ki����]�L�����Oe�}mN�
x�~�B��d�{���k�%��S�@@�h��&���Fam��yQ.;���SA��T$�Wd�:�H�gM1�V�+I-_��(�
^6f6_���-����k������?J� � � � � ��+; �����
�|��j����g
����
����dv������~� ���Vj�L���$�F�\����#�`�V�5��_�E�q�SS��Z�I�V�*5/����Q��jO�0�\��j@�L��<_��X�"N����f�M+�%��I*7kt�/4�\��K����nhQ�c[� �l����e�fO��%Bk�>������7u��*��/T�cSt����������-	x�%X��[��X�F(+7�Z�����;x��Z�%�F{��D��6����M��l�����;PA���7^������WW��RRz���v���#� � � � � �@��s�gE �@�B�(.���GRC����� � � � � � �YTv��b��D�<P�C�^���[7�]� y��& � � � � �@w���'7�j��A����Z��/��v�%=X}��}���[��)S�;S�f���eN � � � � �  RA�����5:�S�����F�`�!9@@@@@��'pP���?'�����6WB@@@@@@�������?�k��r�;]JN�;#�7;�zp~����TQZ?9�J*���F�����������~ 3�,vG���()YmJV���D�kTU����}X$��w�5)NB@@@@@@�;	N��
Q����BW���VX���>&����m�P�n�Lg���
��s{���p��o'�~��/���v{��=���u+L�+������(9���O�N��� � ������(�������'lD��E@E(
���T+�_l]�����nAm�*�V�|������UA#D�$$�,����gIf�I2��%��b2�>�9�{���\�s@@@@���g����r�~��7�&UAh���f�l+^V�����q����2[�V����d�W�����;��U�H?�S��#��Q�}�}�}�~��<O<O<O<O�x��=�{�����x��=�{����{�=�.���[����z��2�%��r��2�~)y�HA���2@@@@@@�A�$��+�u��%��k+�e�!�y�L�-������5;${�,y}d��,K�*�L�[�Z�>������'�9��`���@3s����O� �a��hb����O7��b9�;E&P�����C���2k[50B@@@@@@��;�'w�G��eJ��Be�qf$ l:�n\&�WO�M���x�3I�?>�����|�B�c.���#���TzJ�|�2u�+G��"a��^�T��-��P�-�O���e2���u����:�i��l]�����@@@@@@��P�$�i�*��E�q�V������t���H?�Be��H@�F$ �U��T���P����)���m� l�Z��O��	��>�O9N|/���i��
�����������i�6�����vhnJ+*���M�wkj�wE�����Ec�������yZl>�t�C]t���$`7�E@@@@@�v�������s�`�v����F�wv�r��T�s�<_E�p���C��f�%{��RZZ��7���n� �>����yw����;T���b����p��>�@@@@@HV�q@�����m����*}��	������V&;����� .�_
��'h� ��o�r�{��k�W��Ti��6���a$�����Dj�V&����2�@@@@@HM > ��A���p@����t6J��������|Y�	*��r�H)bt<-����
�����u�U*����+��"a�����<����=*�C�^���.�j�Z�T������-R���:wb��������N��>[�ozU�]@��i��4M����y�m+��u�"����S��:j�C�_����-������]z�?~m��k�9MaUn���������7�������s���j@]���u�,I����
�����6��AW�5�y������O�&W��#�Ut���["6���@@@@@@`h���q��>K�K�������������M��6}�ZA:��Z��\#�.W����G�4r�W���!F����i�5�����-UZ�t[��R��*����������Gst}�0��7�4��D�b��������z�n�1����#��WH�����������4����`@@@@@@�C	t�
���) �"�q��,1�^��h���*��j�L�f��j��\]s�qh���m�f]`SA4����6'��Wei�%6u�K*�����5�Y����I��c%���(���)��'�o>ui�F�]f
U0^|A�V����i�3V�wk�_����h8��kw�Ze���4
�)���~-�V��e�����*5�n��G�5-,�������;"���j@h����@[YA�R�������f�}>[��^����,���*\����e� � � � � � �@�
�as{6a=tz�^����������\�O7*
c?�����Us� �+�W�&Vj���>��nQ�y�4�f8�3k��,��Yr����5��n��+256;���g�����g�����R�	>�c�fv�f�0�����F�m����T�O$TU]���!� � � � � �I�UK��r��IS����qW��������tM;>6H���0�'a����z��]��f�����;��<�J����k)�k��,m�/�����+j�RR��6
<���C�{�5\�h|b�5=���GY5�d��j����5^;L@�H����z����K��BK�P�#=��@@@@@h����R��s�����(�K=��w� l��������U����/�Z��O����Z��,���L��7_xT�__����.�&�%WsG���p��I^��E=�z�j�7�V�fw��#"{ .w��[�K�uQ�\�Cw��F���k5�/�5�d_�Rs�99Zu�-R���} � � � � � �S�
�������|�`t�������..1���c�Z���V���pu\�����h�U���rUxI���HoN�f]U�������?�*���gb���Z�P��>���c� L*�G@�TX)��M���]��|�Wc��7�T�h� � � � � ��OT6�������^��B��g+4lN@#��������=�}z��jM{�����r]��4��,���n�	��O]����E��z���������ihOS�*�T��^->�a����FHv�W�/uh��f�F�D��nM�T��H@��	�
��u�IdG���k��r]��> T�G�����.6]s��fV�%|����5y�[EG������*u�[�o��������6�o�Z��
�{����`���)f. � � � � � �@
��0��%h��t�|*����2�%����c7�q{�5S�qYZ~�C��ouk�Uz�4�����<��a��*�����	9"{*\&j���E���
�-���Y������Cl��V��P��?�~��h��<���<�6�o�\� �8�dc��Q1�j�m�� � � � � � �@�  ���q{�����Z�}���[����u��4]�M��*�������6l��5��*��oX��G�r������j��4�o��C�aX�z����K{B�5�����+�4�K��r������o{�tM@�YCG�u���v|}8=s��6�3f>�ju��.-2�6�?9K�&Z����������-�}�&�L�K/�n9��d3x@@@@@@�	��!�K������������i��@@@@@@Z/@@H@���g>���*��_����#���24@@@@@@`��[nO�.�E���k��U���qO��s � � � � � ���!�>~��bx�k5rr������
���@@@@@@ ,p@��g��>�C�@@@@@@�i�*��+���q��2ef49�@��-����iG�U�c�*s����
��9_VG�j_zPe�C�;��R��?���[ek*+[��j��Z������i���"�?(�����tI��Z5(B@@@@@@�C	�����E������8c�L�F��8������b�tdB� IDAT�~V7MSf����l�g
����@����`�����)��RZZ��=SY��F2�����
��X��������?$�e���Wd�X���9@@@@@@�}Y �5L�|��[>P�eYR��|�by�/����e�����B�>^���lTA�[��*O��Lezz��k��� lm�!���r?��R����������y������x��=�{�����x��=�{������h�
Bw��*�d�s�0��y�������+��U
���P��!� � � � � � �:�I��&�:^U���V'Go)�H�������e��(h��7��
����������.��w�,���kB����@���2u��)��=�;kd�����������E���
�+_��{g��%�����9@@@@@@�}]���T��-g��:����Ni1 �(�-�gm���CTm����)��V����=�w�o}W�����������rT[���A7+}��p@������*��/U
�sk��m�sB���H�����_K�����uWf*�sA@@@@@�$`���`����a]&�Zk�PZ��Fgt[F����������#R�_����@����0��|�k��{}�J����e�[	#![�7�/]�����v��	>����%�'�|���iq\�f�JW���wUk�
%Z�d�	�>�����j.��������k�K� � � � � � ��>%P�o�J��6� L��9���V�n������O]@xd�r�����w���O����{��a�
�P@�f��8P�6�?����2S�z)�|@X7��yZv]��������]�:C@@@@@R��7H��
IU��wZ��:Ji��A�R�	�KT�K]���Dw��k�5�u� s�3i��=6�O�GK��i�[n��v�ji��4�U�����������,�@@@@@�S�+�=�
���Y;7���
��g	������.]�������W6�B��Q��7��0v��*��X����j}��tg�.;3GS���-E�c�N�[ =��.��S�_tjI�_�g�i���Z��%C��Mv��>-����h��n��Y5dx��9=[�{���,1�h>������K~6V��P����@3�F��B��.�M��[��N�{�JOl�)�W�.��Y3����,����j���|����~��U���~�^�;O#m
.��J���.
����?�=C���@@@@@@R�G(h6��L��9���PAh,1z�L9�M���y����!�H8��{����Ri���]�%���L����q(�rg8\�id��
��������?y�Hg����:4����w����|�~�K/��y���*�3������{hF_����!��P���]���B��
��Lx��H{@@@@@@`t�
��P0�4&�{�*���V�y�����v�������������:��n:��\������'��H�3z��~�p,Z}��/G�.�������PJ}�Q��Z���C���S���G ������h������;�uY~��u��34sr'M=�*�Y�XW������sz�lB$&M9 4���R��T6�Gs�z4�O[uKn}�����&�j������{��3��{���t � � � � � �@�� � �QA���q��>�M�� �~���w*������t�U���Y�������/����J�jB�;'��/1�Mvi�y��P.��Vol����S��H���|m�yzd9�f������+���b�m&�lv���eGC������71{9v��P*z��}�	��x���A���'�C@@@@@@�=,�+��a�?S� ��V
9!]�4WS�Y��D�.M;X�
�����HP��_]@�W0�Z7����Mz������[0��0]�����
�m]a����.T�u��P�j��up����n�tyF���{���t � � � � � �@���p�[	��{	��P@�^W�As*�Ay�wB�Fv1+-�,�9���-1��X�s�.W�I]���L�cg��H����eG�p}Z��bM/��wn�������cv���	"�
������Ec���J�c��m���� � � � � � ��^��p��	���a�i��zIz�K�6WA� 8kzi������V������2�~�=y4��������K�L�-1�h|�*��\�pw�F��G+��M���������ql;�	���ks�F�i��_�C�m��)?��{���� � � � � � �@� lK�zc�����I�]�M3O�+�,�w�4����J�Sm����P.'W/\���Y�Wu��%���K�����d
��*�
WVj��U*<"f����6xFC��	=��9�H������h�����.���,yZ���9K��t�����
u��<�o��'
�����j��
�w�cfj`Vx�Iwi�f?Z�9���������hYc�x����.�����mat�����GM��+�ty/=3<v��6<}� � � � � ���VA�6!w��
l���A�J��=#K�BI���~w��T4����-Z}'=���0���?���p[[�n�}�6<G�CCr���{�Bc�O^��3^���O
�_��4l�S;��eQA�T�����Pn���u�����z�7�h�����c��; R�% l���S��;+�������mw$G#� � � � � �{^�����q���Z�b�~^��RA�,]vf��kSn�5m���(��j��]�����EG����_���#���-����*�v�;t�I9��4�V/*�t�% 4�����i�����z���FH�������J����#eh��\�^Y�c��- 4N����
=�q��n�I2����Nk�&��e�tsa�>�agh�/���^m��7��U�h��;UxVw�:/-~)�=��rF@@@@@@�M�	�6�r�~&�^W�As�49f���l�L@@@@@ ���b��*����+c���=�����T[h���5��Nz��
�@@@@@��M���7�s�l��6���<@;�(��V���
<2S9k�������`�e�1��4>��{����m�$�a�N������*M��T������� � � � � � ��� d���|�����^�U�������h|�n?'@@@@@@v���]A��S��h\���9 � � � � � �{I��o���tX�	�����9���8������F�{��U�Hu��oR��q���1
���hj6��{F?�^��+�&�{�������9�U2��qj��5 � � � � � �Q�����I��&����6�w��w���LkmST������������,U���l�r��"�?>&����������e>f�L��K��$���0cF@@@@@HR��q25N���Y�aR�F�V���W�,��������!��XU/x�. �=�w�j���������(�nH��'����0�,�3\A��<�x��=�{�����x��=�{�����x��=�{�����h�
B��U��v)��p�#y���c�����m�����d�J3@@@@@@:���$kAw��}�\_(~�r,�
t.���O$����m�[.Y���|��<[V)��M���+� ��]�~�e�/����WU-s�N2eg�
�����C����'�X�'�
�oR���P��'��m]�� � � � � � �@���|�0�j}K�-��+�
M�rY��]f��u3Zs��s��6|���w��?I����X�B]@�?i���;B�AG�% \a�F���(���Ca�o�%��
�m(nS�
.��������mT�*�W<��Az�nS�v=���l���0�� � � � � � ��^0��>}����F�_5�6?,���
��s�vU�������S������:�Ti��m	�h����������\�"��_�V��f��{�������B?�J�q��Js����H���?��f�FX���j���~O�W��t?��WO�<z9����3����af�FZmi
%�M8����@@@@@@��@������.�
B�7w�����a��]����w�����f��|(�]�E�J�������+��	n���
���'
n�[v��k}������&�l���L���d�@@@@@@ �@|@�|�����oQ���
�����
BK��M��0MO�����_+|����W[��X-�x����0���]�����-v�F�LrDF��1�W����l�)"l���S�+@@@@@@������d�X��u?�o�aPU7<�|aa���-a�*�tom��;@+|n=�������l���~e���iX�t�x�/�[���jK�������F�G�����>�����Y�-v�?�]c��K�&�S1���KW:r���R��=s��Y��e��h(�k�������J6�u���K-fe������n�/w���^���,}j�u�-M����� � � � � � ��4� �5��%��h�
Bc�Qc��V�{���}��p��A�nu��yk�>�Y�-�pW��@���%CO�mu����0sPb���7-�M�v���,]^�%�D��S�K���z$4��?-�A�np���;O�e�a���B��9��Fw��_�]����L:���{,������3C@@@@@8�� l��c�9�/*�w@h,1��j��u�=G����W����F�243Z1����R�#����%�����UW�C7�����R�	+�Z������=�z�R_�����^�T�0\�h��X�Y}���Yn4�tM/1T���_�������Z,����������t��U*6?g��wU��]����<��
:���P��=��@@@@@@���p�����j�.�i4n��0��V_��n�n5�k��^W)>*~���Jl~_��� t}Z���0�S��*��jF��M���>������������6����5=���/[3��f�	8�SG�:������p�8��Ad�<�����;�^L@@@@@,�VA�����I�������'�X�'�'y������p��A;f���m:'n��HX�7_�!�����'�\WA(��������\@P��F���#�Y�4!�3���>x����-&��sdhLd\M{A�~���b*5���Osa�����:�����3[@@@@@8P���[�{������V]d�Ax�L��	�[���=���L@hT��u�%K�n��4�d�=�4h�00��0�:��k�S������Q����y#� � � � � ��H. d��-O�A@�0QL���
��%Op� ���K�YbT�Z���o\��/1��n�[����_�T��/c�����A��Uv�7��^b�Qk�>k�%F��SZ�.hTQ���G@@@@@@`H&���oQ���qT����y��tWJ}U�
h�5C����j��z�W�|~��g��s���=Wx*�[�MJ������t�=K3-����`@��z����1�&
�
�{��X5�dVv�^�A�����~�9n/����c���'r��E�����}&]`��,e������~�+9�Mx�-�9�>or��Cc�FUd���O��rXu���o8�A@@@@@
t�
��]�}s��*������J����%CO�T6J[��:����(����Y�j-68����&��05_A��^&
�e��VK]@gT��U���SF�Zzu��F�4����,=�OKsv�����/�6�&�kl�=�Q � � � � � ���.@@�dE�k����%������kf?2�q��V��z���A�����-���(�������������~6�4����j��W�z]z�P����]n�h�����Am�T���0����H�i�h����{�z���p�^�G������g���Z�s�)�O���f���:t���(�~Z��17��<z��
��d�H�]�D��*��X�� � � � � ��U��0a@�VV�G@@@@@@`��@������u?�o O�e�p��	�����$ �7�LF� � � � � � �[���,���������d��/K26�i��O29�j4����J]���� �����J��P\���N�N?I���&�o.�3����`7��L���0�~i� � � � � � �@G���
��r�2N@@@@@@�v8�+��=���nF�@@@@@@@�c�O.���tp����R2���m��e������������w��+�y�w?�����*x�p��3[c*+3� l���������j��ms4 � � � � � �@�qbYF��k���=��%����?����"�o�Q~��
t9V^�yJ��i�v�
�
�wR��!2�z|[�������'��������j�|�\2�� � � � � � �@������&���g�9���*�3Y��,��Wd��J�������8U��P���1�v������������T��$k��m	c+MF�3s~�������y�y�y�y�
�>�}��������C���y�>�}����!����{����=��:��Lee�� ���$�e��D�M�
���������5D��c�x����.n�(@@@@@@�u��L��A
VV���jy�~�I������o���u�;>K3��d=e��w	_�S�M���L}N��U+Kz����Z%��=}��e2Yd[�Y����?`�,�k������_(�j[50B@@@@@@������g���\U��be��l@�{�K������e�=E�zK��K��~8 �;\5����[����R��Q������+��A�����yj����s�Zh@s^���#l*;��rsZ � � � � � �X'���[	/�	�oU?�����f},G��;F*����v���R�������{FG����|�_�w��	&	���1���f]����=M�m������D�$��T�g����8��K�O�@@@@@@�A�`A��[�ba��#�����A���J����f�T9�����
��0q��>q�`����B@X�_�A6-hVAR�6��a����Z6��!���^,�!� � �$�K� IDAT � � �V > �� ��R���B@�s��[�:�'56����
�6�lV~d*��������AM=��{�_����a��x�Z��O��?���z�.��.��@@@@@@�}L�VF�����E	� lq���Z����5�����=�<-�����Z�6�����>M�����^�[%=�S�V�5{�_K����k���>-mts���(�.L����t���V�Tjg��^]����Y�6��h���4�]����Y�tX�C@7������y�����O7
sh���y��z�<��,�.:����	����� � � � � � ��,�����������*�P�q�
����}��0t�T�5�����w5I�M���%��w��#lz�0�����ZP#;K�ep�c�|�����|����f�<���s���F��7��zG����&���+4$�n:�^&�SG@@@@@�*o;�u�d
*�*����Ws�M�y�=�����IE&Md�M=L�5InW@��6�"�f�����SD���;[4��U�sTJ�/1j�g�g^=���u}����X2u��>�)3���6]��h]@��fm��N&9LRE�_�����l*;��/���@@@@@8�� l. l����t�r���k��#�+C���5���UG����p8�����
;o9 4��?�FyP��
�#�z����'f���l��0�|L�l�M���]�ua����s��'�������� � � � � ��E�VA�:)��{�L��lV����,���jq����4��Yb����0���w�� ���p���U{uad_���4��Z�&�U:��u���f6����%_x4]���s9
@@@@@h��m���Bw�O�V��|��=����&�Y%�)��\r{���:�#���i�1f��������# � � � � � �o�% �,1:�����SXb��
���5�4i�B��+�����4����j����=v�����}��-���G>%Zb�
�}�Ag � � � � � �@T���-��7�z4�H���fdR�Ir����Ow��f��������`��0�9,z��F��o�����EP�i�A&9$�=An�k�F�
;�.��\a��hHXp�M/
9�Z���)?�u�)6M�
mm��;����OKk�FA@@@@@:�aB�4�#���_�!q�[�����P*\���������0��
�{���9f
q�IF�B�d�|���������������7-�
2��� a�y�( � � � � � /���eA���-��>o�L&�lk�(��z��e� ���������~�����wKy]�����M����[*4a�$PK��i��F?&�����Zu�M2��[������vd�uY/��9���_y4]m
�%�����t� ��	�o���
~�Qc���fM=����>�*@@��@@@@@���Bk��-X��s�I�2��K��n�k[3d�}O�53/�|��Mee�cK��H�����L&�lk6���U���M�)uOc@@@@@@:��	+;��c� � � � � � ��$@!aJ7�@@@@@@:�@� l
;�'a���� 4�������;�
2z@@@@@@R�!Yl	�6�}� lx������a�W�*����h� � � � � � �@�����\���X���4JA@��nnq����NTp�������?�.kf��H��TV�,X�E�D����� ��o�[!���V
��@@@@@@�pc��^}>F+�m����������}�09&-���t��5e��z�r/kf��I1q_������������L&�lk�%F�G��]�����"���Jo-K�O#� � � � � � ��z"����}e��-VF�V������U��7]�r�6�X��~B����Wp��C�ix�jl��w��^����"� � � � � � �@��%F�����((L�w��_)��)�_)���r���il�����"��oP��IqH4G@@@@@@�c	���r{(��y]��6<a���J�L�Uo�@���F5�J��_��CC��~(����r��s�S\,���d��96�|/�_���A�6|�`�Kv�,kW��j��W��x�J
��]�Z��F@@@@@@��
�luz�,�+=��	g
}�*Y��<�66;��?Sf��~��,/|j�?�XU�r�l�O��w�VI�m����	�����R��Z5 �]>���F���Z1%[]v�i�@@@@@8�ly=�u�E9���P��6�����[T2Y���2M�����
�%�5'����1�-v�`�T��L�*w�6�������5�u�����{k��{K��"� � � � � ���%���e��>�������E���ER(�3.W�����
��.��1m� \�9]�4���
k���Yb�� <Y�����0���F � � � � � ��~!��0z]*���vS��� �/n�d&Aa2J�A@@@@@�*@aCA�W�}P�E�Vk�V��5MO��u��);�������D��^�[wg��!���7k��V�u�A�9�U}F;qn��3�����5�������:kD�&
p(����]�������?�����sL������<#��J?�Qc����� �����o�*���������gu���~���R��O�^(����]�;�!{�0]Zx���!*<��g�-�+]*��k��n�zL�z������SV�g����C&R��\M:��&�i
H��s�S^-��_yT�m�)��itV��Xn��WXv�c�����������Uo�y�>�wq��+���
����bY�g@�����q�a����L��WZ��<o���K���J'����;���9z���jmp��w��9;������i�A+�(A@@@@@�.@a�t���_?h~Q���������Y��*���|�k?����hr�p{��M�[�>��a�}J��2M��D��T�������E	��A=�bJvh>M�U�o��
�\������t�����hh	�������Ao�����M�7��5�az���������0 ;�WJ�������#!a$ xx�����oo\�Q^M��D#��A�|e��l*<Ln��B@@@@@�X��T��
�6|�`�+f�A2_1��=#���VM�����P��=�|Z��v���OSf�������~�Yk�t��~T��C�`lZ��.�>}5�?s�*SWO��_n
����h��m�{�&���Y�S���K������sxLn�6m������fB8�l. �rx'���E�{�e�r�]��K����	�4-���T�k��h���Txaf��2v�����hR��i�[/�X�;>���:�a��s����kx�Iy�~���w����/5��:[�M������z����ue[*K*t�]��v@W�u^'
���N�_�U�t?H�?�
�DBu��uw���6��
�Z4�H������*�AK�U����n|���Hu��G@@@@@D@�	|5��
[�I���������&h�OcB��]�2g��O��k����nz����0'�>�a���\6rS���[����N��-����ov���eGC��T������F����G6�Fs}e�T��h�%8��d_�G�O0�im�MjbB�����V������t��c��%?�KSYb���-:�Y�f������)��G���Us���)����k���}�xe��y�7�����n���]y�!� � � � � ��F����5g�`����$��u�o{��;h��B�u!]����_�Gx��S�u��>)���=�u���rT�����]�R@��.�����.��h��fB�C��u������eN#3m������K�]*a�ms.f���=�&��T�g��z����*�S���{�@@@@@@�@���A)��+�dO����*��Y�U����)�y����1��J������Z�M�>Y�R��:�����-��W�Hu��gh�h�*�����'�Dmf�"q�����J�wU]}x|�����b� � � � � � �z��a}�������U�}UR'��q�����m�,9{��SN��w�������Lee��#Z�Ii�H`���}������I|BK��R������[5?�at��t~�C#*W�.1�h
�^X�Iw���[�ro���2M��D��8\`T36���[��xp�.�����A["�uon�%�d�/
u���1���%Fz��n[`��Z�TAhS�K�����?;D7������n��$ng� � � � � � ���!�+^���c��)��Tk��k��n�2��.�������
UZm���c"�������AOw;H�\��.%����*{�;3-T�gT��|e��L���j���b=�L]=��~q�5������+K���A��h�f���U;5�2C���>���3_@+Wl�����\@xbO�<>K���cp;5�V=S���7����^}	�(��h��-�w�N�1��F���nd���JKj����C;kt^�k4�+���S���<��h��
���N�Y��Qa�j��<����1(gq��Y�S�Wy���g#���A�t��\
�l���������}tq��K& �.+ZiU�O��Gs�&`��Bs@@@@@80Z�����k����r�Ug��~��_��u��E����������� 1��h��^�[w	q��S�[�4��[��^�=��]�����	9���( 4��2M��D���%��������W�4�7�:��w�j�J�*m�7es�QF��_;k��UR�T��X�<]-)7����P��@@@@@h�@��"�l�"��/1:\�)cb�M���F������$1���
k�p)���e�j�T
���&� ��2�������g�z��+��>�t��9:k`��X�A�W?�moM-����7��A���m7��_3�>��wn��3��������sO:����oSvjv�������k�V�?�zTj�wx���I��������=���h��3_Vk�32���jR�0�IPAX�R�g�����UZ]")��S���YC�5� \i��'�u+K4E��+�z�j�i�4�]������� ���L��pj��%Z���.=35��N�6���gm��
Bc,��X�W��h�,vM��cr5zX�N���;��P�*�x��Z9�`�zvF+���G � � � � � ���)P6���^W;�A��������=[A�^��8+�^���'�����=u��to���m���G��K�g���G@@@@@��'@a��fh��m�X������������� � � � � � �*���g��mI��~��$�-��D@��.�\aI���k���>�;4���n�Vt� � � � � ��@� l������%��/c� �)_���u�Q)���0��$�t_��]�K��i������h� � � � � �� g$l�����E���*)��S�u����A�����2O#k�nI����n��p�+s���}��A � � � � � �td�.c{+��N�+�~Z�?~��{�/W�����5���O��8�-�������QS���P�gK[�-�!� � � � � � �!�*��������7B��5*]�h��K�6@]-��~��2�������-��F�% \����K+\���eY�:���p�k����PU�}"�3<X> � � � � � ����GW�����=����'����Vm��rn\&_u�m�Lf���V��Kd�e����d�oy�?���U�-�}������ ��/ sC@@@@@@`0���I�QH��O��_)PU"��j�*v�<�8�O�P?�-����:=�)���8h��u���<��7�7�7���������������x��=�{�����x��=p`�Lv����D���6����*����5O��VJU��s:�Y���8��L�.�j<���*�U�G-k�.�Gw�r[��E��e��5��Q$��,���e:�T����U�Y<O����u�Q � � � � � �t@SF�2.�F��%}pk��c��?'SiY�3�]|���
T���u���A��G���Q0/�UB1����In��������V}V�,Y ���Z5 B@@@@@@�#�������L�>n�)�K/�����LK�k�������I��:R���b���=���RA����
�lk0��^|�B������6�z�CS�����E�V6�i�B����M�Z[���8,��w�
�p�w�a���J�|u@R������Rj�����|B@@@@@@�[s����o	�gT�.�y[�,-1G���*��j��F@x�(���hka���Z��������u��O����t�n		;x@���n���q9���q�b>�n������~�&ab � � � � � �a����-���\��~�~�����=co��PAb�]~��y�~��ON���G[v�����2��l���+�AaP|R���:t��Vwmjm@���@@@@@8@� �����2�X~��z��}2�r�]��c�~�������R����q��tM�������[����[����N�u�!6]|�C��(���_/����n@��~I�&d�������_���z�6����t��L����)�����4�m���25���y�u��]u������fe�[�Vy���p_�b�5C�4!/����q��1S�
�u��.[E�t��������|k���M5�k�__�0>�yM;��3;����<u��UW��I���#�u�	6�np�g�i#� � � � � �qT�q4����Uz��V
�)��������������L=?�^���M�:�M�v&��f��I�d��w�t�K��,��$���N��[�}�"��������o'�U3&f����`��q��b�;�6{
�UF�3�j�Eo��w�?�|�.�o�IYu�y���%�]3a�Y�+������g��c�r��#� � � � � ��	t�
���^�� l�����g�[���
h��<��P������?��!����Vu3
�\~�Y�����z�H�|-�g^������84 �aP�N�������4-�5�����9O;5O6�8-C�z�C�������� IDAT����Vz��g�n:1Mgv1��d������������0����9�W���,�iSN��n����T�y���.w^��ui&|mpI��^�Y^���W<���
����:sh�n�oUo������Fx��������nZ#� � � � � ���a�%��X��y}e`$�3g4�oOR4��]�4�WT�w�4=|f�NMo�~j: t���k��p2����Dc�"�_b4K����y�S#�����e����}[������/2t�=�q7����P�C����g���������g�L&{��E6Q���H\�B��V�REk�Z��/|�����.��k�R�w��(q���M�@�e������L�I23!���D�s�=�y��?�����������8���*�zu�W+J����~I��1Z��f�[�~{���b��l����Mf� � � � � � �@���P�B��uO�O���������R�
�b�����W�UGu����N�e	W�1f�0����0&4��oyK�h���J�w��.R���,��P��1v�
���\Y�K���Uc�{L2 l�,���@@@@@@�V���%c+�������3J6��5���j���&����c��w4y��n��\@�������i��>�����)������L�A�c���0�y���W^����y�������wj��.��C��-����j��o��*	��+� � � � � �� �6������6��;�fi�o�<s���z��C����QJ��y�I�{Fh*+[j�,��=� l~��s����_l�f[���v��N]d�c'���Ge�����l}����e���V�����&����y�
W����7�z�����^L�����w��k��*a�O�� � � � � � �@�T��%ZA(m��Z�W4�����*������t�>�vj�`��O��m:��Y9�P�]��u��ik}@��}OV���t=9����F�����s��:.�$���Z���������wa�-Fk���f����=c��x>��e@?:���������'E��s?shn��r�[����~�9��� L��M@@@@@@ ��)���t������9jA�L=WdS��G���x�����b4��WU��bZF����5��G���+p����D@(i��Z��M�v�}znj��Kx���ryu�s5z��d"�������L���
B�� � � � � ���@� �.7zab�n8��$�'k������~-����}���URA~��
L�5GZU`F��R��Z������v��j�!6M>������5*���k�68��������i6�MP�������������5����7�xatN�^=��K/n�i���XuA_�����<�XJl�{7�����tj��vs����GY��;��N���%g@@@@@@ V ������[��A8R����3�3h;��� �x/����g��Uo��@@@@@@��� ��$1��
�f�>Y�F����Am����^s��#����i�Iy0:"� � � � � �L�A�����5����L6�����dw�E0E@@@@@@��B��T�f���SJ�����AX$���U}�4v�Hm�}���{��w�W_|��V���N�k�������qK@@@@@@��%����s~�[�����#�,�y�x���J� &)�W��6>za�6���W����h�
F3l����p�l��[�������N~6�@@@@@@@��X��W���YEC�
���[Bekm^�3����&�e#�vn��yy����{F�H@����6h�����E��>����B@@@@@@����@��wF@hY�_�����
�g�I���e�����[z�+���d3<& ls�F
LeeK�H1�+~��
�T���2y>~G������ � � � � � �I���P��F*m�h��r�N��>����\�o�\]����"�1C�?��2������r.\j��h��3��Q�$ 4��B@@@@@@��A ��������~�m�+��yX�����������(o�:�����5"� � � � � ��&�]i9G��yh(/��f�������*���rh�O>1����������'#{z���H3� l~���m��=:��z�2����`T�lo,Q��	��[��j�~P_M[k�s@@@@@@�5K��~���!�k
����o�o��I�3��Q���rU�������:N:V?��`�1
��'5^���U.xC����rr��+^�g�/�;�x�l|T�]+R����)��+�4�)�VlH=����y��{	�����;9O~nm�A�����������:��o����j���=�O@Kf�i���r�]{0]���. � � � � � �/��E�<lj}a��'T���)M�q�%�X�zC_���w��r������)���������[4FknP��#�E���v����;����hB�=�iUq�������a��������
��b@��S����Q�����I�%F��=�J��

�q���|/������g��}��0Z�}�{K�q@@@@@sz7�
��~�U3g�S�aJ��O8[U���C���LY�iaJ�Qt�Q�d�z��2]�����g�(���6n����O�i��f>������&{&+�Z{����� � � � � ���	���~���*��z���q����3�����V>V��;�{+��[m�~�E{����t_���=@@@@@�������Z���:���[�����N�*r�^��.���K����6�u��v]}i�&���T[W�~��4���S���g����
��5��]u����	j��
��3��F8t�E���z/&T��tj��m�]�?��������>�/Z=�d��q�|zba�����r���t��C��('a��*�����J����RQ��^�p��3|�I����p�G3����Z�=����7�7�J�[�p(�Q����A���u�F��_����L������0�wzB�������/��eG�R	)���nMZ��{���L�9D��Z�r��X��k�Y�=�sl�.����]���3m9�K�����M&gj���w���\j��RX5]@@@@@���Q$��.M�]^��H����g��jM����\�pw���5WW���8��� ��]��&/�Yf�i���yxA��W�&-�5Y���;7[�C�H�10M�wz��<���k��=��y��; ����i�ZR��{;9OO��*{���' �����+47�p0��-����[y��yz��pH�xp�rd�a@����S�4"����NO�ktvb�Q�w&���6���g����k�]������/)�&&ly�={X�m{�q�{:7WW
4����>Q�#� � � � �8���2Z}f��s35����Tmmwi��}qb��^��������qk�}�z�W�>������#34���3�B��o\�ys���gj�}
2�nvi��k���jG�����wju�.�>>K+������EC
��\��������el��$[
��Z�P��.1���4m�U9����G����b��9��6$�:��<�n1����?�I8uHr�HO@+�����������3I8�J% ��`�C3�����{��������]�5������mw9��$Z~/�y���t��*���c'd����d|1�Am������5�r�
uN�&��i�����(�����:]6�N���
�[��['�@@@@@����O-,����&{$�kx���V����_��1
�h�l[^������L��2���%�#��1i��sB�������I=b_��V.(������x�&t1>�R�B��>{= �x5{b�����S�"����G��yZ�s#�i�����n��\�-�mE�~����ZQ��
c�����6��5�����hp��9��o1~���{u����W3���e�5Y�u�w�'�g	����^$�<�Q}����1Ax���m1��Mt���]t����F��{6e�ar2�E@@@@@`?  �8��-n�X�����RH
�=u�������3�d��&/J�����5��^w�u�%����tQt����#sku���F�O����N�.�j��s4)�3�}�Z~���kt�m�8[x��<����
��>��~+�������^$�<�S�w�� �a����lZ��%v�dVI[@@@@@:�@_	J���A%��z��m�V�Srd�eyG����,U��Q��&���	��PnM�k']�_:�BIC���S�4!G����z3�/W�1�M�l�vu�&cQ���G�`5�0)�-FSJ@�dv�����>����F@@@@@�-���0��U��-O��mu���}���z�����R���!���H�K:�3�c*+{��YJW��e�m�Q�S�c�1�XsC�ax+P�.��p[�i��)����������,��J3����b�me���y�����U_)��4%�P�������N��W��b�Q�;����3���jM������������Nj!-��j������-�Fl^a�D@��
�������?��-F�T��c�f��m��lR������"����<|z��
�V{n1��M*�-l+���i� � � � � �@G����i�qY���
#35��t���<Am����t���d��HH1��|�aQ�(��Z�N�n���m����h����I����!��{�����]}|�V�1=RU����Tk�l��\����-{��t��V<T��KL�t}�f�nU�Er������5�X�jN��
1*>�:���=����F���Q����sS
	[�o�G�~Q���g��k��i<OP?uk��uz����
z��2]����f��>���k?t������v�o%k��n�p|��C3�phL��F��j�v�V���}t�����L����{���L��k_�����t��l�v�]��s;�Am������5�r�
��5�$����=?[�z�- � � � � �������@K��r����9G0R�m�o�����P�^�����y�������+bGj����W����tk��j-)o>��������w��E@n������~�}[�oP+�/���M�1���f�^g���3��P�*����\j�N�����m{ ��P�{]�.�V����I��j��H�y&yO�O��Bs�����9���_�6I���eU*����$S=�1I!�#� � � � � ��:Haj>
[�F� #o���h�gFgP���'�w����h�d=��4"]cF�U�+R�� ��%�z�^+�kW�EE#�zr��5��]Am|�������:�i{�E������PR�N�~���Uc����c�
���O�OO,����=!���u�y]lTK�O7���������5uIP�f�h��d���}�~���Z���1o��m���:t���*�#�l��B�l��+�4�	�Vl7��m�|n���jTt�'��0DW���t����������	g�5f�9���V����g
�A@+_����z�bs �]9�����ul��N�9A�$B���~�V�o�+��s�MW�6G����^� � � � � � �A������9g� ��wW7����"�]	���g&<���R��� � � � � � ��M�VAhT�c<Z�w��7��^�.�D+��[tV���L@���� � � � � � ���+M��9{*�V��7L�Yar��d�{��#��&8��;��"s���v��<��c��_�5ia�c����`����v��K��Z��p�_�P4C@@@@@`	t�
����;�0��[mMa;b2 � � � � � �@{
t�3_	����E���b�`x�lo/�w�DyO:UU��._�����X � � � � � �tkV�}K}�V=�/r��nJsO?w�*�Z����btZ�,G��@Z^��^d�Q# L��V��#��/���k��}����$��gR�^ � � � � � �t`G�Ir2�~��>P����_����[f�r�������v���S�����
��'?�)��E+�����m�;V��i�e}��n�K(	
x����L�]�)������� � � � � � �@G0�d/8Y��g���3���/-���%��,IhI�������6t��|��%���e�?S����?*& L<�3���	��F � � � � � � ��b*�?�����
z=����`m��FM��K��y�,�7H.WJ{��r&b�g(�>�3'����e���|o�����{�����;�����;�����;�����;���A�;�n����n��^�n�������2�U��i��>��w�	�q�
y�?S0�WZ��
n���Kg��uN)���
B���)`��'����3r��H(/�|�V�GH��<?e� � � � � � �$!��)�eS�?jP�S�#U�*c�?d�f[I�!T7�W���D��5���������#���KR�E�Y���+`�s�C��e����9Ii��Si������n'��<�]3��!� ���U��;kt��wI'�����d{�@@@@@@`?��r�<��x����/~;���]<J������e�;N����m�gX�|������J5^�7�&sI��Z�r���	F�v������q�C]�*��vu��0�u�Ui���Z��N�&=ME]:��Cv�������Y��-%�S,-�q�.t����� � � � � � �@��u����<r�zD������i�Ur��F}muC�8��=� Li>�n1��a��]��O��P^j�I���%0��M�U�=z���n���!{h��,u
��a�G@��� � � � � �$��:ZA���&�����U[�����������{��h)�9N@�i�N
\�V���������W�������:-���?�(��0%t:!� � � � � ��/@@�@@(E���N�`t��F�x����\K�=ZQ������s�������_W��mu��]s�3[W����m8[�N�|o��(G�O�����	��Ck��`�S�\R����rH']����4����Z_��������}�5op��6�6����/�4gs]��Sz����J���AX��[��"OK��WQ[[�F�������V��/�����e�V�c��u�rT��1���@�`��:mR��xD�n�*�%r�c����1[�xljw���4����v��eD@@@@@@�]:@@�rP�M��J���-F����9���J�����A��J0��%��Uq�N�]�;�����Kj����9oo����������9��
�� IDATl����`i��,/Wq��,�p�!Z���$|eY����MM�=��V��>km��&��k�m�I�Y4}Tw�,�
%�������3 ����gu���gvv�������2u�����a�E]]����1W�>����hud�~/@@@@@�k?|@�x�g,�TV���#��=� ;���oW�h�������f�N���E�41�������N;�@�w����nW�fV�k�F����0�5	�wj�+;4'3G����<s(�s9����.�����t��)�&��9�;��j���9�����<�5)?��j����BW�]���P�5��|��,�#S��uR�M���7-^Ia�[�VTT��e�J��y��tM�g���)Ufej��<]���<���=Z��]���9��u{�P���{:!� � � � � ��	��ar��TV�CW�2���Y���b�/�_������uZ��vT����l
������������qB�Z��b����vj�?��������e�{�?��]��7�b>k9����
�����,[�g�3CR.gh����oGZ�������u��j+ jgi�^����>���u��c�l��#��!�Q�e���n��0�=i�7��Zc_���a������r!� � � � � �C����
��[��Am�U�����4�������F6VQ��)���`,���<�PM�,)��&E��~�4��R������5%r�`t�pV��V���5��,Hk)4���g������ukM�K+����>5������������=|�c��QH�z����rk�3�5'^x�1���@@@@@R�> L:RA�d����8���-w?9�0K����z�x��J���	��F���T�������r�]W���+����bR���fi����+��PR�B���8�~]��c3�7�m
?Z����7E��O[�f��SW���~]t[�
M7)=��:5^��  �T[�k�!�<0Z����Mk@@@@@@`_��[��x����,�Ki*�����-������q��2w�b�(�T�g��g�� -)��n�����W+�\6
�u�n�N=���=�nx��e�F���Q=�� {��A�
o1��C��iJV[���0�Y�]d�/�C�5��3�
��3i��i4���5h��o5�������A@xv�.lZ}�m�������5r���<<K]C�o��UZ����:��>�����C�s@@@@@@`�� ��d1��
���-.�
B�_k�W���U*��E�Ff��<���w�����'�������H�j�h}�fn�5T*��o������z��C]-����U����Zt�1@�@*�u������:��nZ4�	�N�|e�����?������h��Y���[�J?4O��\���l���>���w�(]AUT;���RM����{kQ������2}�9C8�7��<����u��5}U���w��s54�A�,=OKO��E�5�������9����7W�[�7Wj��������FO��}���jh��
�y�\����g��Gt�:�`]�f���x�aZ7$S}��PI5���.=T���gu���� �7_p&� � � � � �4h�{�����g�%����
�V^���Y�+��A����u�'�fN�lWq�;& �T[�)�wiQ��
���xkA��^�����{hAok�~F����*�;�����wV_K�v��J5>
�	���)�Z�rh�����9����l����=g02Ww�F-+k���v�X�Vq�j�5���Z;����[����6�w�U}k|�/ ��DK���O � � � � � �Q:Haj���A��^]s�3KW������Z��L���Z�5
���+���JKu�3�Cz�Z�u����h/u�����e��>��f4jiKK���.��
~M�]���r9���j�kgmh<��i�!���O�&v6��ZS�=*vWj��UZP���1G3����WvI��1]�V������Z]��AR���{H��<,SC���|^-Z[�9����e���l����iREy����l|�������53�����e��#
����d�n�[�K����,�����u��Z�x�f�� ��<�[}��������]�Z4�o&�@@@@@@`_
4>��#�����3�_�xa�����.�����Y � � � � ��T�������2 � � � � ��wT�w��	�?����`& � � � � � �^T��$����CeI � � � � �������d�T�-w?��9(�q��~Q��&�r����y�,�|s�?|@@@@@@>�a������o���������{��*9�q�f�;F���.s��I�{Fh*+3���h@��%���e�r����)�����xIj��@@@@@@�,��8A�qg�W����*��]���jn��*�{��KC�M&��5�d�w�����4f$ ��
���{}�7?NB�j���[�~�J���S��@@@@@@�H�.��;�DyG��rr�+����.����T����t�g�Y�
���s�J�V����`���K�#���
��&�� � � � � � � �	���h��0�����%��r�8T�@����������
�m���K���k��@@@@@@�� `����5H����G7���<-��!�'oI�	�9��E|r����R���+��K=�TY�`N�����m=���33�o��z��ztS`���v�]��;d��k�=
�jZ �@@@@@@8L�,���V����p�����Tz�ai������H��V���d���P�`�^2!��G��}�&�M���]v�S��?2Y~*Uu���;�������@@@@@@�L ������"�*��'��g�|YJ��4N���6���8������{�WAXhS�b�L����#��yXA���Y��?���#�>�.��� �Nl��S�7�${J<�[���=��;G���-V�o�c> � � � � � �{E�d���A
�mTZ������t���d����o�cZ�^������0��p�=�b�W�r����o%7���5���\� � � � � � �w������K�:��}�Ma����]��-~���'��t]A@�w�JFG@@@@@�k�Q�]>��B��7dh�Xc/�D���X��=/���������G����V�s4��k�[?��t��6���{x����
.K�
��t��zo�U�����]���5��v��~��A-_��#��z���t�Y�c�M��S�?�������������\���2�nx C�9"�
�
�z��S^-����������;%�v�W���
���
_���Z5�
�~3,�g���� � � � � � �@�4#���.�I��,�%���5�bt��9�4?����k'��x�]����^����_���q��e���\�lz��4e��p@���A��&����+���;��C�P����k:��F��\�����Z:=z�`$ ���N�����h�
o��x@��B��]h~�NV�s�]�5|�p�H@X���]eM�L�x�C��"$�'
@@@@@ho���0(��'��{����n��!
=M���I�{Fh*+{9N�����=��>�2*�N���Af��mV�n&�D���;o]���wt�ev�~�E���AP_���M���m���p�}*�����q�L�G���������z�����4��
c�y��:]�O���v�p�9</OP�?��o
�W������-�F�g��YuTn�������mn���M����k�hXL�c2m#a�Q6�t�U�G<�6�t��w�����tTb��V � � � � � �	
PA�*�
�6lw�Q�ai���4M<4Z����tc�]��`�������[�tm�t}�[K���-FW�E�
��Z��I�FD��/�rj�������k�8��k$�	��3�Z�k�^=�#����nk�u����3��s�]��:l��l}����Jw<��i�y8����EvM,��i����j�����1��'�[%�n�@@@@@@ ��-����j��zC@������������~sD8�l��V1�[}@	��u[���~���wd�����WN=<,]�_f	WF�I|��{Nqh����P-���-���lz�?i
M+��VE��,��i�Z@��=��[od+T�� � � � � � ��)�A*S[rx�����A��-��j�U}Q��s@	g�W$��x4�w��-C�����G�o���2"al[��@@@@@@��B�Mn�,���g)��%�)5j���A��#�{>S��b�_���"R���mVJ
�ih
m�Y��Nn����1��F����-:'��iK7�l1����mCo�{���w�{���h"m[� �i��m:=+���N � � � � � �-t�
B#�,��g���zHb�k���L�8�����ULU���g�����.�-Cw����^@�����s,����	j���Qf���>c� li�Q�Q�O7����)6.�H1��.:|V�YW���_
7����]�Gk���f�oB�k��P�zu�T��M���m:�@�?�/����?y��0{C��L����!C7�bR�q�b��[&������|{@@@@@@�O�q�O�E���
���=����0X�F�k�]�n���3B�9N]�,6�����0��PA-�k��,1�H�_A�����sm����C�>=����7�-hT����d�=��5�[�G������g��{�	@@@@@8�:Xar+�3wm��?��Z�U@_�	h�q�Nf�f���i�ph�m=����O����"}z
�j�9V]8��~��9'�����:{�+]���/ka�RP�/���}zu�N��o�U�n����U`T�%ZA�
i4 4�lx��G��j�q�Nf�~�UWLN��N�H�m��p�M��|zee@�"��fJ�~qK���@@@@@@�-�p���F�p��D��b�-)>o_�H@B��

@@@@@@ �@�
�����i
�w��>�|��`*+3���d+S��R  L��N � � � � � ���
t�
���0 lZAx��9+��~O���'�{�G_@@@@@@ e� L���{$@@�G|tF@@@@@R���3�{�&�7�xV��n9
��)��*X{�<���@��T��� � � � � � �@�0gP��Y��2�}2?���[>Ji=��#d.���o�c8�H��������$��]� tv�,��2��R�R����g��RZ$�@@@@@@���?�����Ln�Lk��7�����?�\���RUi���,:L�#�)��%�1#a����D�W���@��v�]���������~M������41:!� � � � � � ����BY;�*k��dN��� 4U�*��c���T��������I'�X��F���s�?WA�[�#O��mR��a���0���@@@@@@��=�b���5��UN������/��/�=�\���$�W
���b� � � � � � �{K�d�K�#��m������+s^��+�Q��"���

�v�I��;�X���^������&(sA����*���=
���9��r�[�(I%������*��c�d���M~F�@@@@@@@��
X2���
f^��#��Ry}T��[�Z�u�@���j�g?��+���������d=y��
��ze�\�*�Y�Y��
N�!S�;I-�� � � � � � �9#,�$��G�E���|yJK��2R�G�6���`���������d�4hd�	�;ZA�_X)[�{������;T8�V��l������S��������c2t�x��d���@'��>�T�~�Y�Sm	���|V��K�u�c����{k>1�dD���5C}��*���)�O$e��UY:!��D�S���
�� � � � � ��;���
�����#����_�eJ��=D��W6����8Y��8& L<�3j�-F�;U8�&���~5����}Y:a�������H@����R���
u�������u:���Z��0�o.�@@@@@@��h> ���)$&7������ l2�+�zi~�.��!�w�[�%���Z�*W�t�����T���%�Ii�nwV��7R
zH4B@@@@@*��1��e������'���3���l�
�.u>���Z��J�Vm�
�k��Y��<�
-�����t�?k���Jd�����<!C�G�)���K���K�^�t���|�W���T<��������<�,�F����~��!N��}��G"s�38C�L���1�v����j-[�����8[�s@�W�������3�$�N���gg�����|�\G��j����T�e��z����0�k������y}c��U�dw��\X���������m6�ml��<��*�~Z�#b�C��u:��|u�{�.onu��j��W��32.�f�%��ZdUn�
��������[�l~����[�K
�,�u}��d��@@@@@2��SA��4���U����E��A' �p����Zx^'m�_�r�<���R�����Rxv�^��QV^�9���8�U����	�M�F�Wx�M}>�hU��;g��E�������H��u��u�����2���g������m��_��Y7�[�M7=�I�;2F���i����P�h�����u�Y��U����\m���e��#�<������'\�E��W��������\�d�&a�A���r@@@@@@�#��A����g���	%������M��������W�|gW�Sd���Ju�]>��:_�]dS�MR�W�F�"}K��o�����]�\�uG����/��{���\�'������km��he�Y�^�����!y&������C���3����4~�Yv�j~��z���s������_O@��P�Q6���y���J�Z��|m�6]����A�l�h��nu�<G���������sE��dd��vC�N�+�;5kN������[���d4������/G�k����Ip7REx�K��S�aOv�5Z��l�zO@�*�������Y������&�����n]��M[����w�o?sG@@@@@��R�
��c��L�_����H�g�mr^8\�5�Tw����go(���3��=9mpq�B�loc��R
��U��
�� IDAT/���h�����N]2�B��U���	=�z��%�N}Z"�����Pe\�nm�{4��R�}f���/CZ�F�����yI��J���j���s5�;2����]7
m��4���Pa�����;�"Ac+a�O��q���=Z_-m.6�;5���9��qwW��5�
U�6
G�_� � � � � �J�
�����5��t�A��u	�y1[]�?���K�C[��l��#l}b���HW����T���T�����.��hl@�^_���ViY��2#�b����A��[u���:����Z���k% \5�{��pk_����mT"��"���������T��iu��_
M-fI��VOv�o?�E@@@@@��R�cT��h���F�O �kz^�Oo{���V������W�(7�]�^�Z��]��MSo���#��j3)7���V���^/����]*^�����V�9z���b��* T���rg^���q�h5���iz����Ey�r3���j+[��	f	S�>�@@@@@��P@x{x2.������)M�z�`Y���Q_����:�Y��n��������Sm8W����v��^�������5��5��]��Ei
���*�U}Ya�s��*�Q���P3����%3��/�k�]
���5hx��4��^�Nm���$�����7&N@����P��]�an��%i� � � � � �^�
��#l��&Oy�S�:�.�N�������*���g�W���<}K��o�R�7��n94��r#a�{S�.�T����Ww���Y������:�Q��ad��zqnVx|P�����?�5�
�yz%.����N�E�N�e�=��UY\�	��h����s�Y��FG\R�nS;k�T[���F��6�����=�:tbw������y�G��	h�24��{+ ,u����z��<����������}���C[������� � � � � � �@�T&�����)����;��m�	}����8:;���y�-F�z���dA �e�����O�Y�Q�4��	�mZ��'& t��s*�R�9��[e���VK��ixf������kj�9�������t����
��?������74������B@���-@@@@@@�@��0�4� ��������W����n��s�t�y6��hY����/8���j�^6�?���S3tj��7���@������5����#[���' ����j��)c�f
���/��T{��/�nT��N���S�����Rcm6�93S�\���
svorj�=5z��J:[u��=~�]��&�]��d����c�}r�C�'�kt_s��^�*I�^��R�B��0>C��E��l����D@x ���@@@@@H\`�� |)(�B�\&����1���(��R���Tk=I��w�pD����8-@@@@@@8P�+���+����_�eJ��=D��W6����8Y��X��FYY�9����S�Ss� � � � � � ��,�q+����B���Q*�w��!� � � � � �� ��*����T��L�� � � � � � �@������G�,��yt���n.�+���:R;���������@@@@@@��N h?L�9=�n�G����������y�{���v��S���{^�1[���3���0�[@�KUn;O�W~��1��T�BJ�� � � � � � �Y ��\)\x	.����W�,LiI��/�|Q�-;��-��g�����B@��m��M���� �9�!G�s*I�^����.�]-s��R��ww��� � � � � � �] ���5\��"�������
T����y_y]���	�����l��z�p��W�/)P�������|�������{{\A���i� � � � � � ����m1�}��k��O�O��-
�[/����TS���t����2@@@@@@����a���~2��]��+y�
:2d�{C
�%t������Q���<��*���*��L��2��;�����( t��[i�������S�/*���d6A�/(X]��h� � � � � � ��"`�������t����Ba�nY�se�~��*���������'��]�k����3d�9`�����T��2�wj��A�W�S��O�>Nj�4F@@@@@@�@0�!�5?-��UO��y9��y�T^�Wf��e;�hO�%A�0B)hL1���s��������34����������4��+�LI"�N%�J4x}���K�X�E3@@@@@@�@�G���9���|>Y*����$�}�TZ�u���%ce;�L��r����<p��mnT`���0�GL'@@@@@@�/P�+-���&�e��|��[A��2�@@@@@@�*�z~=��V�Zz��I/W�'K��"������}�4����;��hI�>tI������;��Lc����s��o��F�W���C��0��w�����U�T����i�f���������J�Y�ezpG������uX']�e���M_@@@@@@��]�cTF�R���������pw�7�07]�+]�����l=9���L�|�����v)�1�������2v[
]-�n�N����c���O(�Om�[��@@@@@@R�7����3��)��b4�|�>�0ZtJ�N��3C��L���y_���j�07G���s2��+����9[�����������`@����37���	:'���5�WP[Jv���kuB��z������# � � � � � ��A.�1*��brS� T�>;"S�1����;t���E�D+�������]��^z����#����u�wE�>�H;�\��c|l���=���H���mZ)pi��z�ww��e?��Z�� � � � � � ��gF�� l���4��l'�k�t��2�
�_��
�za��D������M@���NG@@@@@@I��0�G�����2<X�&syY�A�# ���k���r�.���K�l*4��k5�=o��0��J/@@@@@@�=ht�W��?���$�}�TZ�u���%ce;,za��������F�`�0����:����?T�w�g������F��zu���M����(a�/=@@@@@@�P�Y����)S^^d���{F�H@��"�
B�������|=wd�N1�+���N�o/���>��hj��^ � � � � � ��$��*�.�i���y�
Bc�Q�^�bT�l��:�[w��s|�]V�	���e@@@@@@����-�{��:X�1��>��L����C�E�;e��y��\��������
@@@@@@��:Na4IL�o�-F[;���@@@@@@@��Y���0(��'K��:�0��4��K��XA�??%�� � � � � � �@;	PA�N�� � � � � � �@G���#<%�� � � � � � �@;	t�
��k�A<�����.4@�i���Z��se��-�A�� � � � � � �@0
<T�;������\1Of�SZ�/�$���k�7�d�l��.S^^Jc����A�U����i��v���������'�	�	@@@@@@��,`�d�,�F��`���weq=����{wdgU�	�{���4y!@ �^���"0%��,�/@��R��?DwW�v�JV���JW�U,�p��J�?��[��fgAV
�(�@x!&$������e����;I'��7��I>��Tw?�9���{~�I���3<�S����T��+�2g��I�i�a����:��T��-�/[���+������TW=���W[zX� @� @� @�N�OH��S���S�?���+�&C;S(�<�?I*�f�H��)���*��?���?d���R�g��K�����|h�3j�� @� @� @������?Em���>�&)WS�������n������S�

w��j @� @� @���%P�����9���1VE��������\n����E)]���r�]�����S��S[tAjC�d��d�qM�w�~��?��T2I�g��|�]������������M����zk� @� @� @�v��sr���Sz_|%�_�n[�/]�������������W��sR]�/S[��C� �������������??!;���m=ps�k���m�|����,�g��^�\��&@� @� @� 0!0��?���_$;��F���{�w�����|��R[vE;	���B}�c}AaS��\�_�}���[R��J�<�L������;rK�s�E�9�f�G�p��C����ko�}3Wh��q�%����RsE��#��������o�>�
U? @� @� p4
���0��:0�����������X9��T�x_2{~���z>��-FG.���}*A��W]��
�$���)M� @� @�T��%[>��H]A8]�ZA8�r� <�C��	 @� @� @��	t.Ah�gN����}���K����J^N1�Y���N���=v��64�l�O_+g�h!g����'�e��R��wdm�S������c�h��dV)W��+N��E}�c���#/Ms���@+���n8m�d�P}[�d�@O.[��O,���)mz}%����|�Hn{i$����������y`�jz[��t�p�{r8�-�����s�+�y�^�@o>��?�[��[Fs���<�Z5�{�y��Y���=9eJ^��on���-�>��9ao>zR_.��=]rs�wW�6���z�����/,��)��W���z @� @� @���\������Sw�A����B���y���3+w��/��?�}8r8�M�s�[���9M�U+�}����}�r�����F	���5����E����z2w�o�a5��e���d���s�AHN�Q�E��Y������9���y�dFv����r�����c&J�'A8��SvU����-_2;w..���.s9 @� @� @�Y��$�<��r��'{gnf���}���=�jq_�Y��S���j-���O��d�9s�����Z�-������ug��y����p������v�@>�d�px��\��HV�=7���_*���>��o)����Y���3k����+�m��w���F6�3������������9������
��������bn1�����C����lN1��:+�/��������r���	���v�#�|��������j�D2q?	�=�*�s�3;s����yQ���w�e @� @� @��V:� |_;	�������d�����������?�r������xv_R�D2���������)�
����s��9�����+����a����0�����H�yg}���]v_i7���@��k���O���r���Sgg�������K[���{'���*��6����l�T�z����E����WG����_O$6d_��3��Phm��VG�r @� @� @��Q+����V�?��R<
WN� L5�>�=�/4��[}�|����k�~���w��|~s-�-��cJy�������\4k2	������|��]yh,!��gj�n���$��J�����a5�8�Om�L�`�[�6���%�0��jw*G� @� @��F�3	����p�}zD� L5�<�=_���3V����y�h�R��[+���J^N!�zL����y�Nh��qK)+N�����,,2��8o|���5A8�u8��N���Kzsq!}�B��m+A(� @� @� @��+��a[g���Xj���GT��<�/��P�\:;w�T���v��gFr@���{Z��������}��f�=�)	��V/�'���A��v�&��W��i�����*�=W�{;T	�f��� @� @� @����L����p~Z���w����a�?�Y������)����-�s�����c�����O���������
c�������4��s��9�}�6���G{r��=�/R�TkY�~g>��4� ����������-,����Tr��v����|���\��8"����w�pn�����{���;������L^} �c�\�g���4�y������~4��N=7�
�V�J9 @� @� @��t&AxPVN����yLF.��������������z
��2����o��Kf�;��|�?/�:��<?���\>57�m���������5�O=>����x��<���5v�C��>���9�<���D�0Y���||}�'>����rE�L�h��� |=!�'Oo)��V��,	�&{�� @� @� @��!�L�p�����^���sa���c�����<�+Y0��/����M]���64�l�=�g�1�7+N����3��/U}�������r�Z���������������m�H���������*��������%�k6��W7��>������8���n*A�J5�����6��v����\v��6�M}D��^��h���p~�Z5�g�r���\}b)k^���G���>q1 @� @� p�:� l{����� �������!�_ @� @� @��M��	���{�3����u @� @� @��C��	���y<A�����?���;{o�&���g�,��97�:���N*�}���2�j];g���������yQ�o�
W @� @� @��h��$�;�p_g��{+�����#@� @� @���@g����xF���R�9�W��.�d������� @� @� @�����N���&W�{�;3��_��R9��TO��d�q-i�������R��#�U\|�Y�������[j�B @� @� @�g�c������5�>�X��1�o�CzF~����7�U(���C�.��C� ����$���Xj���dyz>wE*����������7lj���� @� @� @�@����0��������R����7��4-7���i������������/S����<���������Om'A�����yh�D;�u���7���?����{�~�~�~4?0?2?2?2?2?2?������������'�@w����b�����O���Sn:�� @� @� @�*��v�IDAT3(���x�E).>yf�qUappb����7���������j�� @� @� @��\��/?�����R`���z��V���gS]���[�J @� @� @��(�qzf}�-�c|�D��=`g�������Z���,�u%7��7��w�jqc @� @� @��L���;^ws�����m� ������g����'ZD� @� @� p@�Y����7����Ig���-�+H� @� @� @�e�F�p�y�������
��O�������,d��J��\��#�\����sn��:Z��^��.Y:��K����B��%������R5wmm<�'�bQ)W�O��R�;�T��\X���\���_j���+�uC5�v$gr�)�l}��������u���7?X�M����|o]%���e]����J���q�o6T�Wj�����B.]T���svi�f����-w�� @� @� @�G��XA���V�%�$G�M{�z^)��W����?����_W�rd�6\��'w/-d"�������_�'�����K�d�%kY�t9�n���N� �W�%�jy���5�z�~���3�Q��/C��P) @� @� @��Q*0ya�o��B>|j)��T���j�j���r��]-{ko�rB������/��������y�dx��[�+��W�O�����������s�)�<yz1��	�Z�i����\��KK���h����6���gj���R����s{'�Q�M��ca
���Rn<y�9��'�u��=��������l��/�Ts�[z��M����S� @� @� @�(�������D�L$�����e���~����_�>�Rp���j�~��'���Wc�~�r��������sv1g��M�D�L�r��|v����Y���}Y�l'���m����{����9�=�A�e���\���'��o�:�v�_^�M� @� @� ��@w� �.A���Ya<y8\�5��$�{s��=|<i7���u�T��5��mK��_���s���\zlc���g?	���|=	8Q�~��m;������f��|�+A� @� @� p�
>+3�����_����M&��[�~t�jytk5�n������K����s
�0A8�v�_^�M� @� @� ��@�+W������������+����� O��������������|�-F��Y���w?S�<�p<A���{s��S��r�/����i�>M�+����������3����=�r.#@� @� @� �XA�|~��,���}�� ,���7sA_�����>W�M�r�;{��9���z~4W�\���)��[�N^�|����0�~�j��.���b.H������J>�D5�zko�rB�m���W����'w�6e��$�0���B>��Rn\P���H-�6T���V�n��D�KN:��P���I.?��O*d������Z�}��;F
���z3lwk]� @� @� @��Q(��+�N��`YO�^:e���j��u%+G��x�k�W�7�M��r�y�[�Vk�iu97O���y����/Y8�lj"AX���|��k7��7q����(��zd @� @� @����ta17����:vN��y��XR�'�+��|��Z��b%wl�����k?���yS���Z5���jV
6�[8��������=���~���jnz��{^�eS_!��X���7����.]P�_�V��?/������=�����W��k��s������bV�P����kf����_) @� @� @���M�� l���3[�����U�5�+�������W� @� @� @��H��7<Z�=k������Fl��� @� @� @�8�0AXH�l����>�V�r����4Mw�u6�A�27'@� @� @� ���d����^RHapp���`S�8�+����m�<����g#�y�aS.&@� @� @� �A�����V}������+�j @� @� @�Z(d����^���#�~'���g��+H� @� @� @�@s=����}����ru[[���SOV��A+	[�	 @� @� @���@}�`�O����`������j��'?�#	��cB���x���! ����x(�����������������G�|�����W��O�Vy���+ @� @� @�hI�P�K��-��E-��(�V�����R��mm5@a @� @� @�f.P\����u�����a��o�6�R��+H� @� @� @�@s��%)����
M���3+O�E�+H� @� @� @�@k�����������
B	��:L) @� @� @���� l�N��y�� @� @� @��!� <�]� @� @� @�:'�f��G��0��3������d5 @� @� @��p�� @� @� @���H���+�K[�XA��B @� @� @��� l�Oa @� @� @���@�	Bg^��� @� @� @�G��3��o��'@� @� @�8��\A������a	 @� @� @�{	���= @� @� @����� t���]I� @� @� @��8����� @� @� @���@�+�A���R @� @� @��  Ax�� @� @� @���"�f����KGk' @� @� @�����;���nH��z� @� @� @��N	�Sz�M-�Vl�����M����\�� @� @� @�4'P8��������ru[	��}���x�J���@A @� @� @�3(��p��m%��[?����Z�6SZ�g���x0��aB@<��@<�����G������������������p�L� @� @� @� �%�]��A� @� @� @���PV @� @� @��.p�3�!��)����@����0���x���@<��x(����x�}�}h>`>`>p��� ��L�f @� @� @����a'��A� @� @� @�K$��#4� @� @� @�@'�A�g:�pJ����=��!>9 ���x0&��@<����x(����x(zx���)�+;��U @� @� @��.� ���� @� @� @��� ���: @� @� @�t��3���BgN	G����=�'��`<����x ���x��C�P<�C��C����#e>`a�dj5� @� @� @�@'$;�� @� @� @�]" A�%� @� @� @�:!�Bg(8���Sb�=��!n��a<���0! ���x 4�C�P<�C������|�|�|�H�XA��4�: @� @� @�t��a�t�f @� @� @����a'��A� @� @� @�K�A�g:�pJ8���=��!>9 ���x0&��@<����x(����x(zx���)�+�$S� @� @� @�:! A�	eu @� @� @��	�.�� @� @� @� �	g:C��� �k�!nq{�O��x0��	�@<���! ����x(��������G�|�
�N�a�A� @� @� @�K$��#4� @� @� @�@'$;�� @� @� @�]"�Bg(8���S��=��!n��a<���0! ���x 4�C�P<�C������|�|�|�H�XA�%�Z� @� @� @� �		�N(�� @� @� @�@�HvIGh @� @� @��N8��
� t��Xcq{��C|r@���`<L���x �
�P<�C�P<�>�>4008R�Vv"
� @� @� @�]" A�%� @� @� @�:! A�	eu @� @� @��g:C��� ���!nq{�O��x0��	�@<���! ����x(��������G�|�
�.��j @� @� @��NHvBY @� @� @��D@��K:B3 @� @� @�tB���Pp�3��{��C����x0��aB@<��@<h����x(���������������2���iXu @� @� @��	�.�� @� @� @� �		�N(�� @� @� @�@�8��
� t��pdq{��C|r@���`<L���x �
�P<�C�P<�>�>4008R�VvI�V3 @� @� @�tB@���� @� @� @� �%�]��A� @� @� @�� t��3�A8%��C�������`<��x ��AC@<�C�P<�����������H��� @� @� @�@�HvIGh @� @� @��N��	�:�`�IEND�B`�
perf-patched.pngimage/png; name=perf-patched.pngDownload
�PNG


IHDRiMLB�sRGB��� IDATx^��
�de}(����.����#�`T�7�odE���d�R��b��E�5(oR���E��\B�
JL�KYA�HLb4�C���(���"WDK]@�U�]�ewv���u��wzfzv�����M������<��<������s��$� @� @� @� 0	��hgI� @� @� @`V@�PG @� @� @� 0B�#��.� @� @� @���>@� @� @� @`�$G��]* @� @� @�	B}� @� @� @��	H�Pc�T @� @� @��� @� @� @��� ��v� @� @� @�$� @� @� @�#$ A8B��R	 @� @� @�H� @� @� @�FH@�p��� @� @� @�� � @� @� @�����5�K%@� @� @�  A� @� @��{���o�*"6�u��>�=	 @��W@�px��� @���q���#M�4�#�����J�p�% @�]	��m{WN� @`$���q	��hgA� @�@_$���  @��F`.A�����o���a�u�$���]! @��$u @�r, A������Kf�=�
 @�=� �)�� @� �[���B�c���)���O�?~����W�����������;n�[������zQ�/��~��8��b�+�4���Zg�qD�����b�=7����!n�}K<�#b�����/�����1=Q���n���9���[b�w#��
q��������Xw�aDwo��o�6n��-��;+�p���q���+�w��X���o����-������[������������w����o�������������s�ck�|����7�P��87����<�;���Mq�]w��o+�^C����x��q�9/6�0����9.|��qKD\~�����;b��?����������uA��7/��/m��?L��}��^I� @���H�	� @��t)A�o���zw\��4�V�3��;q.����N��+>7���X��:� ������7������l��+�S=#�����|M��<����8�A~l��k����"n_p	��2�1.����l}���h��w\��kb�R�������e}�������������:�����o\������'���\��[N��u	�����x����[f���������}��u	��?�)�}�����������m�&��� @�@�8O?�t{���b�����\, A�W @� �c�n$+�?}�%��+.��^ybLU+�*������
���3^�^8k�9����c������wCl��������g�O�J�������W��������(��m��@���n�����Il|����r^lxa����q�������������7�kAr��k���][c:6�����������y����������FL_7~���t\kM>s�5q���M�m�����.=/6T��fv<_��Sq�G���������������7E��)��pF�z��Cm�������������~��q��u�%g��ry\~������3Om�����q���	��JV��p��#.�qG�+��;�xI���W @��$I"M�=��sq��������brr2���f�K���a�-�����{w�Y�&V�\���=~�a���� @��^
t#A8���L\�����'p���+6�����\��7��"<� \Q\���c�z��[�R\�7i�rS\����%�Z|&�>zV���"~��������������K�W����5����#��������|6L0����?���������G������pI�f����l��{�s�V�(�Kn��W���X���3b[\�����"��sq�umu(Axz\������l�����/�-1���Cq���=o�#@� Q.���g��4a����U������B��i2	���G��y+�$@� @�@��<�����\}�����y��M���v���q�{n�Xx��X:q6��m;+��|[��k�����8�m��+���;/�
u�q����������<6.��C�/J0.s�\g��������n�����[7_�8� \�x���������1}y���|��o�*"j�;m�}	��/� @�C&�o����G1�_�R0�"L��iEa�X�#�<R�p��>����0���� @�hR��	��G����w������<Qz"�������� <\����������b���y����$��>��e��M���oo�-w����;�~ww��������&�L�R����s���?�P\��C��&��� @������{g/�>j�����������4�����&����~���={%��bbb6�>���"1�hK�%=�R�4�����QG5���'�==�����1�c��J������0�{�����K���'}T����R���[��s�=>>>��:����?��� @�z(�AEWS	����|������`\{��J�
/��	������[?�
�%��^�d���=�������?T�����J����q����[����n�p�DbS�A������ @���D\�H[�zu�	�4���3��&���c��Kv�OZy��s����'M8�I�Z�0�gZ��&���T��� LZ�������c�=v����W��4�x�s�~�(A��g�irp�O�3+A8�=�� @���@��3�^������:M��s�_l��S��S��ONE�\�&vw����+���k����;���2��F�p��/���~s�"��Mq�����W��u�����X�fj�G�6[A(A�tO�B @���
��xiB-M����w��*
�����k�z/��K_���I��Z�V���z��
�4)y�1��J����V�5�D�%�+�da�]z.���W�j��i�a��L�\��~W����)M��m���^i��������� �;	 @���@��u	�����7]�,��t
�������9)�������~
�,ny�iq����������������_�~��8������N� �C�Z==���q�+��WA��8��	 @��H��i�.M��W��	�4Q�&�j��4��0�W��t��oia�q�i�)M����&�c��N��G��O���WK:���&���d���=&�p��L_�5}��=Or���Z��T�
M�I[y�l7�W�����E� @�@�z� ��W���6".�����77�F����>rZ�u�������>�9��*����o�����h,KW�E�U�K%��� ����8�m�-7~���T� 	�>�]�#@�H�wi�0M�����������$MyK����4A���&��GV6z�h�}������_�pa��p����8\�0=��G��"��*m��J3M���R�P"@� @ �=N6���A&g��*~�-���h����7y����~��������.��V!������a�M��Ll��g����p����o
��{�W @��"�V����4�TK�5� L+���t�4��>�3�JK��%�$[i��_+A���= @� @`�@���-�9����^n��Cq�����~8n��s��$��p�us���#F�}l�k�tV\qO���;7N\��������7���J������4n��������gM-�����������o�(�&+w��va��q�m�e�\�������k�ip�f�u�r���:�"T�Pt!@��@����a�{�_����=��pi�&���Q��;R�G�����G�x�9< @�y�u�p&�~��8���EL�W��G����1�"��=�~����\�����*�/	���{��s�tUl���3/�^qQl~���f�Dgb�#���?������\}��E:[���8�C������W��^sbL��62X�b�	��G���,���}��%���<�pl�����ko��wT��.A��]W��\zNl<i�r����~����}�Uq{m����T	���M���@Z!�V��~�~����L+����F��B���GQ�����>�[�xJ�,L��Sm��t�k<8�`L+�}�h���t_��i5bm�4	��'=��?�m��5�J��L��#e��M��6K�-��kkQ6��^���yy5 @���u�0M�m�k������Zfj>�����������w���V�)A8������K���qK-Y�D;M������*�������q��6���K�w����0=�#���o�0nhx
��������9|���������|��8���� ���v2 @��aH�?iB(M�-�SK�	��O��K�n��'}]Zm�&�j	���I�yi���a����Y��a�=��U�pM�5��\��&}��~�a�m�� @�dN�	����;b���(���n�-�LK����M���;�]?�\�p^�w����7������-�j��Xw��c�/n�_y���X��k�)S���p�����-�l��c�[6�y��;q���Q[�p��������i���3��p���W�������&
O���:/.���b��k'SAx����3�6n8t���������yQ\�+��R���G���.a @�����P�,J�Bi�.��K�$���+g�k�(�Z%`�]��4�X{}}Uamm��5i�0�BK�~�����6��L������U��Rk�������G������LM�,M���i���i�����?��� @�������^�S�%��a�u
{m����>�'@� @�@N$s�PN� @��x������-qQ����cS����a���� @��a� ��t- @����=����?�>��8��S��5S������w���o_1�������[�{z/�`�}J.o� @��
H�
 @� px�C��-��
�����~����J���� @����a���� @� @��Om�>��q��?w��-����!�_�1^��M��W��s�O��<w	���;: @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @� @� @��	Hv�gk @� @� @��� �Us9Y @�^ IJQ.�0�'^0��q @� ���a�d6 @� @��h�	�����G�z�!\= @�r* A���s� @������� @��� ���� @� @`d$G��]( @�C* A8�
�� @� �+	�^��/ @��# A�gG!@� @���HMS� @�FT@�pD�e @� @�]	�v�lG� @��lHf�� @�r# A���r� @�h( A�c @� @�@K�-qy1 @�2' A��&qB @����a���� @� @`9	����� @��	H� @����a���� @� @���}'w@ @�tU@����vF� @���� �6v� @����p���#@� @�@�$�Nj� @����a_�� @��� �� @�F[@�p���� @� @�e	���l@� @��L	Hf�9� @��/ A��6r� @�8����A� @�-	H���� @����a���	 @� @ ���ngG� @���$��w @��' A�C @� @ ���n?gO� @���H���	 @� �U	��r� @��_@�p��� @� 0������� @�]� �:� @� @���}�v0 @� @� @��� ���hZ����1~��cSMo�� @�z"P*���?��O�����) @��V@�����N�k3�UL�{�a�D�� @�m�R1����(��m��� @� 08	���;2��$[��b @��^
H�R��	 @� �s	��;��Hv��^ @��  A�D� @� @���$g��Z� l���	 @�z) A�K]�&@� @�@�
;w>�D""������������>�O���C���u��[Y�?��?�������������������7Jc��wG���\}����������������U�/����'sq����8��c�=��:��� ���� @�tA@a�� @���� \���{E`��I>|��?*	��*�G�����7o�G���7c��C�pd����R���� >�l|0?�
�/�/�/{�`>d>d>d>����
��%g�@K*[��b @��^
� ���} @� @���
��qj��{E�G%��?d�?��A8Um�S�4>�'�9��|������h$�G�	����G�G�?������|���>?��y�	�������� �y�tG@aw�� @�*��h @��@���g��w���o��w�Y������\����1�����q�EqQ��q@�8P:XWA(���H���'���~`���8 �y�*��ud-	� l���	 @�z)������M� @���,X����s�� l�f @��� ���= @� @��*���P:� �D�� @�]� �*�� @� @��*�-�x�� l�f @��� ���= @� @��*���P:� �D�� @�]� �*�� @� @��*�-�x�� l�f @��� ���= @�tG IDAT @���a!"���/� ��`��O���wF��4^�+���J�q@Jc��wG���<���������8 ���8 �m��}��:�NTv�g[ @���
� �*�� @� @��*e���,�����
B��]��]V+���x ���80�8����(���M}�����8 ���8 �:� �wJ���)���M8� @�t_@a�M�� @�}X� �}�v~�|3�GE@d�� ��vH�1��0�?�����������p���	�����x �t<�y��K|^T� r?t?t?t?�y�����y�
�>fc� @� @� @����A���~F���^�c�)�0�W�$�H+��d�%k/��8 ���8�z(�}:�V���L���o_wI��??�S��86�;�������������1~�q@T:E���(��lL�+���&��2 @�������#�DL���&)�b������_��~lL� @��� ���hY@��e2 @�,� �% @� @ � ��D@�0'
�4	 @�� �p�85 @��Q@����E�	�N�lK� �
H� @� �
v��qb1J�QZ�6���w�CL����Th�����*����8 ��@�@y�1v�1�����q������������h?��q�I?�����8n�?���#���8��P��@NT����& @ �*3�8N� @�}�� ���9�{E�G������C%AX� ��n��������G�8�� \Ym���yR*����� ����g������n�{?(~���z?h>`>0��}��:�NTv�g[ @��T@�~@� @��@����Q{���+<*������� \Qm�S�4>�'�9��|������(O��J���������[� ��A�~�~��s'�I������G���c�?��y?���cY��*%�	�D@aN�i @�2,��0���� @� �G��5k�����7Oj���������A���
����1�����q�EqQ��q@�c�[�0�S�?��R������G�Q'�P����W�q@�����
�>fc�@'*;��- @�@*��P? @� @��T`��P����5�z��� @ ��Ym�E� @���
� ����h[@a�t6$@��
H�
 @� �
� ��D@aN�i @�2, A���qj @����
�>b;�NTv�g[ @��T@�P? @� @��T@�~@ '*s�PN� �a	�7�S#@� @�@�	�BD$i�����������qj�������S����8��8P��D�qL�d:�l���R1�?��Xu��'�^�O\�d��?�����}��1~��G�����}��:�NTv�g[ @��T@�~@� @���
B�H����7���M����D?��7`�����p��8 ���*��A�;�N����{�q@�q@�^PA(QL '*s�PN� �a�n�F� @��>
,H�2��3�{��
<*�� �����d{�����h~`~d~d~���@��pM������I�T��x �y��������_�_�_�_>�2���
�>fc� @� @� @����� \�,��������L�3�G�G/�G���c��/�db*z���������R���?������!>������o?���;������b�� ��Ol��_��f�o�
y�_���_J���/�o������Z����yPA8���hR�|����k����&��2 @�����7�VQMv��&��R$'������ @�% A8(y�%���a�`^N� �H@�P� @� @��T@�P? �	��4��$@�dX@�0���� @� �G	�>b;�N$;��- @�@* A� @� @�@*P���G����.�(����b�\�4Z��;��|����k���Tp���x������8%N���8��8P���cl��1v����`�����(<��HN���~"�N��v�h|���B'�U������'���c�?�@Kq@�D1��� �IC9M @�@�Tf�q� @��(PWAX;j���~���|sRt�$k��c�����������������H�s��V��x^.F��/�U�y��3m���;��������>o�V��?�?�|�|t��Q�}��:�NTv�g[ @��T@�~@� @��@����Q[���������0���j������)>��Y����G�G�Gy�U*���S�
���lam
B�#�#��;O>�,�����L����jE�������(��TJ���
��4��$@�dX@a��� @� @��KT��I��7J�}�����1��O�����s�Su�������!����������q�����J������h|��+��_�*��~^�������o���>O3��W�����& >����.��Q�?� �c6��t"���=� @��*� @�HTB!@ ��Ym�E���@w�� ���3%@� @���� ���s# A���r� @���
t7A��HNx]f��� @� @���*�9� �IC9M @�@��� TA���vj @�8��
B�@N$s�PN� �a��&Uf��� @��K�(aD�@�$��F�� �u��&Uf��� @���>b�i�����������}[$S��x%^�_���8 ��@[q�������#c��umm��R�`��%���>������t^���$>���8 ���Rq����r"��0'
�4	 @�PA���qj @����
B�<����������7=|��7�|�K�q@�4� G�q��8b{qDG�q@����*���u(�� �D�� @���
B�� @�R�	�Z�����J&�GE@d�� \Q���c��a<������������a��
�#b��S;����uk���x��x`>k>k>����������-����� �(&@� @� @� 0B*U�uw����7&&��f�^��HVLV�L{e��|K��?G�X�_��M��C�![������0>���YaR����(�����!��x��x���|�& >���x T�C�p~<TA8B�`��o����%����a���� @��_`���c|ru5A����a��(O������ @�% A8(y�%���a�`^N� �H@�P� @� @��T@�P? �	��4��$@�dX@�0���� @� �G	�>b;�N$;��- @�@* A� @� @�@*P��sG2�8em�J�V���!;�`�o�)�wv$+V�v�N�����h<���8 �)�<���\�������`��#��gu����y����8 ���8 ���80�8��P��@NT����& @ �*3�8N� @�}8La�,��F��W�4�������1���o�}��c�|�w�7�w��F������?,]A�b�^TA�����G������2222Z����������{TA��l�C�D@a'z�%@�HT� @� �
T+k�L��+<*������*'�
������$>g!>�/�����~T� \�������X������������y������j�j�j���_UJ���
��4��$@�dX@a��� @� @��KT.|&������v2���8�?z�?��AX��j�O�K=���7���^�O��_�5�g�Y|�W�1?�WA����"{>�+*\����_KWH���a|,UA,>���� >d���
�>fc�@'*;��- @�@*��P? @� @����l�������
X�aFO�i @�dV@�0�M�� @� �W�}�v0�� l��� @�	B=� @�R����� �IC9M @�@�$3�8N� @�}PA�Gl�"���
�N�lK� �
H� @� �
� ��D@aN�i @�2, A���qj @���@5AX��$����~��q0� \a���iF����y�����8 d=�<���\����|"9c�����Y������y��w��8 ���8 ���@��
�>fc�@'*;��- @�@*��P? @� @��T@���@2�Y�fu�O���,�K�2��~�bN��|���j/�K{�������8 ���8 �*� �(&��9i(�I����
�7�S#@� @�@�I�2�K���W2�|�����|������+�������7pp����RA�:&�;�Ao�'�+����������_�_�O��?�?�?�?�|i�������A��*���u( @� @� @��X� \�����?��x�o(~���x�K"&VT�F�?���Z�r|�ap�A4��?�onm<�;
�=�$I������{�BR���gcl|E��yI�Z�����r1��~cG�t�{Z��x��h~j�����i�������!~������
�A�h�@��������\� lrC/#@�J�B��j�p����&�4��gbl��j�p���|A� ,>�5&�M�;�% @�L@�p`�L�5	����� 0������� @��� ���=���aW9�� �{	��7� @� @���$F��Z� l���	 @��. A8�-�� @� �;����O&s�����oeqX����s����11�%;�"N�����(���80�8P(��$)GL�Yv~X��hD�@$v����1�����I����=1y��w������q\�o�G��q@��Q�*{�|�g]PA�UN;#@��^@a��� @� @``�� ���R�$��������?��?�W�����~������������������?�� <|�,�},�4s�
�����
���_|���������A�}��4>�O���?����#���q��`�*��u`�	� l���	 @��.��p�[�� @� @�w�
��P�f��7�S��������8A������#��#�.>��+����o6��\���	-�*��5���5/ip����\���[c����������F��_�_�_�q1�]( >�����,>����]a����L��*��ig @���� �}� @�L`A��o��}�7dx����P����+�����{�z-^9��0���?���77{6����W��������J�=1y�����m����x��������o~��&�!�A���~�T,7��ZPA���W @��]@�����#@� @�@��Y��w�gZ� l���	 @��.���p�+nn
����A���^I� @��PA8XG'���a�T^H�	�#��.� @�=PA�V;%�}	����# @ �*��z�� @��PA8XG'���a�T^H�	�#��.� @�=PA�V;%�}	����# @ �*��z�� @���&�D�9�Y����X��_���2^�+���J�q@b.A�f��P��hD�@$v����1������|0�O����|G���������r^>��'q@�q@�8��p�	ZG'���
����� 0*G��]$ @�z"���7�}�7'*}ce�����|3�7��9q@Z�*[���x�������8 ���8 �sq@aO��vJ��*�oj� @��<� �s�9w @�V@�p���N�i	����� 0�#��.� @�=� �	�� @� @� @�����a������{���<*�� �C��b��S#&WT;��d{�����h~`~d~d~8�����#�$b�1���{�HJ�<�'b�������R1J?z4&���K�/.���K�r��������/��x��~�~�~�������L�:+�fn�LL���u	BH @�#-�&�����]�!M&��l��5GW��n��������W������ @�& A80z&���ak^^M�z	��obH� @��^	H�J�~	tY@����vG����a�[�� @� @``��w`�	H���� @���� �&v� @���@a��'���)k�4���X#��3��SoxK��Tuqq�S��N�/�G��x�q`q��g"�����.;?,��I�`$������1��u��IJc�w��W�����7�7��.;^�K�����F�q@F!� �U��~	tY@a�A�� �w�yoA�O� @���	� T)��9��
B�\��%�\�o.����~�~'�T��~������uwwo��n\��������@���
���f�@k*[��j @���� �&v� @���@��p���������T�����U��N����?�G��G�]|�W������E	���Sy�#uk�/?��E4��R�n
�����^�n$�������%#��E�!>�O���$>������C*{�z�_]PA�eP�#@��]@a�[�� @� @``*~#����!��G�7����� �����!Q�_�o|n|�����g������a����5������
���Ks����rA�7�Gh<����������?����o��5PA8���hM@ak^^M�z�C��.� @��Xf
�^�~	hU`qa�{�z @���x���X�����!@� @���T�i��R?iI����?�o|4� ���}���H@��?�?�?�?�?�?,��c�a���<b�@$������1��S� ��U1�%V��
�������-~��#����;�������
�v~��pTR��3�*s��.� �]����7 @����5G��]j��A���s� @���<A�����Wi� @� ��=@�K�PA�U�$@��X`�	Bk���8u @�F\�n
���L�[y�9��s	�)�����
+N�S��S�C{�?���@�����D�K��]v~X��=���� IDAT���5�u4�HJc��a�����e�[<��cqI�q@�q`�8��p�3�.??*��V�� ��}av @���
B�H}sx���w��**}��x��x�
*��x2��q`(��
B�t��z?��V�q@�q@h3� ���kJ�C��.� ���
���lI� @��� ����# A���r� @���H���A @� 0������� @� @� @�����Hj��{em�a�����c���bl��Qz�I�C�>[[|��g�'��A���?�o����oo$I9���+���=�s����_�N��')�0"JQxjO$��QX{JG��$)Fr��[�"���_\��������y���?����C�y���")��r���o:z?V����I����OP�����X����������c�l����UO����;~#��������������X`t���}{�	����7����1~�//b�M&"��t���&��?i�����1y��;��������m�?�|��������c��o�	���(�Z[M�|������G�.�s��@�'3u�N� @��������t���(Lu�as�t������ @��6�%�������;w>Q_s���
Hf�a� @`@��wX @��� ��P[�p�������0��k#@��. A���- @��^@�0�M���[����_��-f\[�����}9������7�c�����Z/�c���_�����q@
��F��"�XSY{�{��A8�}������5O�h>�$�����<����cG�G��~.�C��8 ����ER��U���A�s���|_aVR����2*u @��z�� @��PA8����KVA�2�7~3��o��J*}���~��2��~�s��8 �NPAh���3����w��x�q@8T���R^Kao��J��*�Nj� @��\� �u�9y @��	� l��V���d���a�[�� @��+�8AX[����HR�aDr ���0ut��k���$�(��rL����� @���	H��z���p�[��
����4�!@�tE@aW�� @�@�$��^>���g9�����g;�����skn����i����m�I|�����y����A���5WWn��[XA8�>�
����x�������T��G������U�����������3�������>���������|P����c*3��uj�T� @��*� @�#(��p�7�l
����+��Hv��	 @���a���� @�hO@��=7[-�&�R��OZR�w�CE ;��� <;�������l�_���������?���cv�����8L��� ,E�������(�h{�������U1z����T_����<b���G�z�������/�/�t���g��������@����}��Ga��L}~������Z>Oa��I �*��:�� ���7wD @�PA8�&�������z	��obH�hI��-qy1 @���� �v��U� �@#8�H6��5 @���PA8:m�J	 @�� ��$P�a���������C��A���P����Z���~��~�=�����,��@��@a���5���=X����H��G$�� <���d������� ��u�3��8c����8 ���H���5�Ea�q��8��SA��L�����
�^�? @ _*��^�� @�@WTv��N"T�0w�a�oj���T�oFo�!����8 ��@}PA�?�/�/�/���8 �����*'���RA(ML '*s�PN� �'�}�v @�YPA������H�����(	H�Rk�V @�����y @���� �&�IJ�q	 @� @� @�@`A����������g�a�����o���/�r��(��(L�����O��*`|r|�����g�d�h�<��r�kODR����+�����o�8ssD!��s�����Q(�b���DSQ�8���}R*E��O��~j����BiW$S/4?�����5��d>g>g>g>g>g>��������{"I�Q?����k��7~;w>�T:�R?�E>�����?�����}�x���gK�W����a���70�����������a���c�����g#��(�]SM�q��#�}o5A8�:i�0����dlu5A�~�M�����:cC]�pwl��������z	��_������f~k~k�?����3w�N��}Q�x^5A��)�����1z����D K��'d�� @�@�K��d���$��������� @�XB`^����$���%��
H���� @��� �|9A @��� �����Q�pD�e�O@�0m��	 @��� ���} @����aF&�U�a�Y���[���H�+�us����l�x�����(�zE���� �>���9���������8 "$��D�KQX{Le����_�a��t�]��� ����5O�h~���q�k������S|�n
BqA\�;�N�s��q`�@yo$���01]��\��D��[a����xDT�h��l @��*u
 @�#(��p�7���Pe\G�����}3C�o�o�o�qi�o��8 �!� �O��O�S�T?5��q@�Tv��nT��
��$^��@�Tv��	 @��PA���s� @��PA����	T+� �u��am
�1�������#@���@��]����B�-����c�G8�/����Q�8���JJ�8�������S���Dr�+:���	 @�8�����%�]���PA�ka�'@��K@a���� @����aW�$bAa�Y�5�W�=��"�?�?,��������<�����������/��A��r
��?��/�U���J���|���
���o�����:c��������u�����������qu��U���'���P�p������h���
Bib9PA���r� @��>	� ��� @����
�,�F�����n>'?J� ��v� @����A���W @�:	��k�A]P5AX��}ZB�����?dc<T�o���;����(����U���U��C����*�R��G����x��xPI�����
�w��>b������W1z�����Q�8��@��O��_�>b�z����#FO7?9$����!~�O�O�O����?�/���2���7���(L<��/>�(?��pP�Y�%���
���� 0�*���] @��F*��.	X��K�vC��� ���� @��%`
�|���%@� �	��0�I�
B��@NT����& @�O*��0 @��$ A��������AX{��+����![����|�n
�WDal�n
B�U�V���}T\�q��q ���n
�$�;���A��O�����~q�`��'��{�����q�k���A�D��[����/��3��[�7.�
qC�q`(�����EabZC���u*s��u��$��p�Z�� @��PA���W @�:�C���� �2�e���9�UA�N��o:���8 ���8PT������8 ���80�q@���.��T*5��ZPA�"�� @��\@��7��#@� �H@�~�%	�.A�
�^H�Z��	 @��� �W{9[ @�]� �
��DH� @� @� @�FH`�a��K��{��|����3�FiW��+��������?�o	����o12_����?����{Hv��H�({�����8��~#�+���O�p�	�v=Q.�����d�������*�_8�?������b���(������]_���ODr�O�������H�����~��������@����B��������������wz.<�H�1���XI�c+WW��������wa��$��lk?�E=�^��dc|�pC�K/�����8�M�G&�	�l���"^�����������@�i�0)�bl��Y������������/:��!+���aq&���+b������W��L�s�F<�����(�s�����R�S_�x��������?]:������X����l����������#��x?���������
��'�����XDR���U��_�����k7�y�h��I ���������.Ax|�O�� @��P�� ,���������X:A���
���3Q:��_t(A�!�l�L���Of_s����#~��=�j�&@� 0����*����i���# A�G{!�s	��; @ W�I������?��;�������-W�N� @�@$��J�:G	�|���a	�n|�N�h  A�[ @�	��i�~]i���g�����V���?���5�QXQ�����f��j��>*.���8��8��z�n
�$��?\��I��(jkn�(�[�p�����We
�g"�z����\�x���5��wC]��#_�v����\��?�?�?���`�g��q&g(|�kuk���U�Z�����~WA��T���P@a��6'@���
�!kP�C� @�0*u�n� �a����T�� l���I��D�Id����8 cPA�_c���k���M�q@hT�����*��r�?=PA�#X�%@��T@aN�i @�hC@ah69�@���Y�� ��8�M�G&�0u|�O�� @��H`q����c�E'�;ji�#������5GG���%��pp�s���A����-�"��z��64X��m=�b�%@� 0�s�I$��[u��b���� �
�����
��; @ O*��Z�� @�@g*;���b�e*k�t]���+�|��X@��f�X\A�"
S�����%��n������?����F�?��?�^Ax ����0���[����}�� �?��?�_
��������������A���+��|J~����
Bic9PA���r� @��>	� ��� @����
�4����5��A]��
X�px��� @��h�����8�� @�@�A��V�s�&�Gm�~�G.����P��x�T�4������7�Q��>b�x5^����W�����=�G���������XI�blm�h�������������/:�:A��gK��(�	����#^~������
g"�{6������wG����_<�������b���l�5����������K����_>o����7�������9����Q�O�
����XDR���U�f��k�1�Qa�[W��@�Tf�u� @��*�o�� @�%��pP��{��/va����3���G��G�5����������������W/ >����o���n����A���
�g�t�;����~�*7���>u�
B�G�G��9��[����F���e�i�����0,�����wX�w�����ueC&��p��� @�:PA�!��	 @��H@a�+'�ZWAX�4���q��![�`.A��A������5�W�5[�U{h�QqY�����d�3uk&1����5O�>i��������5���{}{U*��x��uk.��r1��{#������~qF�g�3q@�q@�80� L� �c�����q��8��0'�\�I@�>@� P/��P @� 0:*G���u�*e������{���^|sCa���������~�_���8 ��XA�f��G�"]��x6��g�@�q@��n���S7�S�T�+�8:PA�!��	 @�C&��
���S����>����?�U/�4d�.� @���T�
��$��E]��
Hm��0 @�@[YI��C��?�v���8n����q/n�zlD� @����zG�$�-j @� @� @�2,��a��Gc|��Q(L�������m�/���k3L��SK��(<�h��N�;���HJ�����_@^��X�����SNl����-b�����7D�������}�c��/�r1b����� @�@����c�,:p������
+W����������/;+V�=)���8��_4�����F�J1�"����S�w`&���["IJ1y�[_t��<�������"���sn��� @��|��'����Q:��������c|�	QxjG��x��iCu}.����;K*���~j��f���~|W�:nC]���(����^_Mf������o� L���;����P����QX�����o���X���_��_;��N��Kn�LNyq���-������1�������I��#)���L����}����o^���h/��x5^�2�F,^%�")���������	���U��a� �y�ocb����O�����S~nv>�&����J��Xut]�����Q��L�W~5"fbb���_,�"�����������G�=���o�8*��f��������9b���1�Gs�y��$>y?����c��cO�(��x�P}>��;_��?��"�|,
+WDL�|���|�����U.N���}�R� ��uX��8A�7��{���r���4{(A��y4N�YAx(A����� @`I������u���a9
���%�.&��_� \_I�J��Xu���K&��?� ������'>8���	��1��C @���=~o]�pxT*	���x�����\�+���a��3{D	�>5�a��� 09H�=�1�����.�'�{��� @���
H����w�$�-���I��q$��0 @��!� �Ft	 @��� ���c�Y�n
��������#F�g��^(�� <+�VW���l_�����s�I�6��5��uh
������s��gs��|�A��u$��T��;��b�n
����yU� |.b�x�e��k�w���W�M���>������o�A��E��d���5�8��uk����u�5OYz~?���(���n
���b1�+���A8��u
*����v^/��7�o\��=�5qM�$4^�0���}���0k����/��Ca���]>w�]]jw*��0 @��!PA8�� @��)�������g��s��1*���WA�y��o�
�7�|��O�
��}�}c��*G����o` ���8 �T�������(UA���n��]a�AU�	�a @�C,��p��� @��B@a/T�s������~��Xu��(&fO�P����5b]����\H��� �R������+�����(����qNOi��_����Hn�LNyI���-���� li7Q(����/b�xiM��	 @��Z���Hf��T�'b�QQ~�����3Q��L��o[��b�n
����'��}M���Pm; IDAT?���w
�!	 @�T�*7
���|!V���E<�hVNEL�l�����_@a��3{D�}jk�	�a @�C ��������� �jD�����/��M~=��b��4A�?f_SI^0�.� @ �*��r���*�-���� �S�� ��� @��@�k	B�C�\ @�@*�@��:w��A�Zan~���]���
1�/�^����
���\a�R^G� ���
B�� @`4T�F;���Tvn84{PA���TA�'h�!@��@��*��7� @��&T6��%*��%�:���0��/� |.J��k]a��?"�����pO$�Fa�OV�Hv��r�98�b)�/~%
o|mK�����(���(�[����+b��'�<^�����EL�m�����p��E���������xo<O���,���|<M�ER���������F$�(�:f�o�3�mL������S���C1y�������*��Xut�_�����Dy�3Q^�h
�j>���{����A������|!��?�o�w�w�w|^��������Ws	�7
�������X���)�����rE������|�����
By���\Z�v��b�������FrpO]�X�*;&� @���� ��v� @��PA�G{~��/z�7>���+{~��L��w~�U��/�F��[������ \�}
��[�KU.�}��?o�_�pl���H�~�g���a|�Wl����0'����(A8w��
�rV�������g��ta���R��{�/SA(��?�On���7a9������������?�7>�m|�_�px��RA�sO>z�
B�o�����*��3��_�A��&WA�'h�!@���
�!hD�@� �O���v�<,H��$�e����]���>WA8>��F�X[���[}\��?��I�6�]������Qr�������m�g�h���F��*�&���b^�����8&	����_���8���s���7�7�q�}�	F�56$l�Z����@4�f������Z��Nuuu��tuW���T�|���h��z����O�UO}����CE��Q�����������7m\��	5��/�����wB��������g� ��|�n�����&������������������9n������~jv����er�V�P"spO����58� l�� t�?�T��V)�~c���j�>P���ye|c|sq����>�>���Ys}s��������a��������k����x�5�1>��;����y*9�[��SAXc�N����N�y   �*}`D�H�H�H�H��� �'m���	�j�z��)c9s�?9�����������0YPF!���8�q0���� t�g��������Y��
>�������Jm���D;1^�1^�T��wV�������8)[�/9�y}��Axz�IA(�l�����k����e�:r+�L�����fC�� �������S��Ha#�wk�w*k�
�:�iH�H�H�H��@A(���9#�K�	z�'}l�H�H�H�H��� ����?�`��+��@?� �����:��iH�H�H�H� A�����vN��/�d�\��H�H�H�H����j9���.AXo@<	�	�	�	�	�	�	�	�	�	�	�	�	��@�%�]�b 9;�g��=�������'��,ZjO��g��L@f2-���'9�*�U������-~���U�Ry�?�>k��b�RH��%U�h,������i�g�$@$@$@$@S"�IA���tAoY1�P21lT"��d��D�~d�}��
6)��a�W@���y
z���� �Y��-y���fS��x��#�[��������PR�BO��l���d[��TB�t������R���z20Py�1%��$@$@$@$�z|�Ci��FS���N����&N�u.���+��`D�{�,�hmV�^o��d^���� ��!��6���6� �v��E`=�W�"�u+���.�Crr4�17�U�M���a���!r�*j�t�_��H����E������H_A�)�6��J�%��#�$��6��jy�}u�C^���xL�?��=����oJ�&��8�k����aq��KJ
J�=G�H��?}�����8���� �#�{�m��i�j�-AXb=x�'H�3h��ri7R�_��Z�n(���5�����"��Fr��
�0����Ab�ZV��O�i�9����h���)���i���~�����\��t}l����A���Q�o�L�:�V_�7e��!�]���q�H��5��c���� ��=p�-A���k[m	�H������6d���%����a�-� �b�65����A�	�&$�Fm	��)�	B��gK    � �IA��-Ah{��K�P�m��4�?���[������� �!���
B�q�awAAh&�`��d���>4_��H��-AX'f�!�u"���	�	�	��g	
�b������cf��Jd�zl	���H�@���p<��o0����		��
����D�����+� � ����a�)8�������K'�++����������c[��������!E��/;�������E�,>NH�W�O�W�O�W��/U)�`�����_�[�H!Y&A8M�sT2~2~2~���9�&t��O�<<>��\�;?s�������� ����N�g||~\�����
Bf��� ��7PAX�<		�	�	�	��@#(�$�;RHPA���(H�H�H�H�!	PA��fc�g��MAhe"���X�P��:p�A��q����~j��a�_����U�71h�AX�~��mo� ���E�U5N���� t���A�������N�����E�|a````�A(� <z��1�1��� �}o�j
h��25�s^���i�n�e�A(���n�A�A���\
����u	�mP���[?NPr�q��`�������o�_���?9�8���Y��}�sn���({��� B�{�����k���T�@V������.����.�y   �*���5�qb+   �� �F�kB�
B�iQ|��
���qAa������o��
$�;�������T�S�PA����������s��uG��;x��u�'�	*���=���&yV��
���
��`�IH�H�H�H�� tgG*�qb+   �� �F�kB � ����r%�
�������
� ��V��� ��9u��oO2-	�0���d6��U�pm�'!�0����.-dG!�s���;�	�	�	�	��@���
m��i�j+��j��L�A�y���P�0�g�9�*���5hYq;�`�~��w�n@��9�7:��H�H�H�H�
E��U����k��~��CO������s��%A��`���&s��'�����h2���T"LN���H�H�H��@����'u	�#��u�N���H��}9bZ�n(��!B�����_�����w�0	�]@t�����+	�	�	�	L*�	,�;T���S��$� o��'� ��wsW   hL5KRA���^�	�	�	�	TK�
�j���l%@�l�|�qOK��
��%� �t%   �YG�f	B*g��p�$@$@$@$0K	PA8K
�aWM�
����x�iIRAX�!�>�}	�	�	�	�&P�!�t1    ��A�
��ag�r��	B@���>����g��J�e��+�u@8��k���Z(M�q�L��������������Y�Q��D���J�HKA��U�I]G��.4-ZWb�9�/��J&l5��K��)�$������U�?�E�������������s~Wy}���$P��(��8�k����[V��_&F �%��;�����������&-����� �#�{-���A�����?���i�n�U����fH=���-��1����� ���D������w6l� �2>�Y�U�?����'^�x}����p=#?�������_�p�L��1�����������S��$��+�:	A���W����*�/�����TN=���#PAX[�a]0�$$@$@$@$�T����n �
�1w���H�H�H�H��� ��i9��)������'� ��x�M3��\��IAh�����
��� ���T��:�/� ��Gl4jF�h*� ���e��
B���~���W=?��o�W�7�����*�g���]�����Y/�_*�������2
BZ�X�j��}NA�A���e�YdN�ZFAx;�������a*�0AA��=�����m�T�i��|�?��J|%o����o=��V��VZA���Ce���(|\���Ja!�����.N@a]0�$$@$@$@$�T��#��8�	�	�	�	�T�#s�5!0.AX���������
B5��f��V������������#����y~������J�'�5�: �V�����S���
��hZtY�
$w�s�A�n��� �C��Uu~������U��9����_��<!?^�x������)_?
������~[�PB����Ax�M����a+��� t�O��Z��P�5c��� t�A�@�
CAh�A8��gc��������?��L��S�f.�+�+�+�kc��b��6_=�[�0u����W���5���� T���T�3OB$@$@$@� @�;;RA��[�	�	�	��, @�,02�X���7:�2�3��������2
���w�7�<�/Ga�����a���Hc��t�k����8+���� �n�79x���r���x�```��q������
�;le�m�"�MAA��gJ+��S��ZV�VFA8
�yB
�Y�/%?������������o�W\�200�:LT�c�9+�1�Z��W�/� �I��'���.����.�y   ��FP>�M$�������E"�������`�6*���!   �����6b�A�	Bo���`��.v`��.�y   ��H��x��'r����7Fz_a����Q�	�	�	�@C`��!��N���K�#��H�H�H�H�H�H�H�H�H�H�H�H�|C�����1�h������O@��u�\ ������{ Rf��{��S�T2���@q�9�!�����r�DK�b�z4��!Zb�2-����J0�5��t
j�$��E��14D�@(��M��@
�<���X\�U���-dfRKC������H�H�H�H�f�@:	�xj�Zh������J��\�;���!p��P�5��iW}2G��g�t.G�S@�!�K�8�H=�"�����������m>k�{y��0|��3�!�|+�C/CYzD��n�t�=�B����uOD$@$@$@$P
���k��}h�S5�y�m���oA��B?s
RU!VtO����('����cS:wn\���]ik�6�(�7������K�	���H�z���P"�P��b��������g$��f�?X���q��N�g��>��+m	Bg�� "�\R����{�����\6����L��t����������K+���$���K^�2)���>�J�/���e&==5f������<y���'y�'�;�]#�����W����,�������7�	���	B
J��\�;���G���\�P�=��%w�^K>��k.GxN
���� ��o��OA�@n����2��o;��s�XK.A�g_�:�~z#���6����]^��|��>���wC	vC������e���L����\_s�r��z��-�/��Z��kjt2'��y�g}��3���-�����zO� D��S�� ��927|iZ��������oNA�9��km	�w����-AH�m6W�O3A�2���3�H���� �p� �%��Q�	��� �%�R�L������*"�[�pr
�b��*ac    �zH'�=�-[���(*� ���[m	�G���l	�;s
B����>Y&Ax^AAh&����RM����W����%��� ���>�y   �.CAXLz������ �
Z�q[�����m[:A8��q��#@!T�7l� 4&������pj���9y�����?�?|�l��(����������
���?#y�Lv�%�m�T�
0~0~�`����TT���233'�������
B>O��������c*���LRA85���H�H�H�H`�Pf�O�a}�Ia}8�,$@$@$@$�� tg%*�q�s+���zs��?�5���`�c
Bo���&�w�i�/)� 4�U�A���A[
B����&�s���A[
�����I[
���G&��j�9~��.d�+�/��P�0����� �JG������5����������������|������q�q�q�q`Rq�� ����1�7���jJ�co�jf���� ��k��� \���/]�PB�{�V�0=�#W�����^�HK���Zz��*[
B�������s���s^�_�L�^4���u[�N�_�z�!�e��a���)�O��� ��]k� �s����QAX%�q�� �?�M$@$@$@$P�����
BW���H�H�H�f*���
Bw����
B�!YxC�
��e�� �?�B~�����7�gg2PA�J�E!�wC�o>�p5�y������:�q�q��8@���A�;N��_#�������*�Fa��� �0�M$@$@$@$P������&6"   �YA�
Bwf���'?��+g���X�B1j����H�z����D���u��M����V�g�?1:������23
==5���}�	�	�	�	�@�	���F����cN.�#�R�n���<v�����
�Ai���@��;!3q$y�5�#<'
��
}�Q�����$��,���@�B��������< ��,��kP��� l�,&���'"   �`����������Ew;���PA�;�N~@LN�]��IrD`!�( ]C��nd_Z����@{
#�����HE$4���k�t0AX [�	�	�	�@������ t���H�H�H�H`V`����� t����� ��u��U�|��TN�B��H�H�H�������+LlD$@$@$@�����L�;N~nE���[��� �X��TN�B��H�H�H�������+LlD$@$@$@�����L�;N~nE���[��� �X��TN�B��H�H�H�������+LlD$@$@$@�����L�;N~n�O
���Ok�����}f��9�
��B(j���Q�w=���n(�n(���8f�E��w~)5h��4���"5$��m��� ���m��C���� Zb��/���P��PM�1����L��t����������{hoB!^�'��������Sf��#Pc�\���!~4��$_�_��_��������������/��g����m��c�o2���W�m����!�7>��
�Ci�B�=��%w@fF�|��\���dS+�%���{��>���4w�?d�v����@f�gw@� 3��HG���	u�%�m�^}/D��������a����������^����o���������;��x}���T��+� IDAT������������T�6��F��B�=����Ni|c��������*m�*g�?� ������qo    �� t�T���F$@$@$@$0+PA���T����V6��7��������s����5�N
�N�g�('�w�����1�X��k��(� �z������v��
B���a^0j*���><^A�������/���o�}��#������H�?:�q���'�������2
B
z��2
�5y�O�w
�,��ke����4�Q���&*k{��C���Rt��<?�����0������z^ZA���?���-��(�_���~���QAX�������pj.@���qo    �� t�T���F$@$@$@$0+PA���T����V���7��!{�wSA�6_�P�k>���U��������7�W�Axm��f
��6CAh� \��AX�?���|
B�����?k*���b
������|
�u�7���CNA�����%P::Xl� ����4f
���������?��3;�����|=��?���]�6��,(o���{���X��5� ����� �
J�� �j��5�p�A8v=��$������������A�HCA��au��j��F{3A�
BX�3��������W��W/��?�^�z�����_�0���z�� ���y�i*����5����D�����8-������z�
B�����
��y�S���I�H�H�H�J����[PA�
�	�	�	��� @�;3SA����[�jZ�����b
BCf)�Gpm�MAhef���M��v-*?X�b*��ml
����,��� ��j�T�G������� 4�^�~�s�?W�p��-a�qj��(��d� �)��������1/�W�+�U��w�W�W�Wu�W%���m����P��6�(����5N
��~]���)[�)(���T���}s���!�
!x����iYw��CZz�MA�y�yZ�y:
����rss300L>�5��y�� l|J�l�)O�D�� ����
�/�m�N���_O�u�
B?���U��
�����$@$@$@$@�� ����d��:��w�^}7� l��I� �j��H�H�H�<O�
Bw&���'?�b�����rlLV	�	����$@$@$@$@�	4�'Fq�?1��t�#��F��_3AX��lA$@$@$@$0M� t�	Bw������~6�F$@$@$@$@$@$@$@$@$@$@$@$@�M�q�2	={
������@��=��&�2��h�F�9n9F�4D�I�+��"�,�����B;%11����C& �]�0�����@6E+5�����{"�F���l��c��0�>[��Q��y38�'���xe�N����(�������*���!��P��+�jp������"��b ��4���H�H�H�H���)�x��W"��
";�������g5����c8�F��(��ES�������P���
��J!Ehu^��������`�k~�<��c@l�B�Yh����F���P.������c�B��O����io�e^D�Q��	�	�	�	��l&�~�u�����s}CbH�4#2<������ ��G@}�������TU�VOi|��#�y��7Uu�C���hIU���������tqC��NE��w�@&�&�[R�l��� >y`3��9P��C��!�R[��#��P��~�D*e�.h�\a+6:��R��o����OUg Nm���:��)(z?d�l[����28A67����o��g�'�F�C�IO�?����s���}@��������78�w����.S��<�He:%���������Ch���������������>����Dt�%�p�sA��;��������7	L��?�����8��������G���$�l�����O!��
�����|:��_�:��/=����)C���P/�BQ��C4��#����u��ud1����
28����N�]����8��k�A�$O���^3���'2�>�k����D��=��c�������d������!Z�����ky��
@��������'^�x}b�a������1^�g��m��=�����%�4�����"��?H]���� �a�c{�Ev��H=
#���)��H&_�)�w>����5D|��������������JA�I�Q"Ax��K���B�Z�>
)�PAhKQ
Be�nh�\^6E�S�MHA��
m����>(�dt�+�� {FA����]P[��C���
���@t�+a1AX���b�0����H�W��� Z���� ,$���%   �a��l�s[�p������C[s�� R��N�I6#uz3�K��)�	B	��+��-A������%�����Ay�#O�;�>�k�z����{�_�� ,&� �a���I�H�H�H`�	
�b�p��Sua1A�����^�{���Q$��cKV}��f��	D�|���
�b���]��cl
B�g�G�]&Q:Ah)�����c�)8���6�V���|�����J:)'�� �y�f��IA8���V��IAX<~ia��WNZ
���7�N
����-��_�7Q����q�1�0�z��!�9�
B+A�� ��N
B����;af��#?�!�=��n��������a}��D!���I�'���O������������{�����q���<�)R7�������J:)��X���K+�����y�A�-��RAhZ�
B*�:�9.   h��)� ��m� �g��H�H�H��@�+�>jK~�
�Fp��c��N�.����P�P����5��3��Za����5K��+~b���s
B���7�%���25����7�am��\�����j��;� � ���25'�l
�������d_�������������6�ar$�YO
��#�~D����9��E*� ��G�(Y����p|
���������.S�pz��xD��"��s
����q�q�q�q��8���w�Ui���!��Xy|���ej:�o*C;�">_n���
�����TRAh`
�jf
��	�	�	��GPA��T���F$@$@$@$0+PA����A����[QA��^7oNPAh�aC�C��T��3�y�&~�O�'���or�p�p�L�<�����K�8D�4��,�o����3^�G�� t7�� t����*������
B*� ��C$  �'*]��
BW���H�H�H�f*���
Bw���*� l�!�$2�7���Bd�:�@ �i1|��K���B�Z�>
)�����-�(���}7�k./DjY���C���B;%1qj+���C&�������0������p�)Z�i]�����]!�������C��s����@t!�[�sz�K_��������9�������`>�9�-��X���������Q�!	�	�	�	x�@.A���5j���#��
������i(��6���@�������oF���P/�BQ��(��W!�)�@3���q�k��k+����ZD��z��;�������j�	�Z��qH�H�H�H��	�	�}�]rwCfb
�����";�F$��5/���� �>_�L�/�X~�%�A�j}��LV`;���_����'R=��[�p�9}LV$�$@$@$@$�T�2$��0�	�	�	�	�
T�33��8���~�n~l�%� ��%� ����	�	�	�	TM�
BW�� t���H�H�H�H`V������ t����� ��u'� �����|��
�j8�-	�	�	�	�ZT�2=��0�	�	�	�	�
T�33��8���~����TV��O�a5���H�H�H�f-*]��	BW���H�H�H�f*���	Bw���*� F�y�)�$�DvK
�
�A�\�����G�����=)�B��x��[�2P���v�e�,5
�6KAh���8qj���A&NA��!�g��_�o��A�p�)�	���OB������6�.�\������]@d^E�����|?�5��_�H]`� � ��B(mgW��e��[B��w"��`x���fj�����_�#9V������;5�>10p��h���8��/[�y����!��
�����|:������o"���P?~��1�N���; �]?DSx=�:hZW8�#�O�^pjs'�.z w|y�
��e@$�g�<��{��������}s��������=� ��z3�e7��!Z�����'>�'�~����<��I���m=�� �Y����{�9��K�iH��z�G>�@�f4w?C��`p/��oD���P�ENi|���H��SD�|�����(�#���QU�q����7�~N�RA8����	����qm6*k��G!   p @�+����&6"   �YA�
Bwf���'?��)�aZox�w�D&��MA�u�� <J�2���P��G�S������ �
������Z�� 4;XT^���)M���*�6����� ����OQA����)��o(5'*K�/*#�_��MA�!(�.����xzo}�� �Cv.��`|���Y�*�w���^o��zn�i���x�x3��MAAx��l
�~hkn�)|�� lF���2
������
B@�oSf�<�Z�����>��� ����������\�0�0�0�0��g��x�>��V�����K+�"�cS�\t����f��R��c*��v�]��~������/QAh{����T�� t�� d
�j��
�jh�-	�	�	�	�@�� t��
BW���H�H�H�f*���
Bw���j\����5d��SZ5�A��F�j�=j(��K�����xr]��b���AX�G��U�V
����
B��J@���7�J�X��x��~�u�O�������7���9����A8��x�Dlz�K_����r�����CAh� �Eh�� �j���b
���aCAh� t�%{��S��r��|����|���������pZ��">��A�f"(� �MqCAh� T����a3�j(K���
��s�/� �B����m
��_o�	B������7���2�����������;����D���Z��b���A�X���
��1CA�T�pr�+� |����"�+���;?����w*m�#���
B���Ah|b���n���'F��	�	�	�	x����C�+LlD$@$@$@�����L�;N~ne�Ah}+��?e�� �"`�����G��)��P�Y�����A��5���4���p}��������g�Sh�>^QA�}y���� 4|�6�(� l�)��=~����S�A�)�Z�A�������#6���9���V����M�!Grd\f\f�%q�����\���)�)U�w��� lZW8�/��A(m
���)� l��� �x3b�}k������-�m
B��J���Y?jt�K��p���
��@����5�����D��"����A����QN.N�U�n7k�v�� �������_*������
B*
��RA8�;�H$@$@$�7�� ��� {�Os����Cd���|��(KP��
��p�YH�H�H�H�4���� ���t���S2�S����� ����	B&� ��C$  �'�H���9d/|*�H�s�� u��0A�Oo��H�H�H�H�!0A��LL����V�� ��86            ���I�F���PZ��@1?W��NB����y��.~����pi�6�id
�������Z�ybi��� ;�>�s.�8���w�]sT!��0x��������40����AX�-�E�AmZq���r����dC��"������� ��D�| �r�)�_�	�Q���=#P>�)�g-�T+3V�������K!�D��6x    �i% �xr��@s������,��.WH	���?������	�%1���D��� �A�ROCDW���]�/�r�Y�}���d�� d����k+�G!Bss�{��&�SG�^2����h���G�n�$����U7j�>1���t�7�V��$@$@$@$@3E@�
5����@����1h�a#��nM�����@;q
D�����vD��� >t���
�Y���W��r�-�����};�����we�z�6�������5*��D6Dq�T��^��2�;�#�#�%����&dW��p9BOl��x�am��,�+���l�][���4h[_D`���d����"��cPN� �����
*�Y���X�U���S@ �n�=P��q`�������'3�J`��r�={�E!�du��&S���A��"�R���3�� �-B!�}������Ys��.h���	�XEW��������:�L�i�7��yJ;x!>�������������zn��k��?���K@�B������e�D���R�6^P����"�GC���5���C�#�BDCHj�z���Ud�[����g���#;�D�g=��~�s����wAi�����a����.��)��7�������E�K�6_�S|U��B��������Z/�<Om�+�����z���!s�{��ad�j2^tk<�Jn|C�-������>�{�����M�!������H�4(��r�0�����/���2�m����B
�����{�JA(��:/�����PK����m	���/������)��V�����lCA�m}	��*�3���� ��f('�!��U����� �=����q}@��;
��=����*��B	��]�g�hQEa2�
��8bW9���B����$� n�;�^<���:��N[����P�����c*3�MF"�	�	�	�	���	���1����;�CY��)%������ �� �LNA��>���l	�2
�C��H���PZW�V�0x�h����!5������~��WA��
��<*����	�	�	�	��@�V`����P=v��![���'���
�b�����0��
�7m���fH���T!W�3��
�b��:��g#p�=TN�3��MAhu��8{���(��� 4?W�� �>1���l_����V���|f��R�,*��� �b�� ,�/��� ��?�����6�)*��� t>�� ����0����JZ
B������� ��u���$��]|#������:�A��M`�a���&O���|3�?�GSAh%-�� ��N	BKA�� ��/��V�p��08�=�g�����>����nK���B��R���~�g��_����=�t��?����
�~*(�O�:+g|2�D��;���������
B+Ah)���������g*��� l����xO����o�I�iZ*� ��$��H�H�H��L��
��	��X�� d
���YH�H�H�H�#|� ���-A��q5?v�5������f.k��� d
��0����)}{�a�:�L;3�������^�AXZA��\Wx���Tj����'�g�5�8888�5��ai!kN����,T�1�;~LTRAh��Y�p6�v��H�H�H��� �lT*+3b   �%*'mV� �4:��H�l�SAH!$�;�@�l��T��*E	�����|�o�2��8@ae;+}[!�WC��������Q�������*'��Z�M� IDAT��a��#���9���8f
���+��Z5O1�PA��\o��PAH!��a�s�$@$@$@>%@ae�RAX�[�	�	�	��/	PA8i�RA8it��1� l���F!��:/��J@	�:�%�������<b�2���
m�B`��
0�:u�f	�v�����A2��� u�H�4��x=���t�Z��d��B	��]�g�h���e��Lm��+���*�N�<
1o	
#�����>d`g����wwA����l	�Z�qQAx)df�i^�8�@$@$@$@$@�G�� ��>8��MPV�@�n��j�#w��/@{�G}��,�Al{���
"1�}��W!���B�w�9o�c���Ev4���z �B��o����wBi]�[���H|�E���y�V��	�zP�9H�H�H�H��J&��
F����p�.�x}��D���C���adw������w}2q
�
��s*�L3A�OH_�g@���p�)C���l��"��f��(*=j��v�f	�c�|�	B�q�� �;�^<���:���|�p��������L�t~�`$@$@$@$0�� ��	�����H�H�H�|I�
�I��
�I����T���eR�!��)�7AH�l��#	�	�	�	�'@ae�`��2#�    _��p�f��p��|�#��1e=�T�l��
��0]9F   �� �ch���O�>eU��������g�}b���O������h������������>1�����7�	�	�	�	L3*'
�
�I����T����HRA8�	B*g�t�I�H�H�H`b��O����-A���QA�yC$@$@$@����6<��F���	B@���T��^��2�;�5�_-���6!��\���zb����
k�=�z��i�����e�,5
��X��B;��G����.��V��6 �@fu��bQW���R ��
�:zX� �&sm�=3�J`��r�={�E!��+;�dj;�]#�]Y�\j���g OA�[
�BnsJ5c�]�������� 3q��������N=�)9��o����)���8�8>�q���������c%?)u���9��^���x����������[
����6x��K(%�����v������������_(}[!�WC�����w��2�q��2??��y�u����z���!s�{��ad�j��!�A���[wZ>w��|�{v�~�7m���c��sw�@�<wJ�S�F`�?"}���9���2�m����B�\������T�&�KaES��t��\���������	�	�	�	8`
���AaeFlA$@$@$@�$@���J����fG����������Q��{l
B��������<:����9�V����+��MAh��� ��	��+H_o(U��fS���D�MA��%-am�ST������ t>�� �#v����C�)�n3j����:��Nh���
B�
����olL<�z�6�(D�]�X��_��{y���p~��z�{����������{��LZ5-�U��R�^(� �>���l
B�����VZ5�*����YLZ
B�s�s�s�����oooog8���W�PFA�8�����
�7m�)U����}\)>��AX����p#p�=6a��5����A�
B�+���2eK:��L���z#q��$N���#A����QA��Y���	�	�	��gPAX�TVf�$@$@$@$�KTN��TN�ov� ���j��C���V
�+�PZ5�Gv�y���
���C��!����V
���-� ��"� tJZ5;�7�B
BK�6C���[�Ah}bt��1���F
�U����l�dj[^Ah� ,m�b
�%@�P:� \�W�O����CAh� ��|���R�S��
2�-7���A�����S��5�����S����:� "1l(�j��w��������YD�B�_��\�b
�f��H|�E#8�=�g�����>��������5�����U�p���%�`�����\O���&�'������{���W�9�j d(�j6�zA��[w��a���������W�A�g@���p�<T��+���;���q�;}|� ,z��E�i[� �b�J��F$@$@$@$�uTV����	�	�	�	����6+��F���V��)c<� �
P���p�am����?�2��['����_TZ�6'�Q�P��:)��wT�����P�Z�R.��r���
B{m���7k� v����<�����_T�����(����B������wj������Z���o�<���X����	�������,da����Q����
���������5�)�|������3����SN��P��ZFA8�����6�W\�q����E��o?\�'>�}M�o��}�P���I)��>Ln|C�-������>�U�������f
���+������ ��������7��2����C!��a�s�$@$@$@�#�u�z�1td���'�>�3��L>Ah��W��
��p�YH�H�H�H�s|� Ln|C�m	�s�d*���;3A�3V�& &� �
��c$  ��'���1?�:�L"��u���� ��r@$@$@$@$�QLN�0LN�ovl��o�s $@$@$@$@$@$@$@$@$@$@$@$@$0s&A(�������9� !��u�|�wB��h����@,7j�w�����Ud:
��.�s�@���K=��1�,+�IB;���k�i�P��@YzP!3Y`db������A("���c�QL���c�������� ��������f�v����=xh@`�ee��r�;�0��{h��.$���,_�3�!�1"~�����`1D{K�X��H�BoY:����#�n(�E��,D��7    ���{1��FDo�M(��0�������&���"�z\����A]�1�gG��B0�2���?!�bN7��>���8hq�dC����P���a�}�0�h�A�������5OB=��5����#�q9��� {�U���9�+{*�<��K�d-�����~gJ'��$@$@$@$@u#�M�h���Y$OCd���,/�%���-�S{!{�C�v�!����������'!�� ���#����e����~��MM�N=��oA!z���v����?
���W�=�^8�2����J[���wp�z���~Vcm�}�c�!�R@8�`��?%��$G��/t�g6�~
� ���9m�r	����~=��/C�D�Y��@oY��abBU�'��1�
��� LyM+7�� ���C����}���Dp����8qXZ�f^����4y&A�?����	���e���P��>a�az���o#|�o��������~��?�[�<�w-�@��<���k���F�}_����� |R
�Lf�%z�� �' �uL��H�H�H�H��D�H�~�#X��_�#O�	"}�Po���k�&s]g$���r�gr	������*� �l�7`��'o"����	B����j���Q�P'�G��y�W�@�YYH&w�M�����=l��k3���!~�>D��C�o���������)��1RLvK�B0���dr?   ����."�J'G�A���/�����a�����e������QU�������7 W\������������oC��f�	�3_�
���h���Xtn�3F�0�� {��$��]���)� O?q���	Bm��!� bs���9x#A8xz��j'VA�M+`��A]Dg��j��!A(1���nEp�RDg�v�_1AX�'p   h^O���@�w~	h�y����q���L6����$@$@$@$�9L2A�9��i�D�;�����O��"����ah�7�z�~��]���@��
�s�MAx���3U�z���E+�3��RP��,mo�kHyM+�-�Cr��MP.�8E`QA�@f4������,'���=
E�@(�g~f����' :�C(���G�
(P��@=���.��ye������ t�b|b����g����������>��M�s�������_��cu����b�:��9;��6�(�<�S�3o/����?��>�<�<�<�<�{��O��s�a�w��z�#O�)"}1�7�jSJ���l
�02G_F�����`NA�����=�0��E�����(� �����X�hm�}b�P�����#/n�S.��o!��yD��A���
��/v\w
�+%�A �]����}kQA�|��x�x��x��q]�8�8�8�8��8���@�,(�KE��d?DAA������zj2o����P��������3����� �o���/��MAx#�'�oA������utQA�{,2JH(���)�
�Q{wC��R	��'N�� ,�g��6rEa;B]���I�@���e(9_�0_� l��/���
B=}��v�H�k���;�    ���pl�p������������3Tz���5   �:�Y� L����O�z�a�T6h��
���0���
B��R�����,��F?������9PA���� t�����fs<��������`h�8@!������T6hv�
��
G�Wl�M@�3$��H�H�H�H��L�
Bw�)&�O�s#   hdTRA����H}�+��e�%J� �����m5��8�z�"��F
B�n�'y��!C�7���������}���Bp�
@@��d!N����&nF�BEi�P�: 3�M_�p|
B�������r�;��e�M�A�w0?1:��b�v7�� d�p���=    ����U�p��F��D�����)������"�?��_@��^�gG�9���OA�l��������'F�� ��y�B
���a$w�m�D]� ��q���s�"���
������P� �H�L$@$@$@ P>A����b/�����zj/2o?�p�(�����f]�}b�c��a${!_C�����7C65�:����}�r�\c� 4j�����d�zz��p�b
�P�c��SOo���n�V���My��� � bF
���
��P�C�:�U��h�	PA8�����'y�'*$�HyM+�'��r�'�e��N���a������2j�R���B����M��m����������k�}��������V��5�eR��$@$@$@$�AT�3
��8�	�	�	�	4*� l?�C�)�oQ[C�����?F���l
�e�������I�v�1��P����O��<k(��]�7��D��6��|��Rj'���c��eOCQ"6a��?���j�����
�'a���F����p��6QA�M��e���!H5�e��8� ��|��}k9�x���+�$O���_o�]/��|���)?���5-��P���)7A{�i���eD�?YFAXz�(o�U�m�4U� �x>a��XPN~>�
�n��~�������N���N��&}��(�G9s���������=fS
`h�MA���dS~���W�����p��7���@6E*��V���/`S��RT�K�sV��;+G�������~�^SA��i^*�7��}b�
����=H�H�H�H�����
%�
Bw���H�H�H���T6������A��V�>A���� d
��F�6	�	�	�	x�k�3k���V$@$@$@$�X��5�O���|���rZ�!�����'F7AoC[��'7�>1zD�Y�O�������*�b����h{�3P��j��)�^�F
������b�
�u�Ah|b����D������m���	��J�f�)~b��A��>�����wB��j:��Y�p;z�z�V���'F�@����i~b4�����?��g�%z�"�xb�%���|����������c�����M��s~�:�?1�k(���1���"����O�?1j������'F?��2G7�?1Df��A���;z�a�e���&~b����u��@k3��[H�.������/!��o@�������� Y5�������~����������z����J�k���=��|�|�|������n"����y~ ������?r�]
�-�S{������k�1�?���vd�MP?�e A2�r����a�W����he����m��-G�5�����r���G����`{a=n~bt
�*�O��x�q�[��lm��o�F�A��k��1�X;B]���N�?1z.�����o*m��H����zkQA8��� �	�	�	�	��w	PA��6T���V$@$@$@$�� �����}d
��b�	BKAhW���� |M+7�����y(:)OK���h*� �-����/A��3o]��H�H�H��I�5���5�qb+   h�A�����~�#�
j����A8}	B� l�i�n�	�	�	���	PA��DT���V$@$@$@$�� �����}�� ��u���BE`���@���j���\f�AxI����O�������8� BV
�2�)Y�pb{�k����j~�"P���u72�A��jN<��=m�A8�qN��}��	[
����X�p	�C����j:�t
���u9h�A�
�<�M�i���2T�j�N�����f�bt����o����#��e<�?���O���#9��xZ�A�+(���1�7�j�A(!�o�� #s��259�/��A(!_�[E
��#�7�j���b
��)��M�A��1���s�9�������f
����IE?�~[
���j6CO��� |���7��OW5��_}��a�x0���r�][
B����Y�p5�.��t
���K� �@r�V������
�M�RAX��� d
����{�	�	�	������*�qb+   hTRA�~��>RAh{�[�o>QAX�TRA�v~�]�����'�K��_���)�J��5�SA���E�;N~�'��u������T&��&����~��@8�db����oD���m*9��>�� l�4/���
B*���A$@$@$@3K�
Bw�� t���H�H�H�H�PAHa#����a�Z�	��
��av�(����8����
�s1D{K�:�����t��5�oxs#   �2�Z&����7��C��V�aO�A��\�b
���a$w�-�w���g�J}�b�:�h���E��F���Y5%30A8%|��H�H�H�<E`�'_{
���\q�{���L��|
�����7C65������}�r�\�{,:7��r�(�|���l/�X�0T��r���!n����]8v�F����9��1�X;B]1$����u��>���`�p�O�� ����YC���"k.B0~�	����{�	�	�	�@C�z���������������SU�&i4A9�r $0H`a�K�Adc������1�����]�g���]��w��{	6��DZ��MQH(��4Jir��NUu�OuOMwOw�t��H=���(��S����_��_=����g�A��&�����8��&G�(ccI�H�H�H �0A�a~����5$�=L����������EU�"������{�
(�r��)5[`O��Z�������[0x~���.H3:{�����9����t��O<�4
>����z� �c�&(G�� IDAT����7A��	���M��u��I)�`�-P/�R����_����ONfw|�E��j�$����YZ�������!���3�F�=l��3e;��c4��:�}�!t��_�={�_y��a�xm�a��%+�v:�?[����~�uPC�___���f0�w��`|=���	��0�[���_��T��k�V-|�#A�������^s�����[z-�KCx�cXA?�(}���}����~���5=�8$��g��p��7� �	�:�������K"���O��8������t[�0>�����KZ;����@DO-��E���[����Z3�)������J�~�g�;2�f� ���u~<��<���hz"�����_���?�1bb`�#FA�f��+             �	����{`��V�FM��^���Ps��FC�(���1�����X�����z���dT��9�)U��E��OA+�1�RJ��!(�O!�s �|��&�:1��B��Q}��%������!4�
��3��d���#��_:��@������()�P��Uj��p{!
&C�j�B�����Dz�~(E�PJ.L�y�������
��oD���M@���������bTd�RAUOXN�0]���n{�ix�MG��E��P��;�3@��H�H�H�H`H��%�L�U�JE��<
5P��B����J�{��l'�)k�i�����a��0��w��`|���m2_yzi��������.Z�8
(��(������W���!�����m���v�U0�;`4?��1�������|�DD�Td�[�lk�~(���#G��
�8���	�	�	�	��&�~��D;��
���9ur���wBC��>���w������k�1�~P\�J�����
�z�,�*D���� t�0��	(��
!W�1�����m�MO����s"jKe�|���^y��nI�$��A�����!k���������.�R��,�l����^���A�0�����ovv]�P���#��N����ah�;pUz�L�(� 4���5����%3�M�Q�2�i������'-A(CAh�f��a����'\�l����!��W���L�6h�9}uZ	����k����-���+V��|���R&��yH�7	����'�4Ah4��RXM�������W
����}'�8_�Ch��������=����#F]���B]�K��h��tCsw��2AX���A��D��n�o+���K�	�	�	�	�V���z�e��%k��U�Q|����0���N�Iv~�\o�$��oC���H�0��c�ni����U���b��>�p�D��j(c���G_�����Y�a�
k�|��_~
����>QR����^�
FU���k���l�2iA������m�|`�V`�\&���H�H�H�H 	��J'�L�����_~J������c�W��z�0p��yxq��$�V@���������;�n?L�	�7o�9��:,�A�-�2?�.O����E�?�����\�Wi_����?G�s�����qt�IK�s+z�[����+��Qx���c�P������s��		B����y5���$L��� d�0�R�	���k�	��G!���������k~�a�0�    �|%���os���i��Y���U�����L���b�H�H�H�H �	0A�����6>���'L����b�0���Il�hm=,cf��ia��)�n�v�W����!����KP�,Cx��P�L�6qR��PB�P]����Lsx�4j�C�������_GLAx���'�/��G�@p�aj|M;��:d��|��WA6��v@�X�)�nB�)Spno<�Wx��DM=S����80y<����=1�$��!�U��N[p�Gp������������Zw�@��~��d��}�A��u���� ��0=�����t�K�P������Q�`1��(���r�5����O<"Wr��f\c``8���0��j���
�Jed�V��7P\5
�g]Y0fS7�i��������k������6o�~�
0]�_~/����_����'�|1~��U&B��2v�~to��2��5�}
����I?_Ee9�'V�����?�tY����y����������^��f{3�i�-[�y������a�d�d�<��&��������8p������	����2v=JK;D]=���-����&(� K���E��pd�z�� �C~��y;���
t`z�����D*vu!X���[e�
���V���������� ������0��8��$+��I\���
Bk���cZ��nZ������fg'����f[k���/�{;� �A6�
Bn1zR�m�Y�!�9����H�H�H���@.�/�KNr��l�uN������-��z�1��� ��y;��0   85�ta�'.A��!o1�t��HF=������r�8��$;s�_��z������ ��N���m��'� �=O�da���(mB[A�\^J3NA�>� �=m�
�F����b4��#[��
���}Ca��e���V\��@���'{�
B���
B���y�D=m�o!S&m��Ah+��6 V���V��'g�f�?��������c}��L��g���6�{��|�l=�x�x�m��)mB[Ah{�
B���V�����1�N&������ ���a�
B;Ah+S��b
B��0�x� ����� �z����&�x�����/�/������?��AoJK[���7�)g��I� ���a���IAh{�
B���V:__j�S�P"� �=��{JB[A;TA��Ah+��?����T����W*� ��p�����	�	�	�	�#@�3�h������;AX�E$@$@$@$0�	PAH�H�#���
B���^�������� �RZ
�L=��zb	B��px�g����A�'\�,~�����^��A��}1�sz�^��p�����j�R����� �w�>BK�h}N���y�9kOTA8���Ah���|����{���Cd�AXYd)mB7L�� L�� \��������|��S=�x�S;�����o�2�1�����Q��(3�.��,��Ax<����� t#��� �=Yem1��?b[���_����a�V����<��,���*��L�������� �=�����O,�]�!/���~���=���Y�����a����������p"d�� t� ������ATAh{Z[�^�A������a'������ L}�-F��� e�� �=�Nx�m��� �={��� <�'���oC)����7[�{���W"b���$�k�����0n���TRAH�`g�#   HG�
Bg:Tr��	�	�	��iJ�
B*O���o��� ��rz#���� ������m��0���M��DB�D����;IA��}1��}^SQ��
�5��FA�[���p��������Sjq^Z��`��}������ ���IA=�����5.��p1��+��0��O��O5�bo����{�-�p��_������������������	i��_~4��0q|$+���OC;!\�a�
�������4
���7� \l��sRr��������y>���;���\0>��)����i��p�sRZ
�h���R���;QA��K~��� �B���FA�x}�
�R(S��QZ�K�IA��'M� �<�#��IAh��y����'�9H�RAH!�9�H��H�H�H�H ��TrZ�	�	�	��G�
B*��Aj.�����4"���� ��5���6�%��M�Q�
�i���K(c���7��S� �<��e���|���A*����4a��i�! )������!.�=�U��
x���� ����}����g4��� <�* T��0u�1�;�6@~q������
�����Av��w�|����!Jm�nh�N@����'{�k    V����m�T,i���o��j��ZY0f��A�:?z���z�n�����A(�}��)O4�yf��[�f���I�H�H�H`�R��q�+VC=v���G�%G�^H�d�AXe��^��a
��]�����b��r+z�[_B�t���[PJ���;{v����6�� ��,0�� ��S� |�	���aph'����[�RAHa&�    H"@���`���H�H�H�NSCJ>� ��JN^��7o�{�����sb*vu"X����M��NH�D��?�pY[���h������I��>A�M��b4]��Uxn������ov2A�o�
����IRA�m�QA�c�\q5
,��w�Tf;�X�H�H�H�N6*� <�c��#   �SN`H	B*����|D�m� �A���!��v���0y��f{�cy�i��2PA�3���H�H�H�H ��LrB�	�	�	��G`H	B*S'� <��QLa�*rj�T�/�����
�l���	�	�	���&@!��z��$@$@$@$p�	)AHa��]q[����b��'}|��	{����J��]��U��2�bH)a<��5���6�%��M�Q�2�i���K(c��k����9
@�6��q�j+���d��e������C?G�C����8����k�U�����+�@h
����=Spno<�Wx������������ �W9�::m����5n?��;���_���gh�y_�
d�C�]o���F;�5C�N�,���@sw��5.���j'C������q������w|g���y�������������)�&�ZoD�l�RY��>�
WMC�Y�A����
u�(�����7�Zx3<�*�����vL���E������������������sW�=s��1(�������N�L@�h���uis��������+PZy��C�V8�;��z��(���������f{3�i�-[�y�������<�<?S�	����;�;����>J'>��.�oJK;D]=���b��o�R1	�d:��^���W��~z�� �C~�� �*���m���}����`�bn��7��U:[Q����� Z[�<c���[�N�r+��u�F�������o�6@������yJ���0��C�m��GF������0�Z*Ww2��V��|VR��� �����B��H�H�H��#@�3/��f{�i��-[�ys�����4	�	�	�	�@�����"�����e%�0�'����� �B�}q/���:s���0�j*/<TRA�7�N�7Jx}|�i��i��;"pqqq��m���9nPA����|by��yf�```!q�
B*s���|x�SA���0�T��������B��������p���RvG�r��c��<���� ���;&��hd�f���n���Iu��]��J�s0�Y	�	�	�	��p����.��9�X7	�	�	�	�BTRAx
���t�~	B;�h#��K�'���"H�,<��zb	B��px�g����A�'\��� �|2��J�a��1�szD=��k��q��!45���N�/}����v<%o1:��D=G#� �=�a��c	B����?�{��������7:����R;Ah{�	���''m����3�w�M?��<9�8�Oy?����Yd� ��:mb��M��jq�n�_~�����&�����R�S���"\%�<�� ,B���"�����j�_����/`������Mu��z6C�� ��bLAho1j{�9����x?�|��C�>��C�><-�}	B��0�b�s���2�A8�dF���_�5�A�������{n�{�i��=�_���6>�?a�����B�FA�:?�������qt�IK�<{��� <�'���oC)�<�o��C��!���+�1B�C��;�7�q�w����
��� D��0[~
������%� ��q$@$@$@$��r� �}���������9A��5�a���0_}5�"�[����!E�0�|�DC\�0�^��0;^,M$@$@$@#���� �K���;��A������
��f�c�Mq	��{?�����K���B���P4�N~Ji�nz�	��u5�f�	�3"O��RA�E�T�!B�a6�;>Q�m�O��9�����7�N�7��:���������&�x�x0��AN��;�>�zN�]�~��_����@��~�������B!w��t�qS�����1�����T�TAh�.���<�Rt�\A!�����p|D:m3��0             ���a|���2��	�&`��er9�����	P]�P��f�4(�6�* ���!�����s"�:*w2V�X
�a���r��T�S��5~<G��M�1
��PR%�>L��-�(V?�6!&.�4��8D��Huf����]���Ta6��?,R�!��2c��$5Am�(E'�K��]�����B���,���L��\�-�R4:m���}��mp]p~�f���|�U&����HY�g��=��K�t�"�Z���P��uP�=����T���Lm�2�RT���H�H�H�H`���&��y�-�@L��z���SP>?����C�Z�]�]���w�~�9k�Q<���/]
xY�"� ��SQXxw����;���/�"<������Z(�h��`:�p!�;^����O-����������%p]|��O��z ��2yZ���b�4m�P
J!<��y���    �SO t�3�fxF/����}e�	��z��U}���m[#�@�gB���h�W�\�kC�]����[ z����>KW��X��5_�}���{P��k��:~>���aV��:v\�����v���  ;����L�
����O�x��Z)�C����\�@���0Z'�!(�_� �������"��%R��A	��(��?C�,��� ����^���*��#	B��/������o���n�Dh���>y���0���0�P�~������j�6fyv����$CA3�����*j��
(��x���\�4 k>���
��C��[I	B���t�p-XI?�6gU�N�}����+��sL]"�o�K��Ko����	�&�����P��2�	��!�gD���j(��&C�>����w��;�ZZ��r'�Qe���
��nD������KoA��WJo�����&��M0W)i\�;����h�����l�a��I�H�H�H�N1+A�]x
����4-� <���PZ5�����s>B��M�$C���w���������"	B�������r�;��j���=:���@���g�_D�����M�Q�4Dh��1��3�.W�R]�g����}��������3��a�6C��0{����-��"||/��*&���#H�H�H�H ��[w����o�)����P�.�{����bz_�P�}��+��
�3��qyt�JW��I��>���_�Xup����w��et��Ca�
������C[�f�l����6A�>����0�1)���� l|~
���$����ch3�L���Ao_	�z?\��sL���Q����������9��������&s��L2Ax2�-��)�{?�����7��fD�L�`6�
   �/�M6�D�G9I������# IDAT��-��|K��l���o�}�ML���bkH�H�H�H o	0AX��=h��������kCN�z�����"��m1�y;�O~�Dk�!3��Mr��Ok[��[/�)%�������^�:��%p�'�P��������b4� \g
<��U���)�i6��29>� ����3�8��B���\	�c
����� ����C�{���8�Gp/�
BQ u��]�p[r��vH�Bx����~��DM�S�Goh�ZY�h�iO8�OA8	��PJ+�Y�<�v�vm���0�w�K����8�������AB����V>�O�:��'qpw|��fr9��Jiyn8�����g&������D����xj�s�O����g���.��m�b����������Y�P�r6����h2BG�Ch���.��
���8��0���,�e����Nr����)���>��)0j�A�S�"\����]�j�1x��)��:�;c
����jl�S��V9
�����g�� ��7�_y�������_��������;av��)��kg�#���]��<�_���pyf"��1�Q~����Q�!]��B ����_����7~�{�_B�o
�����L��p�K�N�|��J�4>�/A8fO#b
�g}�6h3�e+��d�?Ao_� ���'�'3\t��������]�F��v���C��� �AR�
B*O����'7�H�+��	T�`�
   �G��fX�����%3�b�4�%';_n[�� \��x��Gh/��0�D$@$@$@yH�
�" �� |`�
B%�;E�0�A8\[��� ��$2�,Bn1�_�.���n�S&��~�� �=� �=�~}����g`!���
�h�������P���4
B@����px���Y��	
��?L� t�?� �=����l��a��D��A8����M���T���o�7�8��8?8?8?������c
������� tR�
�	��
����RAh{VA�z~&{fqKP��N
B����/>�������}���3A�������#s��n�������W��wja�a��A8\
B��|�?T�2I���TRA8���I�H�H�H����0
����-F�a�	�	�	�	��&@!�#x�����*�v�o����E8�A������F��K�<WB9x��Y��A����,����7YA8��b���?��~#�n��������<W���oq�����+#�=��P,��Ah'�� ,�=��_1B57��� ��h��� �����{���C�e�Aho1��A�?� \���������Nm����}�������������pd�2� ��Y
B���vxO���~�R��O@U�-F�Cd�Q��{ �����{��G���it?�X�����K�A����OA���V��� ��x�O{w$ /���>����#��F����uz�Wja�z���p	����WA8.��8��n���a��w���8Bk��+�<�A����x���#��HV�
e�� �=w'<���}m�� �=����A�Fu5\���R`y&�M� ���=����<��C^�SA7|�W*� ��p�����	�	�	�	�#@a:Tr��	�	�	��iG�
B*O�A���� ��i��N������!M�FA�
���!(������{��O��C��
���9{�4
�� %��0Z���0;�����k)�3���)�~�]��GaY��������IA���aS.��w�Q���I�%�y������#�������op1�$r&�(�?��T� ����LA��V49����<�FA�o&)�"�����B��qf
B��G���4
B�x�����K��
D����'�.��S|�LA�p��(�@p��4
����� �����AA�x}�=g�Qn�6sQ�*���� <+7�����AA�����0�[*� ��0�U�	�	�	�	$��0�����3�H�H�H�N;TRAx�
�<��~
��miTA�<�L�Q:XB��s����e9����pZ�������������i@�|1�� ���D�P^=�	�.��!
�����=�A�{
��.�1���M��m�{��A��x�uh���	�����a5��J(E�a���y��;������}T��G�On��*���Vn{v�<���	����N���.t�WaH���R:�C$@$@$@$���	�k���!&M�����:���E+gC�Y��A�p������M�Q3X����M
0[���Y�}7$yVAx
���G�	�	�	�	�
�h����@��JlW�	�G�C�k{����*l�O��>w��Axw�\�@
�*}�;Ahy.����}��y(��v�3��rh���,����0�Y�=���e��� �� L�%!���� �JAI�I3� �
�nT��0�gr��C����0�U�	�	�	�	$��0�����3�H�H�H�N;TRAx�
�<� *s�9T��	B*s0�X	�	�	�	��*�% [�!�-@��^h�Tr"�	�	�	��H'@!�#}���SA������LNRA��)�*H�H�H�H��&���#q[������q[�������]~;<�r�{�W���~�W0��,�e����Nvf�bTGp�����,��H�`8~��G�ov��]�j������^�X�l���bta�c�
����   �sTRA��C��j�9�N*��!�9�B��H�H�H�H m���)������H�H�H�N;TRAx�
�<���� ���"@���9�
F�rHS�����|�\���C�_�{�W�C��
��j�gA.������@�9t�@�,g�KN?i��~�n����8_�iB�|1y\]���op%�W���@������	������\���z����tA���.���K��PH����Fh����ai���F3��J�I��n�

P++ -7�	����
�I���RZ��,m��]�a�8�]������-wBU���Q���!���p2����|"�tu�<���	��������=���a����n(������q#��������'��E������%��S�7���NN�6Q��uq
���h2BG�Ch���.��
���8��0��q
�I��#6�>��P"��6F�3�~j �2��������^����)d���/A���{�UN�������Y�.���!��+����#��u�;��H�����0�j�{Oi�:����}P�.�{��y
Z��<3j���1�;�@p�Q~����Q�!]��B ����_����7~�{�_B�o
������.���%0Kf���������W�������'�-F8|���`�4���5���N��m�f.4O��� ��	z�*����~���b\5��@��A�k;�}?@�ui�l���9���FL���q�
�do� ���P&C?^
��J����*��C�;��{���G_K�[�� A8��tu�<�� L����]������ �=PJ�D&?$@$@$@$@�L��iz�
�|�l	�	�	�	��Tj���A��0o
RAH��PA8�H:�����MA�K��8�8�80���i�1�|�:��'G~��z���c�c���+PAH!�����T*��x�TRA����*H�H�H�H��PAH!�	�	�	�	�I� ���L���ZH��J�f���E8��[/�s�4�� M�yn���4��p%���<����B@��<�����9A8||��~c��C��<W���o}��h=
?�6ge�@�����-p/�BQ�<���a�C��O�����
�P++#mO�}r#h_��t�N�>>����A����a�����!�p�?�{�4�-F������A8����Nn�f�����O��}��#5���������!U��z�V�C�%�!�X���)q����x�<%�#���
�(���1����7<	�c 8yD�xx/R>�A�4z�}�c���g��P����o�����fS��fhs�=3_I[����T��7��x�����d�����������#�� �={��<��~(s�n�O �A8.�,��?�^�8�k��E�a��w��	����A�y�o~��k����{�_F��<�Rx���#��=�g���y�
e��8��	��>�6sq�a�k� d������Q]�A�x^��Pv<�r�V<w ��%z|��R���?���#���7�o� ���d(�_�Ot���}��;�l���������0����$@$@$@$0��WJ�;�!dN�nN� �e�� �.m*������G��O8�,Z��>
�u���;A�\<1A�e���0K`,N$@$@$@�O�
�"���C{�G0/����� �������?�������� l@���a�:�a��(U��V(%I�*�a���������3�������Ex����P��s$��pp���Ah���t���V�+�� �6W���� 4a� ��P"���4
���K���p�xD/*}�zC���4
�,��� <��J��^���sV�+�OA:~o�����yJ.�����"z��0�5Amn����$���A��C.���@)�� ����=P�{�N|C�<�#��Q����L����x������P�@;N�z7.A��'���t��<���C����j�\z/��	������;p]|����l�O� ��T�����������
��?O�z����x?:������-���yd_�����Q��|�* =���u=p	�K�A~����
[�(S_���"�:���K6���5q	����
�d�?�QF��<A���q��F���t����H�H�H�H�H�H�H�H�H�H�H�H�H�d9	B#�nP^
�;	0c�g0]PZ� ��	et��?�3��=�����)0Ba�f,�x��/ dt����5��0t(��9�){��`7��IY�W�4��(�I��y8������>�����!�.B�~;�cJ��0�|������a@m8��(�;J�0{v"|��l���@�q�%�!T
��!j��gA�!t?���X{��� e3 �����n��R^x|���B�`���.��l�Z61�{��b�B�=���TEe��SHO%P6����cf@�F��X9S4CT������[���H��� �� ��r��^C��PGA����P���eIu�=���@_e��j��	�	�	�	��I%�o�Q����@OT���Qh���^������������l����+�����yl+d�r(���k�+�QP<	��;�^B�����u�(��x����=R�<Q�
�.��;��k0������P'�I�6��4���k� M?TO������"q+���_<	�	�	�	����mP*�B���W��f��r�����'��&(�ep/^3�	��7!�9+AU];������B�E����>)��	���}����S��t�(�[���c���7��|�ckuk�z���BN��bd�p�{��oK<��]	h�[�*=O���}����� ���1k��}��C��5j9`vu��P�N����@z�B������J��?9c
��WD��G7@��&��U�*�A�~r��/��G�<�;������p���b������x�� ��P�������0���H����$RO��])����Y��*������p����y�Uv7�����	�]�{����@�'@�#�7����9U0wl�r��}	B�xb��H�=-�c���n/L�B-0A���m����F'�����j�=uQ�����S*r� �G��(�I�[�����'�m�����d]�� �n^�;`�j�5�E��/����[�1��5xo���H��uA��������*���������$��
9��b�    ��&�o@x����|�tZ$A�X�
�8������_G�_���H�P]����E��^�,Ye�tt[/�u���g��H{�����	���!F��k�}��z���� Tz~�?`��������}������FS3��d�
V����U8~�]���	��I�   ����0�&�cW�N������a�������� �5�v%���(��\�-Fs�Y�� �lG���g��(�|9���B������o�B��1j��1/���P0���K�����{@T����E?�&k�p�_��{O'+����1?e��������mmB��;S&;�E�1�N�������PmF�9��1d��L�Hf�0`� d�p8�8����~.��?D��������j������5�\&s0�Y	�	�	�	�%�a�����,������������G��G2�U�����w�E�	��0�	�	�	�	�1� ,B�y���<.C���b	��&�m��n����a�f� �?��|dy�*:)	B��s4�y?oEk�A3�M!��O#����!gL�{���)���8�Jh����S \���k�y�,�<
�7C/<��n��q �:�R�YC?���i��h�2nv��D}uPUKr�X�� ��2j����������BA�%�7�����p`�Pw�����
�/�;g�[�\uCD�'
��b��@`]��y�����bm}Y�A������w��S����uF���L1N����:�{�H#�)���S?e��2���p2���a.��W��~�m{��0�uF���
�_�� ��������T�G���m�b(p�uC��������2��C'nO� Ls�T|�wV�Xon�9�#�c��<\�����������~	B���`-J�_���)�����8�����%��L�U6}�����{mm��:>����c�Z�g�����B-�B��?G���*D{��{��-?�����zM-p/�v"�.^��F�f�
����
��� ���+���rY��l�)�g7�����|��i��[`*�P�^�V�����SZk>���p!���HV��y%�&@�A�BH#������tJ�����E���!��M��rG\��RE��k!�Z;�?������� �	Ys8NA�$����1���!$+�-Q�����h����0��{��E������Pm�e��Y��������6\Y�^`���	�9���*a4A�-F��4�b�-q	�_�~��)�?�6y�9����H�H�H���@R��@��/�Hf��hB��Y�7���_�� ���RA�����"   �SF�
�"H#� �fLA�� <�-#a���	���b4� ������A�-FO��Jy�4
B��S&�$�� ��A�_Ah{:)m��__ja"�da&�,��*�<ma���Wj���FA �A�� L��Daj>1��A�������������2�FA�\f
B 1A����� ��?;a�D���J���%@�������`|`|�����?d� �� �VA��V���U��?���a��7QAh{:)y����G�3�/������������)m��D�7�� �������N:)c��ZA�?A�_Ah{�����03��
���g�J�� ����������AH�f%%   �����i�`�Z�*�����}�t>/�H�H�H��<TRA?�E����gG����=�^�S��2!�7�q�+�R����U��U��Z�(��K ^���a�E+"����/ �&����OzB IDAT�07��� ��/{� ;���s������:����q���#-��Ax/�w?�����+��Qa3
n��:�]�{���E@�'@�#�7���6������_���)Ah{�@��j��H�~}�vhs������G��!�NKA��_8�Pm5�S%}�i�:�"���T"�����p�	B����;u�#��mm?�%�z`�+cB�-F�S�t�@�+O����5�����7\� ��l������������#�c������C����iH� <��gd(������5��5P&NG����<F��b4:~�[���^P
1�� �/r��{�p\'n1���IN��N	���O�+��g�O�>���/������0�8�8����M�� �}�����
���k=C�=g��x��'�O�J�Q2���~F3�x!�� �'�k �.Mq��������|�}��E?������S	���;!fX
B���~��������ok��	Ydy&~�=��8�E�N	���g�H?� L����+*� ��0���r$@$@$@$@Y����Y�$&   �A�
B*�*��;m�)�g\���)�C�~$��P�<�FA8���%-U[|F����	���q=:���+c�S+c��V������^B[AhB������c����WNp�c� tR
D=���x:)�N)O� �?�}���0z����_�Y+�!�ja�m������VA���')y����N
�!�������G�G����y�x���0��p#d��4
�����)���p/�<��1B{���
������,O�����i����!���������?��?b�B��Nm��1B'���ua������)
!�����0��e� ��� ���Pa�xga��h3�]��D���9��[�QA��,�TRA����*H�H�H�H��	PA�8*� ��!   ����T��j*�w��A8�'����
�
��N'����av;yf����&A��a=�Z
T:y��W�x��[�x����!��2A�A��<��0E�$x&~��A8���A���A�8y��?� �5}�VvA���)�O� ,"N$@$@$@$0�<� ��E��������������� �xa���0z5�
�=P��!���#��]������� L>$��0�*�
1A�/�&   ��@ ���$��"�����<��N�>.9���� �^~�
�h���0��[����E�I�3� �{� ��PJPA8z�������<�6�Jy�-F��~�����
Z[���;'u�����tb�Zx�T����U�C��1	�y����+'�-F�=uQR�� |�d6� ��Db$@$@$@$@�� tLr��	�	�	���G�
B*�G5��;�� ��S� ��0e�QAHa�4�    ��!@!�y3�   ~T�	��x��*����3PA���;5	B*S'� 4�6xf�J�:qT�=�������s0�Y	�	�	�	������	�a`��H�H�H����T��H*�w~RA���&AHa�a������(=s0�X	�	�	�	��*� ��    �3��T2Ax�&|o��r���f��i��<���p�>0%��>Y�;���VB����9�B��90u��!��A��j�(������]�q�8@�=����i��h�2nv���:!���m������������PK(ox��-(��6�0��z�S}���� �2�s�����U7@�*�a;6B,]h�k3/V�P<0�Z�"�u��;}�hsl���Q�� �.���9�m�����#�P��G�s2�e���A8���0^�+M[�������u����
�_�� ���������G���m�b(p�uC��������2��C'nO� ��y��q���"�������~����'�'�gn�m#�c��PG���(�1�W^��~Y���6�Mo@]����A���<��d5��3���)��3x��v�������+���
��J��������"�����Z^��/���od�����?@[~�c?u�0�Z�^<?��n�a���*\�Bv7�/���?�Oy�<������1�_�_�_�K����-0�f�c/�PG%�]-��]�.���O�g�s�f��{�B��O:�k�J�Q2���_M�����Fm��
W�<���3h�_���Z�qC^����r9�#.t_��`���c�8��@�P}OD�����`^�#����5�Q�������8��� f���u�@������ok��	YT�t��m�Gf��'�����^������Pm����E�m�,]�F>_�I^#�#���M_�
���v��r�����n/L�B-�P�����m�|��u,���F'���M��!=s0�Y	�	�	�	�
*��7���U8~�]��B�����    ��PAHa�������lKSA���2�TRA�2*��`��,��
_�����PA���0��O�>��������_���
��yM���y*�M��(O!�(����%}w��m�`h��,�5���x��EP��������0s�U�	�	�	�@����
���l	�	�	�	?*� ��p���}&s��T%���n�<�?��0�!�j��^t�~�oWBa����nF�
�[��Pw}���^xJu����7���6�������4t`��K� ���-�G��K���M���x]_�Y���!\��1A��y�*H�H�H�H /	�KB����"���
�.^���3���������A�d�(��{��@��~�>����R�?�\��y�)����Z>
�GF�O�d������-���S��M�p/^�Q}����h��x	�	�	�	�=&�j���=�
���(�zu�a�j��P�����a��z^9v�c�����y!�}�������<�J8V��	1c>�%n�/�����<��,*N:��Ah��F?8��[ o�!R�����Pm����!����
2A8(l�1A���$�l�����WB^�����_��w��
>���(���k�&s0�Y	�	�	�	�%����u��������������������L����?���bd   8�0AKz'�E���}	���$��Z���}w	�����uOB�Z��Ux��1AxF����/Ah�Yl�G�6�mr�d�G�L���������j���P���s	��G��.����h ���z��eq^C��X�p�{�'�'{� ;����������m������{��}�~c���Da�����R�� \(Z�����)M����S~?J� 4�����c������Q�C��X��w@�3�x������uF'��:�����{�����#�P��C��c�N���d���%��:A�z���]���u}���B�u
p�=�=��_�����b���	bV���a7d]m��������h�����q��?+���a>��yoR�G���!u|�xNu��|�|�|I�d�`�H�~h�����;� ,����\��~ZQ�P���Sn�,YS�����[q	����YA�jy���7_E������M��)h�������L�����QW� ���� �?��nF_!�7���oP�3P���,~����~������=�#?��z��Xo�� ����`�.�^��@������	��E0��"t�����d��#���0��)
#�����M�.�%��A��((!��v�8��� L��������v><�$M�0z|LA�I�'����8���EEI�=KA�]$)�`6��)^�E=���v�^�W����� L����!            ����IZ�i��0�L�`10�����������b,t�f�\3`�����R���`N,����w�q�g��}�L�F�*�$K.���il�``C�@
n�&�Mn�
	���&��&��Mr�$�MH��`���4��F���6j#M?�����I3�H�@�w���,�w�s���3���;�]O�y����|e��0t'�N*��72f�XL��&�����E��`j��	����p7:�p�QE�#
z��&��h�5�����nTAy��D�M��.��+N!������<hmc��p�5��{�v;��m�{�����D��^�^��R\��5uX[p�8���N`4�A�����	�0����k���P.`xP�����1�s?FA��|2a����D�v�|3>�|ffx�(��������������2
P��>V�f}_G1������u����(9m������� nG��?���=?
>�*�zq#F�n��r�W�����0�^t<
����y#cNG������4��1���1�/��B@! �YV�������<
w~)�[�T��u����]�}�������!��8�b��=/��w*��*=kQ�������|��
�[�Y�F���0�]u�]��W	��O�6�:sd�W���0f-G�[EO��������.?s�y��H�0�g���n�xUV���;P���+e��B@! ������/c�.��C��}W�@�4����J�l�<����zT�[�vo$�#���e��c76�������h��n�=���?��
<�o�o:*Q�{�S�]��w�J���.B�=�_���D_����%��9%U���5��3��!W�B{������/O��(���=�jf~��cc�1���mX������������`��f���$+N�b�kP������)��F����c��c�c�UX��A�A��wxf�lB�����!z�v���@Ia��#�������
���B��x�'%�MT �V�|P�?)lB-[�S�X�.t�
Sk��y#�)z�����W�OO��F��?(�^����m��{%�}������0{�<��)�kV�u)�0��4�7Xs���y�}�hY����`,?���W��~��@��}NZ��������hCy��4�\c
��}��T-�*��nBWa��qU���1)�z��=B ���0\�GT T�7����B��	����ab�F"�N
�^<j_���&��I��?@I��q?�W�����c������g��W��p7��c5�b����{��{�%)������]����J!! ��B@�I%���}-���Oq%�
w�?�	�b�����?�(*����$�����I	����������[o�x����{	�_��z��?�����'6c���w��ho%Vp;F�l��b��Z��u�{?����a���a��r���I��
�T8R�H4)�������1j.�0G~RL! ��S�@|��hO���2��;P�N��
(����������Nl[��?4o1�5���T���@n�g����a|E�`m�
��o��A�3��^�w���lFU���AUg�"O���}>u���/���_K�?)F�}�Y����Pz\F�0��{D]W�����>2R ����_�
��0[�S\�����F��e9�K����f��0gb�+(a�A(���7��:�d
���c�w~��,������'�O
\�����z��O�p"�'�! ��B�( �I ,X��x{�e���/�#��\���I���a��Sw�6�����\D��3E ���B@! �����X�����*�i��W�A�-����D�s��������FNa���������s�T2�������P����u=���?������B���<=]1k��P�����/����p��w��PG���sk�o^�Y{����_�y��t'���x�c�LT^��;���;��V�<i�M�����6��-��~+��0���1D�Ia�,]�J�gP�TAY��Dh7F0��w������%����r��&�B��$��Z��c�m���6�}�kN�=��� 4S�C��F8�4����v\��V����T��ZP.?�3=;4�}/���G��	f�q��!��Y��pNf���z
���R�����B
��x�{����v?�.^3j�7�;�����d���$v>M$�A��KF-g�m4���i�fz��?v>*2m�|�����b�=�������0�?}���o��v1���'�Y9z{L��8���g|���^��/�?�H��������q�����n������)�Nt�o)�L����t��D��p-�;o�� <wyM�A��A�����7����~.i���38+��;0�ga���
{�8����+�����0c1\y������LR�T$#������v��f��Ax!��7%��xyJ���?�%�d��yV��d��y ��9�����(w2|��<�Nt�v���R������Z�����-@��a�A����5�B��
�h|~?����"����������-����7�����%���!f����c�~���{��n����c��A����.c����6^L��B�p�����0����
o���K��M��c�_E�1_��/C���5y��>�	���Q�1��2O�=O���(v�B	1:���wQ�Q�nu�����QB	1*|! ��B��H`��0�@�C��!auV\c�5�~k�j
Y$�wc��{B@! ��$a�9�/b�x[a���_�9�@�p�B�js�=��O����H��I+G�Jq��P��|RY�c?y OPM�T�U���O2��< �����y@����8�{�A�AYe�q/���2���q���9��A8���8�A�>�A8�}�s��p"�"�! ��B��E@���Sw�a;0k�;B�^�r'�M�K! ��B�h$ Bq:�m����f�@������5�9�]g�	�������n�����+�9�q��a��&�����:s������f��Dw���u�����<�jPy���������hB[1���a�����P��V���[�.G�y#����9�4��~TAy�l"��#��D���o�GS������^����"��t9�A�.G�U����m\���C�����(W�S����
O�B�+�8�r���u� ,�U=w���5	1���0������F���$���:�r��u�������dd��g�n�i���0]�`�E�==������|��>����R��Ki������!�����>L��!-�7���nB�=�w�����������G��L!F��:�h��1�*���
�����0bt�(����2� |��m!���T,���r����P��%1j�9�	�����&���o��M��m�u��,����2?*�� �{?��n��Zt"��w��s������?���6G��?���6����~���q� �
U�)�h��~D�_�]�1��������s��N�������1���p�p��!a��c��ia6�p��e~}~�8�A��W
��p���8! ��B@�	��0;(q�����B@! �"� �����pr�0�p�u���v>���7��p!���8�a�^0��p&*��A<��t�J=q���O9�Q��Tf���dv�
�R�gv^�=���&���0������H���|R���T���PA4����g�� �����A�[{�������;���� ��A8���������T�>�'�� �C�����d������~L,a���q�K��A��
:7c��O��Pa�6�� ��� L��� ���^mV[ IDAT?r���������e�-�o�g��V��)1?�sv�^�
�� �D7o�A��3��A8u��ia���A�s�D�Y��9;���� P�x�(B�
o�A�
M
��������c��p������O�� �DExqN��'��B@!�#qf%�;�B@! ��QD@�� tvWqN��#���|<�O,�(I�c�r�����F��h�6\�
�s�� <3z/X���?��)84��(��D���5c���6������	��O�I�r���O=�-��02����93��!9�&.��p<=0C��������A���A���^�e���r�9�������Hh�1=��.z��~:<<���G� ��0y��B@! ��G�����D��Ia��`*������'�E���u�-����q�Ds�/�r��gh���������Qr��~)-��B@!����@8���6<������''a��>��p+�m_�V��Cs{��A�����s�5��c��plF�S�O LZ3�����w����:�T�JZp��a��v���wQR ��ca��u�7?�Y{�#�hR ����o�`L�A�1����h+�Y^�b����M�e���f�?b��O �?���g�K�J���R�cQ^?��<U6b4)���{�}����QPG���@��O ��!��T���� ��2]h;)n��N����n�Z�p�Z���@�C��r�<�����,���7���0B������g��������7k�O	�O����:��_&T�_����'vn$��`iR �~��@xf�@����E �:�V����'����]�i,�����L��a��r�T����-�7��Wii��d~���������&��;5�?�5����'�C�^��Vb�c���\{l{ ����W����E0c	\���/#������Qn���|+�[�_Y3�}�?�7�����������):�w����b���(oR ���!FO'��
��I����F
���G~��^�o������/������%��
Ue��~U=\w������x�/��H
�_����}�y(=��@8�/-&C�~����*)&������������v���(>������`45:B����r:���(c�h���;U��8��B#�eaN_o+$��P�c��B@! �A@��|��n�v`��� �8��B@! ���$ ����A��IG
��v�� ���m�(�A8����Pg	1�� \��*����M���u��!F39���w4�h&���<�A�}��`���o�L#�B@! ������A�b4��p6��o��p(�#bt��btB�LB@! ���!01�p�(��so�]Iv��^��0���������N_���8�:��0S�Q���4����(���GB�Nn���'?�8c����0���B@! ���avJ��0�$e��B@!pt��@(9��M
�?&�����w�����Z��OE�`��a�y��X�t� \�Y7����5���u� �%�yf�Dw����Q�rGB7����������sH� �b��r��v|[�`3j�����B��`�@��A8���9P�TAY��Dh7F0��A�K|3��FkM$��<�I�{!Fm�M����5�����F�Ah�r� ��nk���~�����#����������T��\���z{��}���pNf���z
���R�����B
�{:Fr��=P^�9k�7�;�����d�^_b��D�,�d�r����A����?JB:��Vs�������M��.�4c�1+Go�	s�(O9nb�P�	���_�o��H��i?6����/_Gx|Y�{�����O�t��Ka��`*������'�E���u�-����q�5�.B�=�w����k������A�����7�������9+��;&���
Y3��g��Vos*���"��||�r���c�,@����9'�����{�R~|�+�����2��< �����y ��9�����(w�����D7oG-:	T2?�r� \�j�����������Q��D���7����0�;���0���9a=V�6GB'��t9����7��#a�����0]N������9���nhj@�_�s�� 4��yA����q��PrNT��D��qB@! ��9�8s�*RL! ���
� ����,!F'wX��Pwb�{�7��P�� ��I� ��I�qq�'���v^H�{o>q(�.�.O
�������c���0{;��P���cG�<&�$�$�2��e�y`<��8�A��/� ���C�� �DExqN��'��B@!�#q��0��"���B@!��  Bq�����"�a��t�z(�[�@#��G��~O�^����m��{%�}�����Tlf�m"������Y+�D��$���c�w~��T����uW�����O}{���d�n�iw� zc�9a��`VVN���8! ��B@�����p�(\��t�}���$Q��Ax5j���k��]r��j������E��W����Q������������e�]{�[9������P/����k�������kFc���tIz*�T����n�v`��G�_"q���"�;��F^B@! ��G#���g����^L^���_���'S�Yx�Q����XV��2�y�BU/������%?�c���m������Jd�W��u��c�9���=�����=��������>����9�R�x6|	m�8=���������/G���5Y�]SB���jBE �P�	
�d�(j�Ft�|�Q����	�~
m�G�1��i"�
�^<j�H�	�;�7y�(-��xs3�u�_����H������@8�6����B@!0�	d����m�@�F�����@��K��>=&9]1���W0�g%"�IK
! ��B@$	�@X@,��{����2���bm�^�c��/��K��pNa�����������<�����9�h�B��/a�@xT�aaL��k�:�+b~�%C����Z� T*b%����0��r��?o
�{_���X(��c!��u�7������������+�T�F�M�i3Qy�������&��,���;,���`3j�����B]�`S��������g�O�m���UP��{�A����� ���� <	#r�C �5�9(���-����U����m������@y}h�Q��gzv>@t��x��Q.��������g�UE�2\�s�E�����'a���\C�g��c��P�Y�/����
� �&f�oP �dT��9��������o��s����>Ob�
_I�U��^�'�'�p=�H���1n��'��l�t���7��h��E�K{K��,�����|���OG� �p�!�[Z��i)a,���� |����fC���7;������p3fY��2�Q/��z�V���W0�T�E_"Pt�C <{D�Y������9���5:���O�7�K����C>��O��+��e?����9;p/\�r'�i��)��n��Zt"��w�
�k=�;�����3�#��mm����Q��A���7�O�O:^;_�Ux��a�x'��u��p7����Bs�"X�s�@����{�����	?�"����(��H����Fd�W����I���G:���/�}�
o'�@�
M
���-��p�H~�?vy���o?���w�4�l��$��~f�G����?d~��1Yd}��A�Yd}��a$Ye}��Q�GYe}��Q���d ����@������	1��^��6�0�=��DW#��
$J��������B�����
�@�0��w<�kZz�A�u���eu��N��A��K��QtS�[F��-��j��P��z��1Q���Tix��{k?�E�a�.rLx����1���V��D:�_��N�n7�FG��K���q�=H�"����BT�\b���N����/�-\��$|��]
�oQ�t]`-8���f���Sk�NtA�n���a�tB8�1g�����](��<��&�����:�	���k��#i���D^����T<�Ve��J4b��RNA��#!��T����z��H�b1���T�G��Oo�_��8e�bT���Q�OH��k���
��j�Uu�"Y
���0�!�*�����cG}��f��{�B�����G�~���������Y|$(HB@! ���I��^�+�~��p�x�j�h��h-��k�������~w��������`�9�xb*���k.�����5�[������
������C��Z������q/@5@�@~z���7�pK��W����q�����r.�������X]�a/<W�@E���3������e(�{2[D�B@! ���Dvxv��x��$u�����U/��W�5c��N9!~����� ����F
�/@$��F~�<��~XU�����}��O�����;p��V�^t���f_9�P
����R��K���[
uY���iDy[0J��t�{������=���}�H��!��2bX�3��.��?�_��\-��]�D/�n?��sP�%(�n�w��T�Xe<���@�����Q��u��]����Y���f���#��~�g~o<����.�Q'�F���� E�2����1+1f��;��
W�w����ff�,"���;�}����}�'B#��}�s�',b��Kj�� *�@��b��-g�w�B���X7\��(=`�_���x���r-E��S��*����P��n!��@^�G���"�*kwu���QtjJ �B��;�)����.��:Sp"���	Ud�]�%Xs&�����$�w������q�_���6\KC���6�/�lD�.��C���C,�f2�l��1����v�w�9��Q�Xln�����h�5�#|�7��
�V��0�����_����1B+)*n��q������"zV��P�����@������+�W\]�`c�/'��m����n������q�������S�m�`���YN�R! ��G3������?�2��'���A����(����E����?�|�a~�'�ky[:���w)�PY���E�������O����(�w�g|c���?�o����������]y,���� /���_"��}L��K?G��a��YB������AJ�	�!���y��-�v6�]r��c��B@! ���%`�����W�ia`��):�)��n��k�Iao�������J	��-wPT�����WO�vev�����m�'��������$��=�z���U������>|	�pU��_qMV��~u����I�����������M����=��1�
O�=cEF���p+�������������T�=K�������p�E�_�����@���=6�v���$�N�0gb�+(���Q'����������)>��T��@���#�[Bz�@�\��������e��#CFx��G��3E ��'���B@!p�� �o�b�����5�����%)z��=���eC���xJ l�x#=��oY=U�����5�@����w����^����,�p,P��B@! �@��������������#$�����R����J��y�X3��B��{�J����F���`��&Y ��~���y+�q��D �������?������1�]�c�[������	�y���4����1�b�����U�*����H�-�'�_��:1�{a�{N
�4.�?�en?�`XQ��:l���1����n?�1{iN�:���(�r�]b��h���/���s���oP L��Q��;������3���:�m��OwSF�R�G38_�<��T�V�;���\K
���|>,s��A����\��r��O����X=�mp8Wf�&{���r��nZm��Wc�a�*A�F����s���X�0��Q�y�o����o��dx���i�8��q3zV5�&��:���P{��Y����Z�2z�L%��b{�`��7��SQ��3F�	>�-�.�]��b��G��8�K�|�^i�Q������'�$�&����+����9����������p�?��<� ,������R����a���x/�'T��������s�KL�SB{{����w�v�J�[�S��]��;o��;6�?�v
���� \�v@m�;Bx��9�-�2���s�'w���)���
�7o��vhoG��w8/Fy=2�e��?���I���W�%���uS�?�������i��O��*oij_���N�W^1v�\3W��m%��7t����+��v�r;E�:�SS�>/���������c��n��=��7�x�^z���L��^H��������af��~u;>�G0�h�o����=�:�
n��������q��!�j�$�������]}�b���>x�GL��=���]1�����3��6���!F��@#�6y���������m�7q��X�N�@�o�	AB��}A�.rZ������� �?l�Y� ���))(��B@���@af�p��?�A 1:�@�?f,a���@8�gb�(��r�B@! ���D� �'��f�@��$�_��@�9�Y2����:�@�h�B�� �s&C�]7�@(!F'q�L�jq���/�8����8s~�#�'B�\nO�'�4��j�0�~(�P���{�V���t\��0���� �yH��w�����@�_�_����<����A(B��.���� a�@��-�������x��#B�n�<�lPqt"�1�����k������,����1�]����vK��QH[��mw9B���R�8��v��W��|��x\��j1���i:|�~�@B@! ���T� ��W���]?�b���=�M"�! ��B@Lq�����Bs�:�{��>��'��q�]�a����g��j�y��h�#a
�c-�p%�q7�l!��a1�fe�AX�z[���c����W)y���d����1�q���O���!Fw�Y�6�y�@��n!��H^�U�v}�����K��C��(:����X�}���� \�~}�@���+�[F�s���sw�9�R<���@���0�2��
nFm��q��S�����j�6�?��J�=�u��d��l9s
1���P��G��B@! ��!��s.C}�_��������c� |��1������G]w%��d��P;�0r~p$�#b�%b;�.I� �p��^�! ��B@�����
V�^\�axKR�6|��>�+��}'��'��7�^o�����������#�>�;u�lG�}(_�@x�#a2����� ��F1:<�o����7���VtOc��i���t����� ����'
U�1�-�zt�DC�>���U(S����� |[���3��������	��g���K�.03�
�U}��!��'����]E�;t�8':�b1���a�/@y���������2s�a�yi�������>�PrN���8! ��B@��A����Ax��j�p! ��B@d% Bq:;�8'w�� ��]=�	��{�O;�.���(�Ns8g�;������A��}�sj��Nq'7F��vI���R
e8re��nC�wf�?��0]~�A����bIaR L����VbyI���A���A�/�;m��OwS6����_r�
���wp-]4�c�@��a����������[9B��I��C_��~a&~ab�M���Q���E�:ZqM����U��� �����>2	� IDAT� ��;3�?� L
��!&�����}/�
�7)�� ��fC�=�����)3��w|��!�K{:�����;H�_���2?��p��oK�~az����� �3��P[w�������NT��q�s&C�fr��'�������2�m��\��g�z&��{k=K������5� ,�n��p*zC��� ���Y����T�����S���Sp�{#��su��/�W��3�9_�����8}C��x'�/}�� �4������%��X� *���=�o�{~K������|1��B�� �DExqN��'��B@!�+qf%%�\;��B@! ���C@�� t�VqN����o�@(9G����PrN�t'�! ��B�($ 9s%�Q������B@!0��� ��CB�A8��D�@��f������{:��c}!F���j�y�bt%������P���e��L��h��_���>��O�>���/�1��/�hu_�����l�1Z�b��1�d���1��A�4'��9�����!�k�E���9�!F*���0)2���-}!F�9�!F;G�A�bt��#T<�'T�Mw��`M��s@���P_��+�[:�[��B������,!F3�M������!F��1z�E����������5�B����=�b�ud�{�A�������[(3^6�x��[P[�s&C�nF����d�9��j�1:Z�b{�`�&���/�h���G�	>�M�.�]����4+��/u���S}~���������O���oSq'�S���������s���S���	8�}	���S�W��{�'Q�~�"/���-��=�����[��7�y7>@`�����>ul���a������3��g!F��]��P�Q���T�����9�p��ia;�~>vh��
x�\��z��~_�Y?e�������z.����/�%�������G:�!F@�	��W�btZyS�)bt��UDC��o����q��'��1:������������?&��&G��{�u?�2}�_{!u�j�\��u��������
�����9��o��E�=����
����N�[�t{�3V��d<�^*��b�CX�va��b4�bt]�t��atW�����<����
��Ru��Mj$���#�������� |�;����'��/�m��}�(9W�_���A��]���0).�i�0���p�c����8�A8j�7��B@!�^$ ���>4��8���C�Y! ��x��8��:��8's�K��U L:�����u�Ax=�����5�t8�-�����:��>�;��AvW���0z�n���aa'Fq6�p��a��Na&��m�p�/8�N<g8�����mr8cX��tP��S����p�Z��B@! ���9
���KP^��(! ��B@����� ����+9'w ���m�^Bq�!^Oi}2���J��f���g�?UNm|�b�qh*��y��CW������3��hQ2��Y��-�]��������/��bG����9T! ��B`�	��0G�P����B@! ���|� �P�P��9�9�c�N��*C���#a5�=9r��1s�(9��4���A�	��:rz���8r��!��0�]R��A������s���������z��9��-��p��9���m�����p)����� ��aG����#��F�A8�o��u��C��Vz�9W�;��?�(�r� ��r� ��}��8��bqG�y�o��9���9a5�&�����6G��G�9
to�����|�d� �Y9�uf�oG��>������;�P����Q�[��d�yV��w�<0�����\>J��x/����A�7�L� ���79r.�];�`���9��pv��}�~�Z�G�A(��]��e����v����d��y@���}t�������H� �W^�#a2��r� \I4�c��S{~���;�0r&������s.��y�����m9r��o��N*�9J�{<��(9K t� ��=����&�{Pq��PrNT��D��qB@! ����8s�+RN! ���� �8���,B�#����P��|�H��\�'����Ixr�h�O�?�'���p�'����z/��� �����#YOe=��T��d�y��7��0����tf�)���������~e�0��BJ=R������U�'�5P'B��?��P�� �� /�������B@! r% Bq��W��B@! ��A(Bq�}Y��P �}m��o��(9�����`��d;�wb�[��5�Wu�]�'�������P�1�N!z��Q�����Qd�]�%X���V�S�G'BX{�]vK'vk���z�hK�L���h��;��=;���-MxW~|D9m����E��]x����UY���m��^�R&:)�u���^�>����<2#l�����/�}�@�w��|��
��X���|gc�&����`w�c��z�=� \�m�`�G���"��B@!0yFD?J��:w�����|#��[i�����U�mi�w��S~�u+vC/��� �W������4o��S{��!�?�����G]w%��d��P;���M��������1��gQ���<��������s}�Uk�;����C/���w��(�{�u�B@! ��S���I�0� L��{�������R�w�7^I�T���W�� ���el@��x]��Y�1�~���1��
n�k�����9�C�t�<���6���'����?�*,A�(��ki{��T���aP��ov�7�xf\s��B���^t��9w��O�:a�<B���|��FA�B�0��!:���������'�I+�J������O��?���K�W��p�;p-���������"�7�k�K! ��B��H �@�����!���0��] �!~��)��s�mt��|��0��
!;�@��I��_��5��Vc�� ,?S���I!! ��B@��	�@�Ob�m��N�#$��sw�lI��/��;�DQ$'���w���S����
�F��	�K���TI���x
����StX�!��,�v�o��*G��v��:y6������(�N�]��s*��t�ZT�JT�nt�B">�b�"�x���C?�4�S?�RT*a#l�a��F�8n6��3��`��Kj�� J)���g�x���n?�1;�������~���n�"����4@h�:�}������S>�z/�@�J,�a��D�}!F�X{�A@�|�8���H��_Fa&~N���� ��}�����P���o����o�L�!�����Q[�c�yij�T_D�����a�R�=����������
��y�sV�?��'�2��C�?�AX��C������0��1���L�o:W��2>F��A�����0D \����xo^�����F�����u���
��@���Ra����S7��~%�r��5�@�j� �Ox�zz^��D����Ly5gR������>��C#��C����� _�2?' �d}��aJ�C.F���O�2>�=�oKS�����St�p�a���5�$�J�����p���-^E4�����)��W�~b�����?�`;��C���v��������?��~/��^�&�U�q�R@|�}��H!�����1�w�������`�.Ku��U
�=���7��H��Q��#�)�b��E�)�S�w��"��~�_xr�����0�����w������On+��	�2>d~��P�CYd=����d? ���~@������d? ���~@�������=���'�h"
�v�6vy)TTb�4������DCs�	����e���f������t5g:x�8�D���;�L�4�H�������%Na�gO��s�	e[��to'*)����>��=�y�r�4z�/���������uE��,�7�=s)��y�/oCc�^��6�}�������N'y�oM��]�B�Bbw�H��+�4.��Ob��&�����]sJ��8�����j��=�W/�v��y�{_FwWc�6�Y�]6w��:�(��9�z�C������c�k&v�RT��!e�#��$~�Al�rl���u)�
��(Z�~���tm�;:QE�p������{
UY��~&=o��9(w��0}	*��t�z���8��P���LVg�S�Y0�A������/F��nA75�
KQKW�=`��*�\��X������jN��g�}��g��\�����(��(���B@! �9vK+=��
�+g�Y��]s�qJ���j[�����x7�	\n��MP��(��Y�:E�e��y���U�5�?����R'��~���M���M����.��=��s.A��c�b������L���l�U3������D�U���C�[]-�XWEn����@������������=���s
&gB@! ��8,z��(Of/3������1����(B75C�l�%�m�<������Bt�.��>��f&v�M��������n��_>�������>Ml�Vh9��~�=������a4���aza��sN��;=��������&t,��f1��KRe/�F�����B�{�l�@�~1��%��d�9vG#��;��k�����GW�W`n����Edu��o���T��x������kR����C��%sQ=
PX�v%]��_��C���Q��v6�{�\G�@������}�"tU
�����K�����k%�S�@O�o��������`	���V]��y���$�?���G(Z���0�}��C���	��cPW���@)#�@hD��
��o�W��� ��?���o����C�G�z����������\�z/���h"xV�'�������wR�����u��_O����n���?+%F#������@�]����D�_H���a��3R���g�*=��kOPt����>��N\�>��9�h�n|�]���?��>/V���BU��z�������"|�����
\I�����0�`���;�@;�(�����J
�V��B7�C��>�a���q�/H	��O~����!�����������}����"b�/�*�!F��D��?K�eO�W����#w04N�u�U�sz����~�~���Q������_zAJ �7�./���B@! ���0��@���l�o�(��>L�w4��*��.flI��j;V�A�UOPx�W��bZ���y�
��_�hZ���G!���_|��_�k�����3DW_Hh��$�vo�}�e��g���o�a�q3�?����EKq���U:m�Q��/`6�������/\�=���a�;��vQNu
)���NXr��W���&+.�p�$�! ��B@L�����a����w~�y��)�������A���>l�����@���;������^J���)s_c^H����@���D��m�1sq��$���N����?�Z���@l~%�\���������7��g��R��w!��K��I�0���Qt�O���B���I�����J�����������������Rz+;�������BWU\-����?S����Y�$��V(�@���SO
�V�~\���/Ay��a|�K��/�p�4�"F�LE�0|�u$�|�����\�����_����T�~���"��$���0�������Z��k�&��m��;����'���@8��O�B@! ���'�I <��9��z�Y��i;mv�A�Y�R�?�J=��r(�:'-����:(���Y$>��x���'��WO�I����1������.k/��5=O���_��]�������@8.jRX! ���="��������T�{�b/�'�������X�������� ��;u�Q<���o������2���R�v��oC��.%~��0s��w���@8���#a����S%"����pV}�A�����O]�p6�.8�ea�nT�1�����Ip����Lov��D�!�)�Y���dx����9�<��(��B����	���y�xo��4c.����K���w/�_��q�q�\����7�����j
����T}�^@�������g'vS+����S�p85t���_C��!��[�������AXO���e��3R�6�\��}�q��=;�K���p��w�%����S��y}���a�*������07�I��S0��0����t��S��pYF�J��|w�����Q���v�@7�G����U{ ��a��X�Aa
�O~���7�R!���l��&��j"o>���V�K���k~�� <�jm�y��������� v���A����JDO;
������|:��o�����r1���������)/�����~$�H�QN��#���x��&��ovK���q8���s�����s!����L?�{���'����x�kN�;�
\wh���!���������~�~,�A���c��#U�<�4B�'0_�b�}�s.�w��E��j��U:���7��:��r.s���=��>���
�iu5��n�[��������r����Z�d������+.@��2ne���?���Iyg2�d��< ����2X[_�,<�W��L�����s��/@Uc���Zvb*r]r��n�����8����t��v�g�r8����s�8����sb7���;>����������������C�i	���\B 7��	i�p	�4�I�/8��)��������q�m���-3������W�%Y��������3��������}��>m
�=7\��HI����EO���B�M �B��c�V��}&_2��=�^C�l�fsn���;�����e3p������Eeh�>��Q��x����o��s?^G?���)V�<������X����3����~O��5����=�QrG������;���j?
�_�c���Wh�����8-�n� L��K���b_t���I ��I>BA�@��)F��b�@�CB����@�T�@�my*%�! ��B�$������kcG�(����}����-��s�%	B���;�0���=;���B@! ���8}1q�I ����7���z!�M������@8��BU\f�u�m�@�'�.3���8�A(���I �c�� ���
%y�M�@�h��$J���!q����8[�O��������e>/o��SA����>�8 Bq���8��:$Bq�����?A�aK1:���iK1*Bq����B@! �@G���j��-k0fuU[o�C��PNWGP�6B@! �����8�Ah����p����n�;O�]�wv�S����H�������*�{��d.��:�}��1�J�����~�����1��@x�����O1�'[
��R�6� ����_����x;EX��P#�*_lsF��p�g�y�� <���� C�h��.D����M5��4���V;ZS
���COMX�2C�� LnNYa[
B���R,go�}P}���wN������a8F��b��������Y�kM	����]����z�
K<�����A�A��Wq�
@
��t�����o��xz��Z?VnN�p�V��B@! ��@\ l�A�F�����.[�Q��A�<���5�N1���_�rwe����"vY�����B@!���aC
����5� L���5nx<���G����0�f������j^��{���W�C���'*6� ��~O4�hK5�)Fk���<�16�3{?lC
�?b��b�A8�+����YT�����x��km5�)F����B������iM����
���		'\j���(�6��Kq�������q�~�p8{��%�p3��|��5 IDAT���	�YjvZ�����B@!p�a+�N�'l��B@! �@K�A(B{�a�����
9��������ms.�&�q���T����03V������>X�)6�m�q��C�VMJi��a�I*\�VW��6�M��^���0�?-;������o���<r������lt�I���� l�A8������#[�O��&��;����B6�3hsD��ms���6����o�A�� ����?G9�P��5
�a���� ���J�9�a4�p|1�K(<s^;��>z�� L���W��7v��A��m�w����|��r>m�G���'{<�� �A�C��B���7:V���A��"��1���}���������!���HU)�~��?l�A��b�h��w�%^I�j �e<�x����?���5�����h[
���y�9��p��?fs.<q�����t�a���������� tc����x���� ���;?m���F�}&�J7@�H,Gf�zI�aC�Q�u��H�� ���c)F�A�Q|��B@! ���	���F� <v�5��B@!p��8�]V�];��a�����K
��	=���h�}�6T�V�L��R��k���.��B@�O��� l�@(5?�*�(��B@��% 5����;�Zk�^ �Z3�h
���w<����b��QX��	�~
���8�U1���O1zj�>��cl)F��v=L(���.%���Awg��=\�b�w}��v�������S���)F�L�<�)F���=v���T�0�b��T�!<_>��_^k?�fm4ye}�QU�b������~�����9
)F��
��F����	��gq�g�y#I����?�W�c����#�l�.'���1}�
[�@��B�h��.�O1?>URF�'(���1��Gs�M��}�gLCzj�*q~q����J}�`��abmJ1�P�0�b���k���������:�)FS���m�AM1�>�5%��j!�h�_�j6����h�����s�E�(��������������7�=�Aw�W���z"�G���7��r}��{w��i���������b��������{�G-�}�������F�Y�c���V?e&�Y�-n��������w)V�XE:)�h��P<�hY}��v��������j��{H�r>���������~@�����!���|^����^��L�g'���)��T��c������/�����cj�����zw<�h���w�l!����������v�����B�j�����c.Lz������l��O�#����Vm�����)��f�t�\�)F}�����l)F�u�mc��� ;���>�����#�S��|~�)F'�t���;��ww{!����0X�8��vRz�U/>W/� ��_��3qd�S/�T�0*FG|I�A[^�k��rL |�&�k�!e�m53d�A��j��;��2&~�cDC
�[���[
��&k�}�T�0*��j����%.����� <�I�5��B@!pba+�+)��8O��-{+��B@�d� ��WH����R���
�-9���l��b=��A8+��U��H�A�����9���Fa\ �FJ��:�%����9�] �:�5{m��;t�A�1�3�����[v&����opv�@(5�6DI�B@! ���|�����PN������! ��B�& 5�����a�?��� <��&���'�
! ��B@���8�#���-}J�B@! �@w& Bq�@���P[
��Z$��_�C����a��_�� ,�{�B[
�Op��b�A8��[
�o��sl5��>�����A��l5�����sj����j��~r
�����A������#��� �5�L�e���u�j��b4Z��M�Yi�A��y�m5-[��h
B�c�-�w��7��� ����2���m����
�R�*0j���w�V����v�r[
Q�F��0���n%wzC
B[��)F���?^������� �0�K(<s^;R�f����	���[��=���X����{�M8��x����1�w'�/�O���?%�q����}�>I��<���� �����I�kN�=lw'� <G��N�A��M5��
���e���	om�5e|��g{�����d��?n���q#�F����c����S@��� <��a�A��)[
�Yw�j�a7���a>�2m5����j��P"���jn���;l5Gn[
B7���V�p?^G�v� ���=d�A�rG�j6~�jF��y� �s���P�R�����8;JN�B@! ��h#q����]EVB@! ��IA@�� ��7��AXWE��0��� �a��4�1�@hOy�Cx����&���$HH�� l�Moq�uC�r������$�tq@�^B��3���	}��{n"�z	���	���"Bq��R�� aGyqv��l'��B@!�F� a���&��B@����8�A(�Oo(�@�MB�
��X�
���y�e	="��������G������k�F����2+��U��V��:*+AF����TanC��C��q;:��X �[�e7����i�~�&B���I{�}�6T�V�L��>zw���9I���r7�����1
j�X�9=r�N! ��B�S"�,>�'7��~}@�$Jfl������7R���b�UyPgO�=�#��+�6��GH����w�'��I��K1������z�)���s0���_�}��X�cqm����_�Z�Z������y_�r��3*��T���7�]��V.,��
3���-�����%[! ��B@t"���/�~�P�|��YDh��G9��q>� 4������sa��o��T<������sn�ZZ����Vm�� ���5&���E�h�^�yp<��_���W/��G9�`�;���[����������O�L����o��V/���k�`���g�t���rd�����7�?�k���l;!a7�~��o���w�����~W��;T�����%�gW���0�HYaB���������R,go�}P}���w�8�j����}��������06>��yY��k}��E_G�o��s���"��B@�nG�9������������H}�6���)�����1�P_;����eL��y�7q��7<�����i�N���������/S;lO���w)�A�YY�����M�1�E l35YQ! ���O"�����X�
��u��wbVl����P��&��v���woo�����M?~���������M�^A��Wa�x�/��#a���r�������*D�����9a$V�|��_�efb�_U���+��\��u6j�>����*
LU�1��B)gP���O�twf��!|�5���2Sm���;|�!�����4H���.E�;��U�����?���Iy�%�A�N@�s+���/���&�������@x&��|�����7���n�3��=5��e����!z��F0����q	�7M��V�6����c
�a8F����[����3�~���kV�����>�������g���n��������_R:�TR��2_��,���s��7�
��6�
��Ca��Z�/�A�[��������q����r|��9���5Be|�����L@�����B��O�_��u��u � 4�4r�����O
e��G?'� T�/������m��A���0'LB+=��/�y
���1^~<6�u5��B�_�3g^��p#F�P,g*�5��^�$ZIA+�Ed�h��}j+0�M�r{�Ot�s���A�����L������Cb��)�S�����������w�a�����?�_~>*/s�v��I����;�<���a��&�k'��/"e��j��v�������n�����5��w;��"�VGd����Q��y��}8�����D��/V���P������Q��!,� m����??�Y"�����}�?�����YT�����=��������~op����o-��t���c��,��rd��|*�~}+B����G��������"��w�����!�C���C��r=����d> ���|@�2����d> ���|@�2����d>p��N����Dv���z�b��P!��[����,�	g���A1Q��h!C�;�b	����c�YX�,5���6(�;�[Q�[x��e�t>VE%(��Hl�(��Vm������5���G���%5)���]��q`�]�rD��UW�Q�	���T��2��x{u%`���=-���mT���8Q�&c����x]:3T�VX��SW�#R�/�&�����B����Z��!L-�~�F6����c�	<�����lL�qg���]r�%PK�in�3��c����]��`���PYC�
�Po�����qh#�&|�Y�Q%V(����L:���	C(���C�\>����X*���l��
����rM���~�/�g���j���30��e)���40�`���2��
kh>����b'���q������l*p|=1{�C��+�
g�4�'6`�Cx�|\#'&a������i_�l��B@! �@�	Xu%�?~GMoB����m��:�	USyC`P�Z�w�����1��w���HI#xf?��~p���G��o��_~��0z�����k�k+��Jqcv�:\�5���4����&Z�p#����BQt�����������k�o��JI!2}F��h�X�<,-��x�b��A]�oo(��QZ�6|J���m?M��B@! �@g8���k�8���sq��gQg���V��8{������������A����y�H!e�Le����;k���+R�R:�^��V��E����+�H�|iga���-e��G��-�e��Q�����>�r�� ��#���c��7cz����"�u�q�bV��wH|c	YCp�B��UU��1p5�c�A+��7� '!��H��T��#���Y�i�qy��K�
�,���Yg`8�L��Y�	�s��r��b����(����w��<�~���K^3�>e<V���U�Iw�@���30/�V=��9������Q5LG��G?� ���x�o <��F��x�\3�b��w\ 1��*�MK���qV�	q�&*FD���7���q�/����5�Q���Q�0�g����<��u_���Kb�
�a)�3c���RT�A�s�D���q��G���%h�����������;&Z�?�?�59���oQYy����{L��s
�����A
������.C�E��WN4�g�R[��w_���9$��b�PY�m�f�g�����u0�v�_U��v����V
V�/������F���.E��5�������w%����@��^�����������"TZ^�@xp�Ckq[�X/�f�m����=g@�q�O����1��K�[�A�t�'�6~o�2�����83��x����\������;�|�a������
e��B@! ��89	D����������Kp���{WO���p�Q���$���������9�?��~���W�+R�NK_�J�*�7|���
��\c'����q��#?�����f�&2�B��L�Co`|�G�t�E����m�>u��^��H/��y-����2F�(e����x�];p�](a����%��B@�nI���}��	3��k�A����@>���	M�GO�����I�#�����.)%��[��16�SV���ca���S��>i��x��o��;�U��qS��
�*�SYd���Bp\4�j�b��T��#���5~�6�jr��1r������J���!T�2����r.���LJ�i���+X���P��(s�KN���VR=5���p���u��|gm T�����z�@V�kDr��YW��	-��Q��w�1�Q�6�
/a�d��tK�0��nf�[�r/����0����?�����(�>��H�@�-a���?�����S�������?!�G�){D ��)�
! ��B��&p�@�pM��nu�A����C���`�?��=��]����iY7~�\��r~L�?����&^�
�!3��(z�ga��� :���^m������"����,��B@���T�XD����������d��'!1�J�M$lU �y��7�J�D��A�;m�)�-V�������x��w)!G���I�=�o=k+!��"v��l�A��;����#�������Ukl�t���UO�f��9������~���8�0�[js����6�R>6U|���3��4���N��pB�}������8GMO����M8��E�
0G�-e|lT]����D
j���Q<_8��[��m�$F�
c�v�(k�AhaK�6}��zu�|Hj�_DK���)��}�a��~���0����_m������/b
��}������m�Dx�,�^���hw>ls���},�h�A��{�_m�fuzVO�)��\�Z�[�������X{W������j����TJ��������u�6�7:����5�bj���� ��x\j���6f�|"��������u#���	������&����8U��l��/�s8����'�;c�I?�~$�������kd��������ye+�7�����.������h��������S������2��A���T�7KL�N��0?�@�����{�������%��Ox2�aL���7��������}��v��ZW���{#�>n����E����uXc��oS��()��A�������^��r����\/e�,q@����N�8`�Y���k���h9&��kQ�����lBWl����G>����BU� ,!��[O�~`��Q������m�j��x�&�6�z�{�L�����&^��������g��g]�c�xj��[qNC�mC��c�aL�Od���������R99������s�s���*�0��=�p������a]�30*����F�rZ�����hC'������v���8���0I ��-��	�������A()F���)F���t@ ���zR%+! ��B��hpz�y�G��D��A�I�|r��jF lJ1�1����qv��&�'��B@�����9�%��7�h�����o4� L�S�v���@8�����W�C �^����j���A��8;���8O�7p:��
i���jy���qE����r���)qQ�������� ����_r�����/_$H�~q@�� ��Kqvm��8	����n[��Tr���J�� � a7|�AvI! ��'q����M�t�b���R��#�k��QZ���,B@! ��'"qzaS�5vm@:>V�P��'P� ���;��p�`4�i�O��m��5��[
�a���#}�7S�0+&6� ���Ho����T8��g0Vyh*#��p��jB�a�\^5�[p����K\��7���?[
�q��k�C��
)F�c)'�3c��v@E)����/����3�]�m�6�����Xd�A������\
���w��z�d�?*�T���xj���&��X�}8�D-��,���m�Lx�L�^S��B��/���@��Wh#����*�+���G	^�e,oV$�Y]���j
!%��j��>��
Zt�b+5� ,B������[�AX���N�|w��WpM����T���P-��{<G/�
! ��B@|
�ag� �?��mk
�K{�)Fj��M�yx,���������|_mS+?����� IDAT@X�k� ��E <�9����B@!p"��
53b��\�p|��stQ=	�>X�N�� \@��DO1��6� �Ie�E�w2�q��m���87��.9���P�+�_����jc2)����W��1&-���U�������l5[��"v���8�A��1V)�������B@! �F@��+p
,a����%��B@�nO@�� �wR�v��A�����������p8�����0+�����_�����wn�9jz��;�%���c�
�s����� <������AXNW+���&9m��s6}���0+j�A����������Kv��������]8���X�M��y�7^kB���������!�A�c��>�\�[A�&�u�lw&��Da����v�N\s�h%���w���%�w����'�$>I|��$���@��03���e�B}�D+�[O���V���c��x���8{QYd���M��om�A�
���:��-�����^?�A(BqvT�aG��vB@! ��m$ Bq����jB@! ��8)��P���,���R�Pjv��Pj��|�Njvm����B@!p�8��R�����rdB@! ���R��=B�A�9����J�@�b6,QK{7��1�h}�x�H-��V����>l
�g`^x
�z5s��hR���o��L��w����r�b�?�#�������A�>����A�Q��@�<���qV�	b[5�Dl�O1��;�{��{����GKkK�����HA-5�>��
)F��eX�����X���PQZ�b�KDn����T�R�M�PSO������'��v�&��3�-*+�DM�����c��k��6�R8��(7d=w�h/j�E�r�6���
���/b
��ri�����K1����Y���$��1���h���6"�;�%�W�
���
��^�W�W^��mH1Z���j
!%��9�=� \��?���^���Rb���4��@X�J������=�������T��S������=�#?��7��6f�|"�������H�u#�����_������O�����83�_���;�|@��E�r[?6��>>t��%�����W�W7��J���+��^���$^6���+����h���\�fz�=[�8/>��?�K�,��dmYJ�}������^S��	,y9i~�=�Br�������'<�0&\J���y�
�������]��mkXW_�G�h������/An�������}C[Z�:��1X�'��x���kFS�::g�/�O����v?����d>+�Y���%��P����}!���XY����t+�w�4��������t:�wD�?+��pI)���r�����*���������H�����y�������i�r�'BC��)�-L���]oS�V�3�����1x5{]�)F�!T�2����r.���LJ�i���S�aLZ@d�+�������R99������s�s���*�0wG�mA+r��YW��-��Q��w���j���������W'�|C��sa�|��I���z���z�0���o%N��s-��&���hA �
1���J
��bER
B���� l����YX�p+5��L#y�n_�0q��kFBY��B@! �3q+����\s�R��;�i�7! ��B@�(q���>$�h���f�G�����g�}����fN?a����8�}���
��V�c
�� �����8�)�i�Aw��� #X�� �?V/v��p\Si�A�J�iV ���fa_��m�A�G��Io��7� �6(_������
�84�@����+�� l���{�Qox����O���?��	����a�( �A����&�����A���G�M|�� ��"�E�KK�%�q���d~&��������!����5-�k�E��Y��9?lr]���������jB��� L<?���y�>�����7q��P�-����aG��vB@! ��m$ Bq����jB@! ��8)��P���,���6a��0���p�%���P����Ukl5�1/��������5��!� ���3�V����aK5���j�A��{�� �Pu�v� �e�� �5�����N[
��R��k�Q8����7j,�-�� ��r��q����l5'�;��A�;&T�P������]��'��j������jf��h
B����k���VZ+5-����a��}��R�Fk��|y�����[>n����/�_$���2^d��x����8�T��<�#������_��\�0�����Ax>\~��a|~�T�pz����a��L���()�� \Kh�N\sjv�|������^���{��$H�8 q��q�����L[
��1����+���j�&�������%d�~k�����Y�rT������W$����wZ�A8����VjNC�m�� �Od���Q���8��m5������ l~��� �����^>�p?�8�A�Q^�%'�	! ��B@���8�A���"�	! ��B��  Bq������ �Wa�����X��X�D�_[��e������������P������"o�t�
��\��)ysL��8�$��q@���2�`i� ��~��w���u]���s�$(�A(B�8a��Eq��P���A�Qr��B@! �@	��P�m�*��B@! N
� �8?��,aw�Cm�pZ����P�0���CO��0����.P�K9�������*JQUh�~��m��������%h�����N�|Q��#�5��;�AX��k���&��X�}8�\�z�^�m�f�g�����n0�v�_U��v���FD��M�f�U���{����2�7+��.E��5���J�������]��Y�1n"�7����-i��n�J��3��P��a�y���_�51��KS
�S�B�(_n�p�V��B@! ��@�@�3.�f��8����s#����i
:���lL�����H����{b�j���<I���������]t99�b��Y{��YD�^H���T��@9��n�1[�_t_�v�����D�Gkh'/Z���-+! ��B�'p|�cGx��%�d�~�	C�
W��Y3t/���F��Gc��w�C��<AF������;���Fe�E�w2�q��������������u5��j��(��w�����J^!}�2T��T�I��� ��2�U^�1i���2����. {i%��T���i��]J�*�0{K��
��a�\���
�UZ0�r��a�����m���o���D ��a�94�{����� v����Rj~�8����r����>���^ �������fP���7�W�b���r�G������m�aM���}? e�]�UU�
���w�zz-����|�"�����67F3���_�M"v~l���B@!�-$	�)��[�cny=��T����#"���9�L#m���~�B=75�gz�w	�^C��\�t���}���*:���|�/��c������D l5YY! ���?"z�����~;�1�p��dd�EE4v=�"B��ZC��R��7D�>i��=x6=����=�����^*K��(F\.
��N��)���b���t(�@�]B����1������O�@����_���}�����"�|B�>���W�����5��,A�����9c�U�0*����4��n	! ��B�x	-�3/�]1s��Pyu�W`�����-z
���%��Z�����&���	L���}
���������x�Yj��#D����?�W]�w�q<��F���=�(�~�9��S��C��5p�+�aN�����x;�l/��B@�����^��P����<��������6	U�4V�^H;g�T��^nU �v���U�n|�����z�!u�U��0r��@X=������2�������SS�2��>���H������F��p�����V��c��P�P���?d|H|�x(�P�r=����d> ���|@�2����d> ���|@�2����N����� ��D�w`��B!��)�eQ�-�gW1u��3o�>E8C	���2��p`jA�)�w?G]�8�'8s�E���,��)���<�(wz�u\��(Z�=�7Ve5(�JO����a���-��F��}���O�Y���SO����o�+Q*
�k��\i��(��*�7��C��M��q_�w�����9X�A�bZ]���q�-!��u�oE����o�'��)k�7��a�}���}82S����>��*�,�J����c�QN:��>��+R
���q��c�aU���R��R�}��Sa��$�F�!��#��F�\�U!Dz�������QN'h�[�b*G���4��TC��+3�����a�YYX�*����_*hz��qs��}��o��9���D�X���������y�i�I�����L��q�y���y#�t��Im��x}g@���K��� ��k0��:k*'���{�`�B���k�����Q��T�S������<�'���H������!=gn�i���AB@! ���:f�F��Mh)����J`������Jw�0-�/f��g*R����c���rj�&�OvAJ�O�@(Lx��F���w��<��@��P�X�����k����lp��'����m��3�jj
�4
+�7��������UV��B��h%������i��vC�
��B@! ���c�R�t7������*#t�-\���r8���8�P����W�Ee�����k�b�`1�f?�@9)�/D�4����a�%>�?�����J�����u����x��F��_#�j#�k�0�z����2�Y���x'��[�c�V�g0��/7������+�������^�����'Re�V�~�l��E(�AdO_�P)�Q#b�I��u�}C1ra�\�V-��cN�)W���9�>�d�G�����|����Dp��P�����(�1�Q���9��;�t�4��Pa������������2��T������{�:������I���������r��&����������O��ba�d?��Mx��De���@h�!�}1+�b9wBk�z��I[>�pcM=5�y���{�H����Yd�
�g�b�������9�H��47��g�4��]B������k/��kh���1�0X�
�����KI��.��B�����{q����z������������+D[�/����>X��Y�.��ah)�o�?��6�
U�w}w��1��<�1���V����V�����uI���
������>g����a��4\����M��#�=H���x�Y��rV�@h��qz�c��(�}e�@Hu	*��� Vn�N{`Ck�A�Y1�0|�8�xC�@�~�����J�A$��F�������������}����0�8r��3
R������s�t<��������42���U|c��j�����D�n���'��s���+����t�z]���'M �B@! ���J ��r�����C*�y���u�u�ig��a���k�
��&���O��
��op����W�������W�Y��Q�Um����D�>-kX�@X����=rB�w�\1�0R�G��B��m� ����8����K:m������B@! :��{�c����tZ�@X����N{4&�";p��5
���^D����>uAl?�_G�x'i|;A t�G8��N���l��*E�<1�P[��_����C��������G�'��c�H�_M���I?��U8W�������s���v����6��KM��py��M��K�
?[G�/�Q��{�XBU���F�9�@F?�w���R�Y	����k8�U�<&����U�A��������cN��c
�Vp
�5I�cR��
"vS����KX�l�q���s.H+{	m^�k�Y�������
��O>Dl��o]�0I ����]"�+�7W��������n��K|K;��3����@���'[	! ��B��n9+��Fd6U����z�d^�K���������"�`������JK	M?���/7	����^Z���0�9.����&'! ��B��E��BeQ<��'�@��K����]�C>�S��i&u����"e-
����c-�i�v��O����(��x>������q��i�@h.��+=vu����3���E <6�Ou
[
������_����0t���9���\���0���EM�{��R�=!�M<���"�� ���rGS<Fs����QUL��������X;��u�9�blyG��5uR�s(G��$���	�����Ga{�~��;�H�a�4��g(��Bd�&<#��:�{��"o��p�-8�2m���a��Ry+���@���m��������k�c�v�9��Mn�&�p9����8�`�c��6n��A85��$�FA��
��>g�s6�|��Xe�(_
h��}��9�D��5�MSj�kpz�a��W8{!dec��P]����**�����]�������Z����9d�������4_���[����}��6��������/�%+�����?))����c����:c������X��wo�/�F���%��s���5#���:2�8��o��d���XY=;�C{������"��W{�G�_��Hi��M����e��v^�:H�j5�7��������26�U�=�=3c2��������~@h�l�_��Q t_��GK,�o1�9��Mp�����>�/��x�������\N��>D�6���;�>�*+�*�@2�� �����'$N�(qB�S���$^I�8p2��&�pZ�l�*�f���N{��$���5E|~X���=���N�S_�MR�M�Og�
���g��/nXU%(�<>���G�y���(��0��M�O������+qFS�6:-���c�������9�G�@�������o�G�,��{�)��'8C����:w8�)."_k�M����8��iF �h�|X��(����ny�N��+�n� L�R��Ul�	���
��p6/J�������M�������mM1���~�@8���G���,J��6_�dE! ��B@tc'�@�X�PR�v��'�&��B@����q;O��O=��09�������S�&
�n"_msiY l���m3��(�F�P�� l��#� l���f��������!(�J����N�8p�	�� �q{��[�>��Y���s�$t<��P���#����� !�
k�O��a-]Dh��VR���P�zA6B@! �����O a��l"��B@��
q����������k�~���k�SZ����:��^8.�|���4jF����C��\}���AX��4�V�p	�~7��)9@�`�~Qa�;���kTa�a���U(MC��o|���Qs&al�G��5����*P��{�H���~3�s���� E���b�����]���p���]JJ�w��xu�__�>��{�t���j�B_35�#��A L���ay��#pl��������,)�<�1���V����V��Fk^���(8D�b=)���Y�,fq$
��h
BVY��M�� ���r*-��=��3\����V�0+��TvT�A�h�?Mo�9nn���
5�Ph�aC���k~����0�8r���T�x���P�0�h�e:���p/��gd�����>��Z��N'��������)�! ��B@��#�yu7�Ax���h�w9��7��9���a�cS
��D�6���'	r�^�*�D2}�� IDAT���8}Lh�%�6����*-! ��B@�D��h���1'�)�X�2[
B����q(����E/�
��w�[
�k	�"���Q4�?���?�[���JQ.x���� �2v����J:�P��0�$\^G���H�A8������f"����'���G;��7P>I1�YvHq��P��R����d3! ��B@�6a[�u8����=KVB@! �@�$ Bq�{�8�v���P�� ��a��fB@! ��m& ��
�� ls����B@!��	��P�"~zT�� a��8;N6B@! ��h3q�U a�;��(��B@�nL@�� ����� �8;8��A�Ap��B@! �@�	�����8���dE! ��B@tc� ��������hY����$p�y�e:pg��yp�K���\L���H�F����y�$��A\}���r��&�������_O�xO��P��$9��}�FU1����������x�Sk�:��S1�|�#���:)��
T��^�?��{�����+1�~��z�w����AOs��y���-Dvn�3r>���k/��kh��s�,�`�*4?8k��]FJ�w��x��/G��=|�����?����1B����E��L�~Y-��7����8���e���������86Ta��=�}�`�c�m�`+^�xg+is�D��$J$��QP@�b)���Y�,fq$
������JQ��4"�=H���x�Y��r*-��=��3\��������WBVV8����>���X��@stJ���K��l�C�����oH���O��f�������p�?aiY��5��}���C������Bv�]��������{?���h�{�i:����������t�2���m���3r.z����W\���l�z]���I����#w�x�42���e|���' �M�g7����������3�?:��)	��������s��������+�7��M82F��I�VYVy���h78/���~�����Y��M7�2d<�x���@@��������RjV]O��G�B���]�Q�����^@����>uA�����.�I��F�4�t�G8?�,������G�����������O=�1�������y���1��.���sF��!�4��_��d?Z���/��
���9��� ���N��b����G�,��{�XBU���F�9�@F?B����:w8�)n"_m~������v��`�����0���F�2Z=Vp5�u*(]�����D��sad�8�Xc����wO_3�yD���3ZFj3�wq��YRd_�������(8D�b�M ������2��W/��^ �o��5	������a�Klao��I��W�+�������B@! ���& Bq���B@! ��8y��P���l��\�l�������4� <z��~#�3���A�@��q8�A�X���0��<��':�P������X�-;!�sE+�g)�����p.���3�����������a�Pv���-����������h�&�Us
1j�Qc4h���]\@E6Ded���a������{z����ga���<>8��N��;K�����7M=������O�����
�U�*��.a�@X���p��paM��� 	�/&q.���5�(��?���H�*�r����$���-�82�_��Ax��w��a��'���_W�>�?d|�� �C��r��I@�GK'��p.��6I�����q��v?=��P������{C]����1r�oI����I�}�}N��/�;��ax�q�~�,�_�Ax�� <�Ax�����P�o��~�8�A�D�^�M'�	! ��B@4��8�A���"	��B@!p�8k��#��:�Co"T���oU���a��M�AX�Axo\�����b�F������A����e���vX������C�1�q1�Q��A8&
8��v�3�1�u� $��VG�K�1�Z+a�A�b���1@�=.ah��Kj�����j��w�b��uDc��h(��P��F���b�".ah�����U[����A��������_)��A8&�����&�w���9L�u\����Ec�*������O��A8�����5Db��)�{X�s�����:1w�
q1�13���>�&L���K��}��-��,�S�'����)���@���'��0�'�pt5�HBf��q1q�#��E�u� �
) �GY?��Q�����7?����%�K�W�y@���#_��#!%3P�p���0�>�� ��� � \��1#���_������{��q�� a)����b����A���r�� ���Q����1���AX�����/�����P�� l�4V3�8�NNB@! ��h0q�����E
! ��B�$  Bq�wc�Axtu\�Do������M�8+Q����uS���[3a)�jAI�wC����.��o��v�%���%�.�Bp��������Rz7�-�A�����`���A��B���������
�� �V����u����M��1�Xl���]�T�]�n�?�Mu��)<#��0R���M��oI~��D.����g:���J��~+�;���<pr:c��i���u;�u8{�?��,�����We�y@��d�oh����	�I}�.a�{����.f��S�AX7���p(j���o���uK�^�� aExq6��&��B@!�`� a�;�$B@! ��I@@�� ���"�T���9��[l���:aj4��oS\����P������po	.��kE�h2���/]��1�U�G�Y��)���|�)�^n�F\�d���O��P8���0;O!��3l�z�f�Ry�c�.P.��u]�������P>�����]�>��oI��A8�V�����A�6FAO0
��PB;fQ���������U����������`����A����uPZwD��������$G6�"zok���=���s��7~���D1�X�:F������lu�o�oF��{j8y��5��.����82�_�
���# g! ��B@u���D��������gZM2�x��
���}]{}��l���q��^XH`�x���w@kT�g8����zo����5~q#�Sk�[��\,�Y8��bF�X��D l4l9A! ��'0�#���o@+�J��;P��?m���u�"�Db:������X^'\�C�a�&q����Z!}�t���c^Non_������
�\��!������f�XtcC$]`��3q�@��Z������������^�s�i���;��,]���y�W`��	��a���U�<d��#nRB[�@h�����*�O��e��5��>�,%e�����<��GC ������:����BOT DBl�{�m1�
�I��wr$��?��>X����}�R��L�;b7K�������)��`�Q�4+�IB@! ��hA�B5�

��}�%�wF��kq�����nw`�3�7�s�VgOF�ty�@����o�aEcm�5�C��@�h�V! ��� S����������T�A�+j��g}������[ <�����xO�'u�l���]-�}�r���H�9��O�_ �Q�Q��wi����=��r���@X/�c�@��*~����/�7JJ�a)e�W~@��?A�:�=����6(������>�G�A�_��������N��FX�1�������,�s��n~�z�D�c;o����B@!p��y/�k��8PI��W��w7�������8�&�LP���>��/�~�:��3�3z������KB9���=��s/!x�������(O�2{	��[�W�r:�!v��"�KL! ��B��&p�!P�m��� \��S�����l=�4����G�O�cjA�.q�~F�v�g��N�0���=�1�ys�z��}Q����M�m,�a�~�/�B��U\��\i�@hq3�5O��^��v�UmX;O����a���-ol+EE��Hp���*X��! <�����A�C��~ �CY�z@������d= �Y�z@������d= �Y�z��Y�0BE`n[	j)hmP\v,�<i<�Z�������J�u%����3%�Y���~��?F��@�N�f?�S/�������r,��(��`9W��C1��~��eC<3w/���rP���bW�����+��(����5�e|�����Q2�����(��`��`���R��+�5����,���X���`�:��o1�|A
������
�O�����nv#��`���am�:�fT�����7�c��r^_��b�J������0�l��N,���l���O �%���M�N9�rQJ�P[wD_�������vC���f�.�Q��s��6�,����]0�����f��|��+�.�C������U��}���P������9��
������^Q��v�4SB-�m������9\���C���cu[�s�f�&bt�a����o�$��Kyv+R�����C��c0L�82���{V�z�0{N�t�����{����h�2�,,��7�
f�n�����Q��+��Q8�u���J?zK�L�ZJn������[�i��%�B@! ��	J �j�U�|�K��*�((#z�L��������|��L����K�5���X�]��+���&�`�sa�6�V)��U1�#�*_,�s�����_�[[af�AP����E�l�ol���|#�_L�(8���a����b��Q�o��~���B@! ���N���7	���Sw5
�Eo��F��'�G�0
L_h5Ip�jPR��6�@����rqu��#����2V>��5����,��D5����y���m!��������(7�a��T��	c���K�������:�\pGv��J��[�^Z��}�}�|�y��W;����)��o+V{Zn	fy
�7�QJ�1��O�|V��8vm��\��C0����q�Ek���z��	^x�'�_������0T��1��A���0���b
�5u�i��h&	���8\?>��Bc�����r0���,���V(�C'\#�����w��1�����5�������Q�����
�[/$�b$�=�N)�r�#PRs��[q���	���_��-���	�������F��Y�Rf�v�h�����Fp��@_1���X���L]���]���T�����;p����C��X?z��/�MsI��Y�]F�'Bu�BT�����a�PY�,e���=�<��|��9
����qu<%�p�,�,�E�:+,���80�/d\4��m����Y�$ug*A�`,X��?���-��9�����tL��(={�B�l����yo�,'���h�=Q�
�%;�a2���]��9M���(�T�={���C������
\��!*F�g_!f��o;���A�rIT �*����`�A,m��������FD��B�o�W��F�����P�fyu��G��������-`��[���A ���@.�W�H������o�����D��u��X���q�Y�c��|�o�+?��_s1������Z��iP�����q��c�+x�Al� =0e�V�����):�I���B@!pjP���:�a�����`}B��.��s1����x������=#i=q
���C�7��c4��btm�%�k��K��,��Yq��c�<��0������'�"8�U�����bl�{b���n|�e��(j�����l��?���_��#�#�C���2?�;J�����o��?��8��y�/���t���_�%��:}�[X{]�i�P���P��Xz��b�x�\+���$��o��b���@X>Z�y�Pi!��d;J0�����7����nh{�����c������~�K_��u����^7#,>/�o�@���T�9��k��Q1J4�_m���gx�}*�NcI�N��6�s�{s1.�������k���>N���	E����'��]�]9��?^�c����������8�D��g�R����Y��54�@�B��N
-�|'�������oB��B;�sG5Z ,�.�[���������aip;���8�^�{���6] ,�,<P[ �z.������p��{'$K��T&��Xv�!wJ�0�>u
����$a��[W���a�]p�������;8�K��%����P �����gY����{0�x���q�
���(�@K^C �n���T4��y0y��
n��w7��>�k"EB@! O@�����&?������iL���M�@��q���Kq
���������%������wn�p7�����������"���B@!�	���OM"��3mc�A+�C��h%�[�8����El[�	,��.������>��GAu��n���+8��Gj�o����s��)������x���""��5����a��|�i��c�$��g��� ���
�3������yE��/����7�����G���o4������h{vRa�a�./�� � �R2[�����X��8�R��Ll������8���v�s����W?���P����:wF��Q#��|��A��G_��������K�#��4o�'?��� ��AT=%� ,�����8�������V��~��I��x~��A�T�������|2&M���]��&s�x���X��+�C{��<I��=��8�����	�����Bvfd��(�����0�
�����q�dp8�����z7�A8Ze��g�!�R/j�l���Q�L�teB��A�E� K������k}����9��L���5!AO��
��W�S?22�8�%�����)�?�X�>�R�3�Z��1���x�y8�S�^��I�3�����p�!'��#@�?����4��v,����Mf���Yv�7���?����3[�'ac�Q�! ��8�(K��|5��OT7��4���s4�����c\N�C�2��}����6������xL Y��+� ���5��+�A(Jh�$cs���>��B��"l�{a�]�n��s�z����}�~/'����*�O��H?����2T�1�p���*�#�:*�A�
�s��b��$�CP�*�������i�,���1�zc���m�p��1����w�eC��X>��:|
�[~	n�+�R��c���x���J:���`����0���8z�a���w����0����H����F>�l�cI��Vy�����r�����e��Be��������@
	'������ �w��z��Qq�����X�AXS �?\��r�-�t����e�K���-FOQaE%������!#�QB�������s"����~�0����p�W�D �x�v���A�$�B@!p�h�@x����(���D�'RN! ��B@G� ���h�'��p��� :�\�i�0��u�G�>{3��p��Y�@(��1���)�j�P�� l�� <�S�qs�z������`��! ��h�'��P�Tn��$�%����v�O�e2���"���5��P����8����8k� a���$�Ax�;�����g���Ae��.���}@�*/�f���:�G�/�xg�I�����{C�.��|�1/��B�A6�������{t�{���]��G�;��d��qv��`!;�U�z	,�5��4��"�_������I��<|��^�B7���hk�'&�"t������]u�!vn�fclF������L�T��q%^�f��.��:{���� IDAT�ev��K�t������`�,_���+��l����1����P�D����~��Ag����d��$�St�TnJ��5�\��W����<���K��<��x�K�����-\3����T�������M�''	! ��8�	4N �I�!��B@! ��hA�A��8�{�� <��3� <�i��-��F�t���S�*��?t�5�M���}y����.���1��B����9��m8]���7�P��@b�t�:wa����.^n������c�R,�:`���q)}������A���	b&�bt
Xt�6o�=c
�W���9�����p>����d/��%����SC1P6>�$���� L!#7�}J��-F��������I� ��'���(6ku}��};�b2���]��9M����;c�l��Px��w\����mb��b�zP����A�
����m�bV�4
m���"�J\i��9\q1��@XW��+(��y�������b�cK����� ����8����A8%��h��{V�8��j1�'"����7b��"X�yX��wn���Y�*>_��\|q�����^2����z���1�9Xr������:����y���^;���������D �V��o���\\���i����#	�j�3�@���R%��_8��k9����1�f�������&5D������'g! ��8(K�`��;iUk� m1:�������b��%��-F?��6�Cb��b1����������56�&8�U���bB/*���'��
���X�^��6�z�T�RG! ��B@�4��o�;�:�8EM�`���d�b�D��6�^�b>E!��BT{6��1� ��oJ\����U1[Z����q1C[�>����1�Soak� ��U[�>^���c��RK\�q���btQ8���AX����Y��5E�5~��		�A(Bq6a��N9)��W����n6����K;G�a�B���8�����`�������3�s�Y>�yl�������
e�{5��/up�������0L6.�1q���O8�V�0��{Vx�/�I�]��]B�	����ofi��5����t�5���i�5�>�cw����#���������_���H��i�9yxB��:O>�a�;�r0����Nad�D'������g��]/�Q��
+�	��/j�_�����T�?3������'�pk*�����mA���p�<pt��	 �! ��'1q���+UB@! ��	N@�� ���� <�Z�5b��P�
p'��p�r��m����pk��Y��u�9�"�#y�����iU��hV{��2��
����$w�C�d�� �6il,���:[KC��5�Q�@h0��J~��Hx4x��Jn�p�=�FlxW��-F����\\R�����Vp�G��-6�o1�v4��Rs��:�������3�����A��Cc�Z��P��u�),97��n�@�v?m�`�5)<5A�C>�m�`��P! ��)A@��D3K%��B@!pBa(�8�:�8��0�8�A��1v2:#���w�U��q��#y$�����7����(,�>��/���vJ�S �
p�.� ~`���������f�k����C���qa2���9z�in
.Nf�O �
������	�i
]8>�bcB���P����A&��2�}���h�a��$�B@�S��8O����
! ��B��" Bq��Xq��+BqJ�&����AxlB����W�Z��z3����)d�����@�S�.�oMz���L�;�%h��Qa���{�,���z���M:{P{������k5���$�B@�S��8O����
! ��B�# Bq�%���Q�0���F�������W�}l�W4?��7��A
�`�O�Y�%�P��N�F�I9�� �cF�_�kfk%J�������~}9���Y1�D�!v��o�z�J����W��A�0rr��]��:}�(�5+P�l���u�Zp�����+���K���i�k����Gn�;h�:O~k�A����z
����0w.f��c�t������^�j��#L�@Y�,e���=�<��|��9���Opu<%�p�,��l|����'c��?�U��2��'�X�1���>�.�_(���"�JFn:��j��=��=��v>}���I��|��^���D����5
�b�X�m��0�
�����q�p�1
P��{vc��.�7����+p��h
nm�}�0K��}�1��F�r	��(�J1;f,���&E��������B')S/�b��H�VT�=��:���AF�/��_�.�0�K�U/R��w��/A��N������1����i�:��9�o��G�`���1/���}����}w�������F�*x�!l� =0e�W��Q�-�qa%���[�&pF���0�9ccM�`K"f%��M����.�=eg7�.��b!6��Y��������}���G� 4x���<H���[N ���/[���b��s�&l�`��J~���n�h�a�u�H! ��IA�q���FP���q=8���P��5��v�x�Sn������Ne�<���2*���c�<���Y�y���������w��*�W��((D/*�����
���X�^]�����-����O�OK~>$�S�������V�����������/�����B���^�a>E!�}��x6�c��0=�J��+Iu���z�m���
����G���Q��(���1�B�����q���z����
A7`�|��)Xo�%��T�����u~���GU2�/]�m�J�/��?����E��qP�b�}o�3s�xW�"}��?[�,�5�%�z�V��s��w��=�-�0�*�>���,PB������0������<�W�����G1f�A���@5
��
��8��
0��~Vw���O\�9���P���A2�u0-��X�W����3<u��mB.4@3�sPg��&#��1�'��dfs��36���=�4�Y`��[��
��~�Ms���2�4�P��^���`�eNk�K4���c�u��K��p���	����1�!������2Ce7����ofi�����_������b6.�0�M�����B����F��+�dS�N?�`��*����]}b�>��3��'~����K���/L�u��+<������@�����2���+�.�����y��x�G�
���r�B@����8O���z	! ��B��' Bq��b�Axt�t����Bu9�����X��8�Q���8G���$q�]���0�������;�Puw�BT����EQ�pVT ��A8� c��A8-�g�d��z�l��$qb�z�8�,8��A����8a]��!#=���7��I�S1�<[k6h��
�y�^a���y����`��}Vy�7�O� ��g���gi�'��b�������
�fa���@���������
�c���Kt��Vc'�a2N6�����!�k�8�!~�'��a�������NU����u�0|����&n;�.�{��+��&>=�w���
G�
���^! ���M�e8#��v���5 ���~��CI��_�G�h���c=R�ys\�W���W��d>��P�'���n� ud�X*6�O� ,���I��y}Q�%l������~�����A(�s��� �8�����tFY����E��7�Ge��6ng��n��WC��,�����������m��!6���2�}�"�d�?�Y��:_�� +3&9��������a��vPc����+Ce��^�n%bL3��N��%���t�1m��kY�r�5�H,����.�s�5f�0Y'
�s�4c��r��c�0tI�d�R?�-�h�a?��5cm�-q�Jwi<�Z \��NV.g��	0�f3k��&�?����[K#}��sm�[WQs�Q�d���|���B�>V.�hg�5�v���i�&5��$��B��% ���i�bB@! ��8�	��P���X�GwH&&�E���}q65�����A�kU}�!q������0>�H�5_Dc���x�a1�0�}�AXo#�m�@�@�AXWBPW��u� �B��K���A�b�11{��<�A�BFnF#cF��H�������1'���A���'� ��A8ZeE������1�c�2�1K0;���1��4N���!X����~|[���6��uX�
��W����^�Ko���b��A�%���b���D��n��D���[! ��8�	4^ l����w�A8���W������:c�G7�FcZ�8~�_#����%�2�/H�H���)���O��������
}>,�7��j"�1�5�u� ��(jc���9�c���f�aas� tP�ri�����~����\r��u��#�!(J(������%Bq������Nfa���������C>��v���P�>9��B@!pbh�@8�������x:�.G-��h�i��N�K�i5�U�|�m0�T�4	1�0���J��(�+|��y7���b������z��\������P���.�� ��B@!p�	��P��]P�Gw@�� L��H]����\��G� ����G� L^�HB
#�.�h���&q���u��A8=p0��PA]� ���<��|�(:S��M?�:lC�M� L���;��P�}��8K1;f%q6�?��#�9�-�A��)� ,����>�5pN�
l�N�p��)<:(~�ft�"[�&��W;�Xl�#���*��B@�ZF������ ���}U���M� �	��on�����I�H���K��e8:B�CI�-��lk����u�u��c1�$��~���O���8�A(�&>a�������>^�o�c�]6���#T'B���B@! �����8��! ��B@�T� a|����"�P�0�r	��m�q+��Ec��~���k��v`�1������HB�A1ue����m��{FU�GA��]8g��S�w��������B�y����&���3��'���0���6�?n���:P�Dc^���'��\����}����?����G���:l8�-�F@�0
��� |��E3r0.�;F�!wU�9�8�W��E(a���� ���m��u6J�P�V�P��y�J*B$aEe�}�>�uF����~��

�d^�"�kG�����cK��������9�_����c���fg�`���1/���}����}7��.���[��1��o��p�Mb��UrB@! �@�$������^		y�N�[�<k�Q1o#�{F��������W��$|���-FMNNB@! ���E����VR�^A�k��gc�j� l�L� �2��.����3��/��GA1��w���g�g���j��s�t�����}�w��XNgq �}�R0�J�0�C�ui�v�X�q8���;�w
�-[U�������`��H=��}���9F�� _0�!a�7)��-T ,�����7�q���K��~X�{�/q�&���.N�L�'n4�T%��=g$K���E t��������wc,\.'�{�C�v��5�`��3%���{0�vo�
��^87dRd��������>�,��r! ��B@�L�K��S�����:��mP��Ix�dT���ga�tV�ay�����!"6��� ��B@�S������z��?�^�w��mI;A��������z�EXg������������>�5tYC;`zT�_|�����n�<o��X�?"��[D |���f
�O�"0�+�_W�|�?�t�������}���j�/�����\1��I/�����y����;�	D l�a������z�?P[u�!V�K���e2n�5%��kn����1.���x�s�m?$�g���@�@ �_|
��?c��������n��P�����A7F�Q��3���,<���L{	�g�Px�I�r�h�m�`X
�vtg�]! ��B������������j������b�RU���P���/�b<k��a�+o���kX�~�SRo��������V����-a��$�B@! NmG,�ZIv�;|������y���g���zz��a���~�5TXbM���P']������������s�}�G��d\x5���
�����������������l��BV�A��c���+{qj�k�[(;v���w���u��X���H�q�C{�������1�w��KI=�)���$�z���y�+EE��� �UGUE�;B@xH���!����2��@�������d= �Y�z@������d= �Y�z@�������8q���%��:�0��1HG9�z~^�����K��u�}~�����E�b��3���=f0A�,���AUL�3���t��h����8u����zo����yo`���s���{:,��c�6C@�R�����S����^���5tb���@p-vu��.��?C��s���k��t�� \t>���b���\��)X{��%��k(�`u����1�
��j����jX1����X��fG��T%���b�����f�V��1(�
m�w�l�����?���|�G���K�E;
�Q�o��6�TAm�������������T��t�e��N;��4l��`@����B�;|��Pl���p���6�Q����%�m` �������21l�����B9��z&���1�����M(]zc�����'�scu���A��X�ZD��lI�V
<(���o������SQ�pG�G�.T�*���h���l�
[�bKj��5�V�F�����b�/����_z:��3��9��,������0�*��w����i=P���q�1���B@! ����:�r�7��V������o���W���fv��{j�Jt�%m��T���u����Jp���l|y_b��am���~,m���r)�3b������t�{`�f�c{@~���K�~(��0Q*va�;�������B@! ��q&P�����t�b�9#\��/_�����9(?�D�Y������������e�{��9�XQ�N���S�yp;J�a����/on��b���m����{(
�M�l�L����N�q�</�����	���1n0���������`����k�.h�r�B���X��{F��y����������R1�l��a����l����p7X(�@F��s���>�>�z]�����)�1�]��P�0�*�?�������a���C��3n9���	� T|��
[V��v����LZ��y�Zk2Y��UA���Q3waf�����z%��R�������mo���'P������N?[����oB0,N ����`^h��N�=�G�g���+�7����}�Q*�X*�C\X �h����qeO�kY_pe���_��*y����)��/�W�H���(�W��}���(;��O0��g�R��W��(b��
��!���QZO�����cN�����yi'F�N����jm1��]���jG�o����j��Nk9��W��_L���^2~�3��C��e�<<=����at���5��j����b�����j��X^��Pi5p������X�r�����������)���
�X��8�=��}-�k�����@�x1�9E���s0(1��H�x	��_������r��p����({-�	�m�/�R�m��w�U��[t,i��8�w5���E-� �}���V�6����\�)�7�O%�O��~�C<�B�<$�� IDAT���3p�87,�JP���@��� e,JZR�v�l�GcG��*��B@! ���C�q��:c{y��?AS�Q�������q�x�����-%s8JE�^�t�`��&FV�8���|����9X�&�a=���){�i��m���y���gKp�dl/���G��/��������a����������#���C�_d<�x�1��d5�/�CxT��Es�/����u�r��:���T~�4���"u��QZu���;���������7���M,�4\�g���3�!!�U���zF�9w�}<���G=������l�gn�!,��]S�X�Fe��w���*�y����s�d������	�I��I�����L���c�|wY����7��\�;����cy}��"p��4��~�u� |�
��{�����	�g)]0�}'�Nv���g���w�2a<�O.%c��n�u�j����]5�V��L��Uk'���:Y��'��0�@��r-3�@�����:�4���8�p3����wV��b?��$a�p��[ �
������]���#�R'�D�z���0i"��7E������0&n��hp�@��w�����������=������j1����@8l*e��8�0� �	�~����������Y����D �NG�����q�:V�	������!�� ��
���8�pI�]P����G�9���
5%�@8�CyW�	�"��`���Iq-�4!wrG��L8�*�m��hu���KQ��ZaZ�M���w��B�%�{z�	����#C����ZG������������F�(��?�u�����{pO���Y�B@�P����K��{�|��~�w��F�����QN��9��4N |#+����|��F������8�pZ-!y_�	��.�� ��B@!p	.<94|u������e�&�^����F���8Rc����*,(E��Q-���wT	��6�����n0e/�_��u����+#���H�g��p��w��,�:���A���m�N�K��]?��[���QO����K���$����g��	�N�++!S�3���(�����-_8�e����,��'b���sgr=�V��Jz�u���
a�(7>Q���Jym��*?�W�k��p'f�q�3(~��8a?��q����t�s��N�*GX]�F��99q��0���s�B���9�x��q��r��x���a��'p*;j8���~��y��Q�|L�5�A�9F���Ah%�ja����d�T�&q/��!p`Z�A��~�z������e���__� ���g6�����6�d��q�H}�j��X^�}�������p�+��m��J�Y�bi��[� l!�h���Hk���Ax�����?�A�����ja�����f����R�I�����;q�>���������,�Dy[/<�mq�I����9���w7f�t�i=`�|���d=�8>T�1���0�	|���F]2�s/�-�A�u�c$�V=
�������@�vO6fZH?9�=�\�8�e-��R! �"�k�a{�����?4�hW���9l*�~��9-���8����_
s>Hp��8a)��}q���~�A8Tg�#����;Q����"���2�< ����������p
�k�aa}�R���?��:����z�@oZ+�pa	�7��9[V����CP���o��Rr�|=���{�q��/��>�m�v�3	~����P�T�f���z�
��c-U�o��isu:�}���x=uN�3��L4#����cy��Q���W����>�I}}9��>(%��?�}����=#�z1� l�^���v��O� ��<a���8���8�j8���'��|BZ%� p�����Z D���u:k�Mw�o��A��
�v:K�����?�{w�@X�-�8>T?R��H�o�]<|���Q�N����"�8-y����T��h��l�.aS@�B@! ��)D��@x���	��P�(q����a�	� ��k�o�.��	����#�S#��]�����[�Gw����:�8�A��7��Axt'�c��<T?����b���w,�u�\���m1*c��p7o>�.��SrB��@��� �7��{Kx	�S��~��r�H{H{�}�����A(���&��;���P��:�f����Q����������v����3���]�u�t%,�-d<
�\\��7��Wa�:�-L�����;%��ZdY������-��o�=�4�6��
���0�C�
�����wO������?�[[�����6X�"<��|	y��B�;n�%�k���l�Jp�6�
�gc:�[��MU;$���t5<�<|�(R�W�7���k^%�5���xvO�Y/�+o��h�<~/t�6��5������a��X���
7�7�3v/�g_�����C�������aX�Wxq>l)�����������N!���}�mN�k���a(��?o�1�(x���6����[��s"���'����_2�2�F�����������:��S����2f�2�Ce��	<�wx%4�D�z�%p�hm����sP%�})6���E?�{~
���O� w��xf-.�-OU�2����yu������^y9Z�P��9<~}�z6d.3���]C�!�@�#�����%�B@! Ni� a�������{���]	�bf���2i=��m�k��pd8�FpWi#/C�����S�Q�
��M�0K��U1;Q�����<��P��hF
����}�Q*�X*#Q���)x�eXJ��'�@�bt~���P��%b1�����[�~��z*���qZl	b���m��
� ��b�8I�|����� �=]5���P��b>�YTZ
��B1�b��Nf����B�Y����A������L�����"8s�vK�!�X��KR[ ��A��	b:0�]i���KA*<�b�jZg�bj��!�?
v5!��`y�-F�����>�?���	�o�7E���y�eo�@G�����]O��	B���zB�w�����z�e��\�'*(V�;n4,�2.����N�!�����\�[7L lh=���	�I���3$v�:������Tu��_��;�&����K �����&�p�����*hW�����O��&�`��?���=���[�V�Q���h������F���
�U1��������o��@��q��o0�����������7�����������E�P��m�@��6ma\,K0���<>����(�@�\N-ac���B���M��bq3� l���r�`m;���
�{����gpO�)v����?�!�{~�)����e����h?
���~��v���i#�.I��B@! �-�|�d�e��2�G���b�*�P,K����/^�����i��� ��X�O�-F�+a�����u� ��^loA1��c"��'�f�A���T	�g�zvT tc�<8�@h��=�F+K��@x�a�q��:�
�����D�@-�(�WR9|I�-OT��������{���]��v��;~Z�A��M�_��!�K��c0>�{�����g�]���|�������}#��90�.�$�rIt��+���I0�����Q�	�f����G�_u��l��s���|f]M���zc�������W�5f����pyT�������q��[u
�����#?��M<1#"�.�
#�R�d�i���p�ppZcmr?�(�>����V[X�1T�G��X���\-�?�&��o/�$�}V��oz82��y�����;>�*���{���P��w�HS��X������������emk]��UV�e���P,�(RDH�CB
��2���s�$7�d2�p�4s���|�s�-����	��I!a�����C.�RX�*\�R����O��c�����lo���@n����������m�S�@�/W�U��h���
So����T���9�!�`t�w1}bY�6������G��	o��g��k���3�����=�
�_�����'�
��O�9<�j�����r�v:��,&`��  ��	A@A@��E@8���kB <�3O8E Be})���tV8,����0�b��J�b��7,��o�Im��>�#LhU���V1#t����<_�{p���v{��W]MA������02�R ���
������0�+��8�f��7Xr�E;b�g}����{	������V�P�}��"�������~Bl���2��u��j�FSX6��o����T�������
����?*#wB�=�z���-�W;�Y��0?!�7ZD�P���0���c���3��#n�9�`�j(,�8���QGB �e^�c��%�������l}�W��f
���k���
��~!y�eK���1�cx���-�u�v�e# #L���O[�m��q����!�����  ��A@A@��E@8#�p�x=��#��"
�����{~�t��@����6h��k$N{o��a W�b���#�[��n1*������_�wo]]q������,{[����tr�����1��\��0"DE�0��E�H���@�S�����B�.$pEoJa�����Zi9g$7T,�����0�OA1���k,aC�.�Q�������d�U 	��$*�0�s#
J0"qi(���'��W�����p��p����V�H���k�V������0�y:��3f��,�Am�Mz���+b�'e���0�5�B#�e�Y��1��  ���A@A@��E@8���k�Axdg�p6�@(��AO��c<a�E��G-����V�e|,BB����������39���Y��R��r-��H������9�@�EYeNYD?��p������{��:�[�F+0�2g�	���k�xs��o��O+ 32w��E�GoV�6�e��ac�e��eA@�N@8EL��  ��  -�A(�B <Z�
�����P#��=��G�{�����+�[��Ii�|(����~�J��9)��x%��*!i�X�f�0Zv��Td�=�d-��T�G�E��&���J"�2{�v�6"���(���5'$�����p<#L�L�R;Ji+?g�Ce��(����)�L����HZ<�K��s����+�K��l��a��nGRh;�axl�z�������*�����C+����b-����;����xm'z3���O ������g��w���2`��B���}���/E�m�*�"����.��CA�W�C?�n@�y���A�����ra�����e�.��[�9��V������@R3t�}��00���4����������}���?"u�����R9��P�u4o�2r�
-������9U�w� ���[�@?T����{��������0a ,�6W������	���b����|h+�9[*����$F�7�K����An�@�8e?��t�����7�����
���hC�p���:��V����� �^�eK���������9�E�����r
�PfQ���{�����5�-F��n����<��.���p�0[�6T �mk���b,�:�h�b���2����3��w���$�
;���� ��ZV[{ba&�
�� P����Q������<4_���5p	�^W#5;��l
�+����l�:dD��Wq������v�ukqt�F��/�
8���o��0��C����u��V�;i7��K������|3�s��������4P_����#�C��>�����_m��E������5>5�w��������u�����d�������l���L"��0�6����R���$+�=���++@_�5��k�:���7��g��
T
�������_��g�����KvyZW�|;����V(�9=
}�����M���a��O���
���$y��������y�~.���&��%h��#l����H>c|5]���d�\�:�(r��������p
�E LE*�~R��K�e~���I�� ���dB��������Ypk��HQ�>�
���,9C/�G��_�\a�"��#L?/��X^����/����A�FlV����0�-�4����u���������\~�=0�Sx�y��
W���hh}'�������������5S(:�'������A�?��I0��C�x��S�8D��X�d�m�X�@X�_�Ai���.�����
aQ	����e�������uB��\�\�.�|!�bn~?�p�l���?����]P�����Oh���o�����o;��<<�X��%����
�m`������c�b����+���`��:[s���5l���0gL�5�G;�������`xF��h~
���`�k����Fke�X���o���a��0}"�6�%��G��	o��g�C�Y���-FcZ��hO�����  ��A@A@��E@8C 9�V����8C]��!��~/y����W�=�db�0�-�Fp��������}��H������� ���4�A�$2�@X���GP ��A�A���AX[�h����4�A{|Wu�[�^K�������,��xZ����~{�]��HM���YC�M�WB�����#� ��{V9M�`5\v9dV_���unU��,���0��Z�A���y�6k�p���Etwm����K?�2�a��k9�E`����� ��"���3���Op�=0��JW]�!���8x|u�z��[m[`f�Y
c�'�up��01l~��z9<X�o'C�.�����������6�L������W�u�<Q� �c���#�y)wd?X����b4R���2 bg�B�(����	��>��8F� ��9CB�+��GPw�FpZ���:c���4��+�Q�/�`�+�U�W�^��P���zp�^�� <������ ,D_3/�����0~��z;���tV�����cu~U�?B� �(^��+�#u?�q� v�p<��m
'��W��K��s��0�d��0�u��#��`��p����y�T��yL�f��_�h!l6�
�-��kad������|(�������a�
�v'x������������I�������`G��g}B���{�8<�*�1��&�
���y}���!��i���`�'��sf,�xL�����Q�f�TX�L�s�bZ�����`���;,�������m���VyN5�d�a��,��'��1c�t�N��2�.��G��y0�xy6�����0�O�-F�M�>�oK�uS`��D�3��G���?����7��o��������[���0��������`,Z���Y�9a,k�c

Q^�$ �Q��A@A@L@8���D���Q�)�b��jah��P����a(a+�C)r^�A�#������������`��SLa(a�`���>�0��3���8F���6���s.	� 4�������=/S��CB;��A�A�sj�������� �
� ���9k�Q��sNB��[K�A��(�O�|����HvJ�6H�f���'��P�����9��S�|�Q��9C�6�n|+�K��������
�����s>M\�J�{;p���������e�Q�����9{s��=��������R���M��
#P�@��Z����c&��7�����  �2���s��t�r�
� >��A����_�� |�(�A�A�?��pO0a����e���F�����=�Z�E����x�|?"���b>��P��R��e>�W#m��.<����rJh����(3���M/����Ax�n1Z�}}�A�>_�'5�	�p
a=�����8��f�M��V���m� IDAT��ad=<��|\f��&���W��*�x<�;]K��@(�1�E�A@8V4�@x5R�AH%!���b�#��!�0@��B <��u8��&�[�.�u}H ��3�R��	W�fbY�A@A@h���P8��)�Gv�Zr���n��J^��A��Pr�X�f;1Z������Ax����$E�p�U*�!����u;OCsgZ�Z�x�|!���\��p*���bp:�v��8u����������f[������:]HR�������(�
��2R����P���ui���6h��k$N{o��� �+�����Q��� �O���-�]�'��8Wb<�B�oa`_������q^� ���#~������ ��rw�*���������"� ��{�E� ���k�K0}��]����#�M����&��'����{c���>l���o�L8��[m�"���5cc��O��@�� �{����l��A�u���Uj8�Q��K��xF=lq�m���B�j�����A9�?����'���9R���#G
#���Gb����9����9,��7��_�5���E��o���x������d7��uE��c��+l�����7�ZQ�y�D��BK���7{�@|zK�w����h���'��"�{o�_���;RA����Go ���u�����5�:}|�p
a��X���������7����P.�	C�	JV�@.�eL���8�xpL�{'"�cB ��F��yq^A@� � � ��  �� p��pZcM8���aK��)����&5�>�B�{����d�W�F�����__�{� )��
����A�b���qYt9�5��6�������/�[�]�H�b���CpE�j��Hk?�;�]n�63�9�_�>��}ppr�P�Z�����n� \��e����sQ����'	C��E:Ryi�����>�����P���� l���$��_(.�G�	Rf��X���d��4m�������P�u4oi:}����-w���$�}�vP&gLR�����A�6	�X�� ��~��aC/����� �,��6$v^��5�8ZA@A�� `�u
��w�~������1����1���[��LN ��  ���E��@���?���7ck�����]S����q���l���D\���P�y�$�")
R�7+��s�Cvm�Z��P_�g���6J�d�>YRg{����3c������4��Q�k�~���W�o:��P���6`�y7�1�Q|_Q�����%FM|��7��'*'����0P.�#��9u�-T@��B��zu��������H�y<�@5�za�|S�y= ����p�0ik���p�U��j4�P��#�����d�n���RC T^J�7�(�q�{�=�BM����%4� ��J�G	�8]1	����Ht�AW�9to_��?������C����bD-&t+Ej�R�
q�  ��  ���C��;b���8��O��c(-��h�?c�����E!�LN ��  ���E��@���Uh��Hv;rZ��@���vND�EA>B5��������hJ?���
� ���Qt�L���� �Qs�N�Vo����;�Uoy*�����p�����j�)W�}�?�2����>�����Y����W����b81v-�����F��~���5��'�|���]w	�O_�����	g
��NzG����@�Q3)���Bg]�SZUU ,��4�x�)���F1���&��3�����0�������p���+�S ��.�e�~����-/���g\�pM]BA+%&��V�5I���7�g0�^D�z	x����t`�d������_b{�C��<o��/=��r$jA@A@h:d�
�p�����8^�����a�<��3([�!zy^�����B����O����@X'1Q@A@��@u�P+�K���H��oH��~�P��u������;1� <B�OW����]D��@/�P��Sx�-��h��O�A�U��%v$���C����W'�5����d�7�B���|Zg��_���;=8���~�Ex&?��}�������\r����V��r%����f(,�N�sCjy*��o�~D�y�n�.H7\���9)�n���[��w����{~{!���]������1�������?��~��!��X��(��� �5�q}�Gq}�Gq}�Gq}������?���@���q �����?�����c�Ah��}�����u���1
�����2<g��;�m�z;�G�C��)�9Z6������v�Ci�.w�/���H��$����,U)FIl�$+�k�{s�4
��t��}�-#��C0@o�c��G��C7��l��/�Ks4W���v=��R����wW�
c�J���1�OB�%����1#�'�5F~�K�!�"�n��l-��C��0���(�"��x�eHe�q���_j�n�Ce�%����K0�����eG���C��%�K��8�A�9�����k�E�On���Z���H�������A��`;�3��B�v��7�s�uG9�,�U���������`������� C��1��HIi�f.F-:@��-��.�
���+-��|I���za.���B���!��c>���QZ�#K%�R���A�o/���(}!%� �1���0��@��s����m�go�A��L���w.�
��4�d'J�3Q���a^�����������������	W�|+b_��u_<��1�t<k=H	.F��84c����M���  ��  ��	L@���;.B��(�nD/X��w��������"_�����Bl����)4���8�S�e�gy�w���pj�e����+�������8l�}�:�k�bt��'�#���Q�E:�+��Mx�SP;�1�{p�kn�q����  ��  ���W���k�����h�	e�;��q�OEj�C�"7��x.���cg��-����9�_�����k�X�i=k���q^:�I��
��.���y(�� 8��n�K7`���]��$#��9�J���"�
+0���� �?u�J���P�FJl�������{�NB-�����oUFi��6�l�i�����e)��G�UG��g��z�x�u+7������u�!���h�s�X�i|a���R��������v����
��t�sv7� ������w���+h������[���
���z�D�
z�J~�8������-���h1vz���6�js�E ����C���)m���y\���Bu���\�CQw,AW]�q��m��m@����g����o����d��������9�����F��+�����	m������|�s���JK���.�]���L0t��KQ$���7A�p�"����]���}���zmD-�Lr�����4A�������J���N&�����<K_���R��5
v��~�&�'����15�t��9����,����8'=E�����o;l]6K�}{����>�b|�����F�q��&���q��O�N�����J��q��{tgW������R�V�r�,��w��<�������'���&��&F1�U$������������+������N��K���ZQc����o�����2��
�����v�fvl�N��m
�����v~��>�y��e\L\nk\���h7�R�J��t�����V�nF�0���1�-A@A@8q	HRs���th
���cak����7GN��V^@��h}��o+�fo^E �>�m_��,)���g���=q����v�Y��v��`�J[6����������m�(N�#z?;Z��������8>������@���O1?��$�'�>����q}N^xE����r�xw �x_��P��9�>[IZ����/(��#8�����6�F�W��!l������G++�����������o13�5����SxflC:o�]��/	�w!�gcz?��o��
��CH�6�;WO9��w�V,�8�v�e��bp���[�P��9�_�c���Sl`o�����Y��O�Mz�}����-�BJ��H������p�~�[���]E�O�3���C����C,���5f�S�:��
��?W=�Z��_��"�����p3�6cv�.zq�����m�aPz]��/!�0y�IE�B.W+�
�E��j��Y(#�#���G_�@x��W�u	���-��x�NB"=��)�����u�u�����@xj?�)-a�AX)��s���N���S�]tu
��nM�������3`��
�U\��
����K� �P7}A �y=�8 �@X~�@<����J�O��'����N��~8�@����"J��T����%$dgU��%2Eo�CH��L�	����E����~cS;�<���������G�\�mc��;Wc�]��
c��nQ�  ���A�f;��+@+��C��P:L@N��V��@�)�w��|%��U �c����V��"��tq�  ��  ���#P] ����_Q;O�x[G�}����5�wC�&��[E ��1�����@�T
���^gb8SH ��n��{`w?�2gvl����s������_����UZ!�j���j�f���H�R��\�<P���[;	��h���at\����������R���~���g�b�|PU|�^����"�'��c�`� �������������qO�o�SQR��q���7�,\G��>���R�A��d�Q�t�ad�rP����~�NigqF��6���=n������p�)���<�pM|	�������g��WP�p�)g]F��c���@W,�%�r�B5kEi?�||��x�R���a���y I���/����0�'�k���Q��0����?[@��cq�dq��"[�Ax��-��Q��U�����������{��j�4|�9���]-0�����t�� �b\��\����=j��0�}�S��=�������s�o�M�m�qf�X�{�o[�w��P�s}��7\	��g�t��B ,~������K'����/�@ha7{���P�|Y&�~��uT@�����i�c�����z�US}\�������b=�������v����{�@h�Z�q�4g�F�4V��zA@�P�v8����[��Gp�<�?v�;�����Q��hq�b�Y�Ax�)+F�������
����U�����[����:$��o�
�r���-@:���)���� L��������~i�������l��A�M��a��_���=���K'�'�^7���9�Y�}���G��^�q�1t j�/�yk����7�`�a��"K>��~���|�i�s�g��6�f��~��l���-���:�L��GD��A�^S�{}��T�+����(��=����|=�@pV
�M�AXU |���Ja
���z9k
��n)h������@Xax���9�-��]Iq�!��R��B �Y�~�@�h��T8���R%���J�q��;M��o1*��k��~	��  ���A�����@���9��pGUa
�0���q6X ���a�n��^v���Y�G8����Zs��H�p&
��]��\���@x�zt<�|4]}G�\Mc���bT���1^���  �G �-�p�_2�x	��jZ�
1b<�:,�a�4�u@8���������A���� �C}������W� �(^5p����:Sg�|��An��9��-�x�}�O����mB���^��p���\��O��)
Z�Y)��i��k:~l�;X����P=b����:��Tg���v����)�?A"��j�xs-�v�w��nd�-WU���s:�l0X�8�l�5��id��`a�Z�����Q�M��j�S��*��eg���:�?��mO�*q�u
�I�YY��_!�,RH�N����,����+��'�vV+N<
	E�L�y��E��q2�L��Uj0����}���1��zZ*��;��t���V�u��i���U���AX��W�^g�t�}i�t>Of��
c:U���fh<������Q���0��1��������+e:��>�j�oh��*1Ud0�	�����D�1n�����9S����r������?�����j�^����HA@��!�8�p6�h�ZA@A@8>	aCB� <>g���U�Axd*o�Z[��r��lC|N6q�s)|���`n1�A�4�w�%�����9i1���'Q^�W��Hru�"Bo������%����5j���nX����+~�����9�=�M;�
��������^�+������&�u��iar��]�&���M�1���N�vq�!������IV�-F���z�����)�/&|�J�f?m���.�����;b(�@h�A�O�.j
�]�~jn1�
���������:=�!dWs����[�]{�H9h��Lwh�?f�D��p�wRbJ=r��������V3�|����M��s5��K'7,���g���Wk�
Sv��v^�8�EPP����i��^w��d��3��2��7�<��f�iW(|�\(O`��~��,�h���?(��_U��������*�/6��CH`���1�S$������L���ju:d���0Z�� ���j)Z�@H�Z��6D��r�f�����J�����GZ��	���TE�/������1�B,5�=Y���j���0���~��(�r4��q6<V)�1�0�y}@���5f�[�0��xq�  �@8
CN	����F����9�)����%a��Hq�)��.�]�79p��\c� \�k��0x8�-�E�����S�  ��  �@%��p$E���.\�����]B�����f�b�z��l�[�>�g�6�*[�f�g{L#�Ah�btZ��k� <�|s��� ���b����?���A(����B��0���q��A��6����Y:���X�O����^�\�Y/�L�:=��P��>PvTO�������T�����\�^~�!Nb����It��
�N0�@�O�+��e��7M���1�~��Mw��]����6&��VK���_P?K�����A��%���X"'X6����-�58g��g�K����7����]o����Wu��!2<P96�?��j����lL���������a���=���[���USU�z�����y���:\�p�m2���h��C�5f������`<�r����k8�Y��i��Ia�3�����c�����I��;+]jE�u��a,�c�3^T8'����/�a���pC����5.}���6-P�n�0P.�����v^�~_e���,[���Sd�a����&�/v��rQRA�.
�ngTNohA��/�
�3�	�K��<"(�������q���  �� ��	�pZ�S�Gv�
�?�p
a��xr�^��_!�U�X5��U/T:=K5��� \YVj�r�����N�sJ���j�<�Se���������[�m���� �`��:��[����!����
��+�M������5>�~�2EQX<��4��M�+d�tQF�b4�4�� ��
paE�b��M����v�J�Q�m!���6B��L,��fW��"�1~�,\<��[��7��4�O&A�yV2t��� �%~*���6n���i���A���
�X5_g�R�
�������~��y|D��u`l&����!�SK"�� p�4\ �#7:�fA@A@��Ah��P�Gv^�p
a=���� �)B�.�D�H�*s���"�a��+�U!pEov�q���[�B��6kc�g=�(�@X3�^e��v~i��5�0��,���"���.8�l�# IDAT��
��Y'r<���@<G�Y("�Q��X�'���@���s�x������=B�X��u+��a�$���u�L�zn�m|�r��  �h�@(����A@A@8q	�pZ�_�Gv-B� �/r�g�O�X�X�����# ��vH�cn�:D"����!F���@p���3����P�v&�3�����*SI�����#$Z9!�t�F)0�2��k��E 4+�`�
�e?�������H\;���#��UQJA��h�@(�GntD���  ��  ��P8�@x��rP 4��3���5�����'�����()�P������)|�v��
<�����`x�`��_��5��L����������cKF���/&�b�0����m���t$Y����C���)����y\����p0���h�=�����X�������������?���k�����M���IYS����?
��/�8���������
������u������.�	tg�3����/A-�a_.�	�1���N�vq�a��u����'��p�-��}�f���wH�-w�Y�
��Kq�4(��	����d��]C���z:����gQ�_L����������	�EW��Vg����[�}�E�����)�!������������<�;)��������=�*��+�O�Ei�
�G���e���n���!�Z`^�M�a��E:�&�Ii�8����� L�q��
�Ax/�mk�e��87������
��=���FQ�\I��E���__�
]P��L���i>.}���.�um[�>���k3�jf��V���1�wu����
������U�PO���gh����Y�0����(
��)��zHC�=F�����eK�����37*3�8��1�>Z���-[�V�m��0���0C��' \���bt���#m�}L��Q}jn1�C��x*��3f;j����u��*2x~��T�m[�y'A@h������ZyqK�� �x���A��(�D��C���9�����P�T�9kp���]�H�6���?�����!��m��Q���ssS}��j��[�����Z�}*����'���t���a�[�tp�)7R��'�>[I��������~i�a�!�����?���n������he�{��l�g. ��[�����SxflC�PaG��gz��Q�{��3�1t j�/�yk����7�`�a��"K>��~���|�i�s�g������o�F�=��2�P�I�C��8�O?`Jz�Q�c}�B� �z.k�����\��q:��)�8E�s<P��L}Zc^�ek��X�A'�P�
����A�.�e_8����0�X���qKLx@a�yif;�|�'�*��D��(�x��s��eW(|��LS ����:������
��&���3���S�
�1��AF�������h���%�����~I����y������i�O��N��K�i},N��l1z��6�)�l�Y�
_j<t���q6Ta�	�d��?Wc��
O�+3�=�uB����p&xB�W(����'l<6V"Y��Yoj<?� ��yc���:����
����e�m,3X������,���? ��~�pS�@�6,
�U�n�j���fN�f����u:�g�<��Q�-Fk�3��������e����R>�>�We�.!F3OEA@�4����Ax��H�/��  ���N@8����b��#;�-���B��hZ�o���ts��|=a+�=��0d�����A��S�Z�CP7��� ������ ���A���?Epf�.[�KQ���n)8��@|U:/�����A8U�������C���aO)�� lx|�������fOV����NZ�V(<�u��Z�"�_l�)�L�I�����Q/��6��X����3k�������Re�C`��*��5�y�'��.�~�c�#�E��)x>gq9gaX�i2/~�0�}�|�)t����s5��K'|�=���G���5fW/�!�!�`w}BS��z��D�����@X��������;�*B��X5����B��Hs&�8����5�����xq=��8DA�Q	4\ �� hk���/��A�u�2����������P�w�_7�y!���/�E}�G�9&�� �A���b=����v=�;am�n��L�n�g~cq6��q�;A[��r��Jaz�����~S8��P8����xr���jSgl�������G���}\�BU��_>����uf/�Y��I�3Rf�2��[1:��j�l���s��f[�-s������-35�y�j<���D��e&�A��f��wY���t.=�^���������3+�~�#��	��q�7Cg�����u���:���$��[�g��w+8��|��An0�&�-��t�Y^�D+��m�y�5���1M���#e.+sN��Z=�u�Fg�yn���2��$1����
c��`�4�'��+��x��+}�3����x���Y��:^��\3��*�@�
��x��@<���x���:�\���Ah�Y����=���7������Cs_���c�)�4�sL"�� ��.
a���PA@����p
�5�����N�jah���I�������(�XrLa(������*�K&E�A�H��gXrD��7�Q�on�A�A8����0��p�����;�
0��������p?�J�AX[���KMam9�p��� <�������7��� l�����!aH����5�G���r.���+����)��0���[�]Ma(��_���r|+�`���9�@JL9�s�����M�.[�mGvq:�k�M <�;^���C�����D�_A@��D��a�A�O�����#�V������H9{S��]|��'n���4p��� NiK�Am������'
}��7��P���8���������z"��}=�.a<�/�Xr��mn:k�Az�� � lz��r��F�Ah��3��� <��;�7�AX�x��A�A��� ���S� B� ��[���AX�����3�[�i�����[�>�'4qX���U?���Yv&�9���0���������  4A
���	�h�  ��  M��p
�5 ����NOK����M�_����A�e`��AF��A��w��A���dba-��8OG�M�YHa��_B/�r�u�`��p(��%�Nl�[����jw@Z�M���K�� �0�,���Y�,�>$+�A��}��B[E����Ep.���3�n���Y��a���� 4m��8�t^���|�;k���O���d�����X�=R��}2�-�b�k?��?��u�;S�/>���cD ���	��(Oq:A@��.F�A�A����3#8GP�25a��y�>�r��|����-x7����7�6�{!G"�D5��j�y��0��-�Cx�w�� ����-���������A8��x�M���Ge��q����p
�p��E��� ������
�.70������s��tN�'$qX�R��Q���">0�z���1"�V##����8�  �@8
��PD�  ��  �@d�A(���#�b3�����1x�vCi��A�M��\
	� �|���f-��%���T�A�z�$?�����_��-i��(n�b,���^���P�|��B��:����i��$_3z���P�}��&��A���DA���s�J3������M�����8{�3I�g<K_���R��5
v��>��mv���J������W� ����s�S��>��N�.�
l��`���u�����#�r�����f���q��AXz�~l�W#�'�z�tg������R�4|�p�m�����1��P�4iH��
�\a���|�M������H����y8?t`4�c��^�����\I��\J{��_�HV�&q�!�v�(u��t������%O��r��P9�;�8JA@A��#`3�����~������~���h��� 4��#}�6F�>0�?F�
����������9��Z�-�6��~mG	��  ��  5�B������j�������6��������a���`�%!�mc�
����� ��������r�PB�,�^y�CeR�7w/��#��D>�1�����f�������x��DW��k6�R��_���l������n�o+�m1�e1����A/��p�1����wJgJ���>!�w�W�w�
����}�pw"�W�������Uys'����~w'	�N��@hl����5j>�-� ���d�C��I�s��������)5B��~x��)��y��
��<�c�*��{��}b�����#����R�6�
:����I��G���aY�Ai�_��r������#�Ti4�-k*B��
���_��#�#]�9��k�u�A@A@Nlq�w��\���4fr��#5�`�0nr����5�Q�
�0����  �� ��	�p�~�4��oi����ED�����1(����[@�t:.�q`5F���GMN���z����k�'30r�V/���*���w��(-:�<�"�C���	��x�a��p��9��l.FO��=��h?���-mN�n���s��G�|�r����������>!��;�#�/�p��+�y�0����71�uC?�Z�����O�h��b��_qz��%���P8����CR�����(�F��?H��B��b@Ni���p���|CM�p� <	{�K��o��
���zi��Kp\qI��S�A�r�;30�w��$!�����#��n����������7��x�t�����G���$ak�������
�~]Q!J�n��	��F�7��mP:n��������X�QVA@A�$ ��p8��t�j���0���W'
����z,���!�ILA@A��&P] $�������0%��~�0���8��[�@�5� <B���Q�}
*q�[�T������J*r���[^Y%��Mk�}���W��.(�^�w�����l��uO���B�z����Mx�����/��s���x6j����\�:�������I(�����?@�~��	�;�HR^R�N<w����Z��������"�n�KJ*���$����*�;���u@�b��X�: ���u@�b��X�: ���u@�b����4I����������z�W�����CJK6�H)��B��IH�o�l�Z|�w�4O�����m\��@vF�Z�}j\)�+����5��l��ha�nvr�.tG*�T��h�$���M*�����(u��!%$S��'����<�l8���a�NG��)IA�K��q�x����K�A#��=�;$�������t=��b4�0�����s�IPX�6�R��0�5���c����� ����C�V���|7r��h[!~���������/�9*=�IC��I�������$�[���IvT���k������rz��.G.la����}H�W���R�:�������=��k�����h����8��D/�0l�x��E�6��(�Y�����#����N�����T���j��HC�~��.���WImu��<��C����.��)�(
�Ah��(��y�fn�<ex�.�~�$T=��������RN�Y(r2�����%J���/D+JB�l>RaQ���_���D?gX������I��  ��  �� pt	���=0l�p�^��ywd����^���3q�!%$ 
\��������lI�0��Ej��S��e1�3�y?���nA��{�Sq�J�u��]j��d&r�}�m��G�Y�1����+�`�<�8��A@A@"��|����N����(W��������|$}/q�8�8z���[���G���?|��<������K:C=��	�~�/���qq�}�����mKNA>�<�����������w�Sd�a}�N�8Gi��y�_PqN�e��/Br8��bJ/B�����H|�3��d�~l���{�j����<p�o�����
=����ct����Q��Fr��J���A�Pa���_�v�o�T����4��^v%�-�)c)��I�E��HxF\��w�����op���#���q/���]k$c#65���@k��0�Y�����{�����sq�\�W������{�`K���C���������� �J��2�s�����UH>�����P�����amCO�R�w#�z"�9����@��oB-�E�1R�Dm�JR�Q��%u��8��5����xo��7��ml�!���CGb�����%X�'�x�W��=Q�C��l����5yh�w�������h<�����j��2��6
�-)��;=�R�����������]�qg�g?l��������^x��gR�a��)aU��Cy�z�Z^D����r��
J����MHi�Z��~������R��?�nK������P�������6C;L��8�����0t/�K��i���}A�����w�}����Qf}O�C�#������y|T����3{2��YDdQvAV�bqAT���hkQ�������Z�����Z_��VY**��� ��� A	����9���d&�d&daI�>�h�g����3s��}������SW=��0��@���p�#���	���8���=���OD��K��:����T�k��l.G�'��B@!prh�i��u�}
��J�&j�d�*u����P'k�:�}c\o<[�c�����0�[�n�B�{3Z��8s�����u��w���	�X/F[��X?x�k�a�bL�GQG�R-�?�����~B���P�����	O�_���e?�~����*��~�|T�c��hx�R�&]@�������W�����>��2�[����J<�>E{m1��CX��F��7`�����i� >�U��]��������v��.k/�m��yvK.�]�G����lO�_�����I���v/z���e�v�������}Jsj�
&w�C��=Q����!�S.�3�����=�z4}'�oG������\W�:�q����&��)���:kBZe~D���M�fo.5�eLmO��O���Z ����9�@hp����W���R�0�~���>ad��4 �������W 4���}�?&�^v������5�������p�(��q��\G ��s����R^O ���~��E�y|���P�u���+����i�aN�����A���.��q���<� �{���yT�JE���O�n��MU����J�!D LE�V�@�|_	�F��+_A ,�p��!����+C��'�AA t|p>�h����4��i[����
o�mt��
�kn�����uzq�l��7��95��>�c8���.��������LK?�}Ko' ������yta&<M������"^{�IFz�H��'�h�D��46�F! �@�d_�5�F�%D %5���0k���p�D��G�=�ICP'�A �����~���'����"��aR�
! ��B��+:^^���j���J��}��F9��O_A[�w���@h�:W������n�	�B�����7�i}�z�'���jG�'���}���b0Px��-�����c�����Q	���:1T4�����a��w���o�5��8���78���IC%
y�����0��+����lt����07�8�����?����A����CL�Wx�/�R�-�K���{e�g�*yC�k��p4��[B�_clo	q:���� �1��X�O���N�@��<������l
�/�oBm�h*����0��!�/Q�!�M����B�C�WG|�����|W'�xg���7��P��
&��z��>���U�AXls����T�BE�B��7 IDAT1�AX�������p���'X��k9SFo
�Y 8�"�� ��r,7�A��3�uG��q�9��4)�
$L/�v�,k?��9}M��;�bJ��&��%�O��%aK�A�6�@C"`����K��3�C/^u�G�y�����b��Y<��B@�&���a��=���������w��1�\�������{�S�l=�Lyg���"��.�;?�3������,�;�n�Q�^����yB�A���?��%2�?'z~"�K�'�'$Hhl�+�^�A����g��(��p��u��Q�������p��%���=���GV�A�&�W���u����h�^?i�M���_��`{b��X�5��S~������f������i+��=Q�?�����z�A�F <�^#q��q��8��~��0a����6fm�0����8"No�����%n�@8b�q��ag�xN�@��Ap��� l�@x&��7C �@�������9}M�����+a�����'O\=�#?K�kM����R��'2xu�����LC! ����_ ������0��#;?y6D �(B�~B@! ��hC���h�!,��`��� �<D ,�9k���[����#���iK��a��8O��+Bq���N�p�<a��t�D.q���/��#p����u�."�&�7�r���B@�B� l�/z�K{)�:���\�8'q@������q����8%5%��0(��P���z��m�AX�f��R^XP��,���iF>{���:��)���/��W�x�R�L���w���/���,��
��Yyk�#=<�L	�Y�&7����y�Qd�/kI!������|m����kc���X���Z�C�<��X�kK���i=����f^M���r���S�K�X�O�L+3oH����G��{wa)����m�Jx��
��ytqG8�����yK�\\
e���������k1��v��c��;Uda���S�����kXm�[aq`|��8#��=C�
'��[��B��]�/��}B�v���;1����V�[[<���s�C	<0�T���N^��|���_C����8�a#�z��W����c=#��u�&����>��g�����f���{0:����%�F,�t�����������QLJ
! ��8��Ax��K�B@! ��hk�A��k��8[����v����4��Vu���Cj
���VY��I9_��0uh���}
5����g������� �����9n�*�����w=&����vb0X(�e���a��p�=k0�7�uD/����*{7	Z,��[|�8�Xlek�� ����1G�F�A���.��'�Ax7�n������G����l�� i��MU�v�R.kF��V������m������kn�V�uXh�:�Q36j�@���G����vrV:yvF���8a0��@���{s���/���&�������"�0�HZ�J���f�c�QA�.�8��'?N��>����d��<�����Bf|��������\p�b�!�+���������<0�q���O�@�Y�q7�^u�Q-n
�1����Av~������z�T�Z�fKn#s�������X�/Mbb@����R������F����7&W���
n���0]����
���&��<uZ����t13Mdfy�E��f�����9V���"��iSSX>�Nf3b�TB@!p:	�r��#DM�LT�����2bhp(59{�������Qwm�:h�;�9'c&a6��[�
�`�e}�o�0�t"����B@! N@���D�&
$��i�s���U��'�A�=b�s����V�1�A�z�A[�Pr6������.l�^�zV���D�� �>bTr6��m�B���fs��0��^�e#���������;o�� �z�8}t��>�3�^������b�������+��k�E��
T�R��<�P<��X������[O��EF���}��f�9U<1�8���=�a���z7���s��1�|���(����
^������N|L�~!�G'V��TY�F��U�+�w@�22zV"O��u!���N/�S���@��)%����=����=g��:��<;+�'V�
�����3���_:���Nf�����W���a����3�K}e�dw9�����RY�q�#���w�]���)>������8b�����c"�����dm	W�P��9���x���@X�ap��,?�����D�����a�c���m���^(d�<'����kC��M�hRV! ���! ���YzB@! �@[& ����8[���V[��r���Y>/�=`���S�8m�A�t�H�\^�6�#�D��	T;�:=�uS6w�S������v�v��t�0���Y�\1�������1���*>����[��TxX�=
�{��_��<�����������j>+j���~a��r�x�>�m{V���V-�)��k]���f�@��0
��D��T�>��j�����nM�ss2SB�y=R�������@B��o�JeZ��1:U�/�����l;9{��e�R[dn�@�w����z��B����esj\5s7��7�>������7cj;���������}���
! �@�' ���F2B! ��B@�i� l�
�^���M[�t���^�$�����mp�� l�~8Qmq��P�'�K���f��D����{�Ztj�O O��p��_���
��a�c�\3�~�!�#���L+���0d��+GE�?���������}���N7�BZ�,� �M`67��UDa9�^mH��XL��%����cw)3~V��pG����^�f	����ia���������	,?t"6#�I! �����8O9b�@! ��m��8[���W ��-[�SS[�� �<Lr6��j���.V;������P��b��P��{���
X�l�����w}+�:Y�����nr11����2�tQ��[�f�1�,L5�}d1�d	���E��T���;�����%�;��2���1��V��8%�Q���@)/��B�Y�A�,lRI! ��?*� l�r�^�P�-[�SS�Z ��gt�u�Ww{���1���{�h���~��Fw�x@'6��N�������E'�����:����1y2)��I�#��%��p�N���!��@��s7���(�7�U�<����1I�Nt���J�/a7�����nAq�`�@\{�����R��t?��IU�������o.Z��x%��U�����<���(�|H��q�����A�����T����	SA�rm�������K�2�8��
\�7�O���
$�9�f����#�v���c��z�B�G��C���awb��>����5��`�"��)�����F*�
�"����'ksu��tQ)����0j^*J���q?��~���"w�)����X|�j�V������m���
/�s���~t�E������r,C�~`�/����;���p��~I���@�;N�cP�(�Iw���A��bl�����C��s�����Mj0��#P��v���W��!y��7��u�;�x�h�4���r�5t���#�5��R�KyyC���E��!w_��J<~}.�R���ZL���|4�D�����H���[�����@k@��R������,r�Mc�B��/�0~������W=�pG�������3ytP����� ��O$���Xz�Q�m�
#u��B��;oGqX)���DM�LT�����2������+k�:�}c��x�n�����{���l=�Lyg��]��C��E�']B��b�O�����h?�9��S�Q��?C7x?�����m���<��~�;��By�u<G�u�uh��|����$�O�I�8|��@}a����<VEA�^b���8�Q��uy��C�v����0^>��C�O}����V���0V��#����;Z��?m��y���O�mS �|��J�a��(�F�P���8�A(�f��6� $������)�h��N�'�y��g�u04����?N��Ly(���[��:�G���k�x-��+3���r��s��Ly�o\k:��{��3=��������dw��6����2i}:�67�W���t�Y����^)geH^�����2=�ic,�yP����r��j����fs��f��m"wM����#�����������m������Mb�8�s��V.��m��v>�_K�s�9b����c���8g����&�����S5�}\��w��5��|91]<;��wL��7���m��dm�����y|�'x������RL��4>��F|h��V!��y9z���9��qgi���x��Bf�s2�����D�v�v��B@!p�	���d����B@!p�a���m
���,9[�NT;�A(�����;�������O��0��X��p:Z\f��0��82%�@�����`	q�A�vs��p
��������!B0}�a>�r-�A��L	qn���i�A�=��� ��_A�eD�~(�A8����/|�8�C9�� l��>y9����]|��^�kK	�_U����!�HZ�ZK �����Ej�X*e�s��������Da���}F����S����*��x�����K�2$���.)��N���,��
���i�������|h��0�����.�Hv"T��O
��B���d�a���yca
�:�_��F#�_���CZY��Y[ ��B�Q e+k��� mj
����l�~��B@! ����C�p��1�w�_��Y� �/�Ip��n���&� �����A���5D���i�/�e>~���~�x �P����������`�������q�����qF�A�h��3�z�8O��H�kz��8�A���#m�A��l��R~����x ������k����!�A�/��wi/��`�/gd������9!�^^wV�U��)w$����&��]�Ndf����V�72��r��	��=�7�:R�#����nrs�������E�@�u[-~���|Z�b��11dr���g��P���m����^� P�H�����b���V�{����;i���39����<��0���^�l,�X5�Axo�me|�]�T3'�����	���}�m^��Y���j���m+'�����W8���'�X��G�fF_��?�"���^��n�Q��s���Q\yC���Vg7����B@! Nq����B@! �4q�l��A�2~?��u���g�o��F/.iA�W������)���&� ��x��6<���t��A)�\{��@����^a �->���A�S��A�A��,�:9�q�:#� L�|��Ax�r���m a���9���u��������/r�%/�m� l(�m,&���z���-(����@�k���?��b��2A��B@���@���B�	�A��2�� <U9�QG{����^�ur����5����'�6���!!�)�A��%��A[~?�;w�4�9��jcr���s9��J��0����|^��8{a��O�����-��6u��Ay=������_����A(Bq6�9L[w����H��u�g��2[��E��A"�A���B@!�H� l$()&��B@�1q�l��A�2~?����1��xj_������;�������(��e����� ��fw�B�����3� <{5� �?g�A�u�rQ��E������k���
n���z��N~��
#���Gf�����qn�Y�D l�+.CB@!��!�A��2b�)tE���'�����R�e�����;��y��<���.��w�����o������s��UQr6�A�z�Oq�s��Oxa�Y����A(Bq6��L�v�:x��>����1���lus�5��T9D <��O! ��B�i�A�4^RZ! ��?F� l�����e�~l���@��o�Q:�=�#b�K1�T8��>���Z��������S�v(�L��B��8��_����_D������9.�������@�IF�� �P�������:v4��[P�&�AhF)��^�A��-mlH��5x���XO�c���P�����c������A�;����395����@BV�J��zT����1v�`��H��(}-����a�.�F_����`����Q���AX������~C��3b�����]���Ax�A�)���X|�jU+�bL��q�����j������r?B�M������r,C�~`� l��^ |��E�Mj�l�F�u9����e��x�-F[v�ImD,�bJ�m'�-iE! ��B@��@y�E�:��������A��	t��2���x����d���#��5�=������u����P�c�I���F��O��,B�v?Ze��@Bk�
B0O��YIu! ��B@��I��@�xy��y��Xe�;�P
��^�W�����=���i�?z#����u�>]'.����"� <�#o^[�gKP��r�*��e�����������������u����O����h��VP��5V����=�z
Aw�����uE���]%�Cr6�k���6/�v:������u�0��|��5'W	q�K�,](7��:���xJ���(�)5�W������7a�����qv�i�@��F��c���f�0����ET�tG�-c��{�)�)�����n��z�fb�/�@8pv�Ea��*�';�3S�|ih���������A'�v3���
�����o�nt��;�G���U�oy[��B@! ��h!����������i��>�$�7
O����M�(������Q|}w��-\;�.��B@��I �@h��A���Q���s��h�n�%������u���@XX���[Q\����0�N�^_���]a�S��G�5��<17b���Q�i#����2q�S�P,�s�6y��b�i�����	�����{�����������T����z��%��|�"�)$����1��N(�V�OZ@i�w��x�����Q��FK�M�Cy��Z�eso���r��]����2g,���fF�����'��\\G��V� �����1����[�����?�\�o�Hc�����������#8+����i�@���+<���=�[�@h_����Wqx�\oF�G�ev�t<?���?���'�Z�M��������_B�s1�?�������%5��B@! �@�	�MP�fQ���f	���=1^�i�|���x�\���.�����-������E l�JB@! ��8}�
��7&SY�Q��P�<��z�|�1�Y����am���I8{l�
�-��q�����s����L<!��)��Cn!�����
x5�e6�?���
--	���Q��

k�JK�t�ufN��d+�G��Q�[� :��5�7���1��h'�j�����xb������nE5��,�~��G�68U���TA),���O*\7���];����I�e?�~�� �#$H�8 q@����$H�8 q@����$H�8�v�@�t�.�	����x������P]������wZ�YTm�
��,�	�X�������w	�2��l��S|`;��@l�p��{�<U���H���p���t`Hl���S�����Xqo\�1gt
*���,��A�?�!�
��x��B����������q��`-#����
��nA������u���T������:�=~��
t�v�����1X�D%����q�w}�9�7.=}�L�]�!�z��*ZQ��c����s�J+(��D
��9K1'�BU�`��b���w#��|�4���:����[�+q�.ES��
�dlq?AY����rNB� IDAT/��Q�
@u`�=����1R�:�;�)?��`{\�h�s�t����*T��1G����*J!:���{������a:g(������:�s��8V�n�Cw�����h�EK<]w���e�����-�4g��w�Qq��1���1>��O�+�N��b��:j����
_7~*�ZFe�tw��q>a�R@! ��B@������`,�8�w��U,C��S�&�Qv4����\s��q���B�!%5����_������L�����$�:�f��QQ�K7��n��^��;�L4]%�~+(_��}h�����=q� "]
! ��B@D"��.@�*�}D��]	���j�L�(l	E`�F?��g����c{^��{���X�u$z�P�}�~�K��;�`i�O�^��1�D��EP�.��Q;d��EG���N����=��]�W`�����'.+%�E �A�P����^QG�����a�r��.�p�A��J\�v�U-c)��_��������3�*�����/b)��b�Q���O�@���8��},T)
�_-�����W�,\Ga9��ODR�<0��c��x�A0gt��`���;Q2������-������'�|�i�jJ��8�7�P���H�b����,^��co,sn�	��O����"�.�G�z�b�b/�������1j�T,1%X
Aw�'��c�!,/�����f���1dvD?�
�B�up6��U�����M�~0�����v�����s
��l]o�t��T����f2&�'������������L��g_���P����u3�M���$�\��}���#��:���_�y�{\Gp���}��X<���FAl��LL����>F�q����|���e�,(,��'�V��E����1��]�9�J��������.�-_b���X.����5���y�]�,D�����S�z���T��y��K�	nI%>	�/%��������������6��JB@! ��B��Hq'�{��n���:O�UhIE`Hu*z������Bu�N��������^�5DG7�J
�V5���Q��QX�|�:r����?����)��RJ��a7�#6��j�P��������DO������_���������4��J��K}��~!�I�'�'%J<l+���_�i��Qx��VQ�9����a#���D�1	m�N(+�y�X����`(t���W��>����%�
m�a�/�k����;��#3D <s��+w��S������[�G�S6%%&?[����X�=�m7���S�y��:��<��a��I86�
�1&��t�o��~|B�����d���uSIsM�@�����C�8���F �q���h��
?E����0���CB��T��:D <��A�P-�K��5�IP��m
�Q1�0)����N��,G�����B;��u��I����5cI��~~*� �����_x	��Gq�|���C#��N�;�n�'���sz"����U�� t��Y te��v���eCj�G�U����zL&���OT�n�-+B��'�AXG T7���iC����Q����������m��K��D���{�3;3��P�$����9TL����������=�\B@! ��8IL�id�YZG ,C�Z���#C�]�����Cp����4�(�@x9���c�������8eW��� ��Ax��W�B@! ��I!���O;GP �,j�k��o �IQ����U>��q�8�>�	��";�om��>i$QWu#���s�q~�1���=)��Z#��+`�������mf[�Z�o��A�wz�{�Bg� ��_F�#c����GV���[�z�|��P�V�
dL|>� ,�(�d�RF� ��y��n
q*(���p.�8h`��PG��+�A�sC��1��:�A���4� �������);w}p%z~���

����1�����U�{W;���f{����z�F�S�5�f�1�pAG��.�.[v�Z:��q�A<��������FT������dr�,'v��'�8����qN�����Bg61����MA�2�,zr-�ZV����6+��BO� <���a����*P/C��UIW�^�5����1Q@I�D���tq4����*������}��&g�}��4���Oy�����^da����{��C;&'�)���Y��B@!P�@����p���9p>�q�`��P���|���l�3)��Fv.��C������L��2b�����5� �{�B�-�~ �v����i;�v�~��U�W�_%��8P�A�T���c��������>���l���f*���T<��}%�}t(����)���]���0*���L�����o��1�����og�|]�g�;8�}1�������������v�r(���wG^���a���m5������
a��s�� ��`j��-��N��-��8����F �����;#
�7�w�G��B���E ��M��@�&�M-��B�� P_ <��P��i�@x9�Eo�^+�8���$�B@! �Bu�5aw:�;	a�rF=�����<��y��ua��$���w��{"\��|Q�A(Bq�rJ6�� <�S�AXW��p�I�B@! ��8������S���=S�����~��U��o$��� �g���=�W��	�x��8z�
�4����J����'�Nh��8����8��8�A��6�7�ir����e|����F����������B��}!D���.��/Jxw����vR��rzX���y*YL�+W�H���VbC�������8���I���M���c�����M�ZL[V�����e��xmy��z�2��o��$29-4)t�����7�R�����B������ZZ���+9P�w<�L������������ym��t��U
�[TB\����+��5,;��83�'p���NV-,�7����W*o���CH_�C���|�3:1w��)���
:�~�L��b���`�1�}����D��\�&��UN�-*b�a������L���Q�T��� 7-	3��4����d|y)o-/c�!����''s_������
B@! ��YK@�g������B@!pR����q���L�,��>����G�'�\r��������da�g�$����k��>G�������r��v���dh;�Kh������A�z'�n����4v�Z�u������a�<.�v���(Y_6"a)%�7�� ���h�s�G1PTo�8t�8/�8h����ij;v�d�G��-�n�!9'b?����$$�q�o6��A�����k0�J��x���������8?�.��09�h�KPc~Jt���A���j�
��
���Vys���m'?��A�=b��������G�nB3`/H	�����+����^�����.��'�E9���g�m�C7o�\�=�����W�@��)�A�����_u��.5a�QdWE~���j!��������#����n�{�(a��3���~5B ����Mo���%���9������	������:���o�	�BK�9�o6���ZW�D��e���-�Y��/����=���G���^8�����ac��_ t���_wn�aq] ed\��+vH1����3Ts~��m]��~����B@! �f���hI����Mo�A��v�?b�� [��������h�Z�H,k>F9m�d��rN"&N�=����M! ��8{�B'��s�����s����f#r���v!���a��CL����]����9�����O��{{�nl�X��b��
��
k]v���v�~.^R��O��0����o����[VH�<@�m���8%a3n��� tW���9��h��Laf#^������o�8��7�����oz����5P��������>3�9�,�zE-���/���Gn~��_��p9�+y��\>��/.��h����2�|"�=}y��x�$�����r^z/�w����
�Z���-A�V#C�$q��vz�p{X���g����cr���uE*E������]1�����0{].����+���Qt���4��R��o��}V&����������7�W������w��7/���@����q
�
�C�'0���j�:��������62������M{��&�i�+�}"�	q�R��AXw?�}W�/^.b���On���8).��B@�}�Ax����H! ��'��8�ir����_�u&pE;C�m�����SP��?e��O�]��Q��2%Gyr�!�h���"���uY�]d`���pE��s's�C��)��f�A(���� l��s:����~���j	QaFX}$�mwweN�:���x���M��{��^�����>���x/�z�
y��|�������:������j�Q����9\����O�0�^{<��s�;F^y:�Qu��<!����9v��P�_�iY�b��0e�����%�3=,y�~WW�s1��l�+����*//���Z-N�praD�t�/>���x��D�j�U�m�`��J�C�1����-�"7F r���IT������B@! ~��A��]{��B@! C@�!���xc�q^�Y������q���xbT�q�Y������>�+�<���|��5��:qI��������|����%��SV�G4����;3�Z-+#���;��A(Bq6�N:���s\���� �9z5p�d�X �V-8��_9�wT����a�cJ���U�x�MCs3���Cz��Z��\��K�e��.)52yfO_��|}
	y~��`�Ggv	����U��"�O"T�k�@��WOJ
! ��B�K@����B@! " ��t�U*9E��8Z�W�y�[�.����	��,l�l{�����h.7������cH�G~y�v>��T�����z�8�Ax��$����Ax�B�wE\�r1�M���1M2`���	C"r�B�S�X}t������v[�~C"����jV��R�'�6����B�*��B@!P��8eK! ��B@�X ��
�7����7��~�mf�g�]��������Y�2��W4i&�x����sH~�(�O ��g:T��a:�:���n-�0�]�����:������Xb-1�U-c)GV���{���{����Ki4���x����@�����O��ByE%A�y��n�u��s��5����p,^�q�@�^+����cJF:�������\�y���d���1������;���F���#���k0�J��x������	]�������);w}p>���a���r?F��Jt�r����n�C���X:^�K;�����FN�����*�
��
���VD��;���`��1�_�k��	tD��B���a�����G�����N)��nD%��rNO���qo��f,�^�L��������d�S]k�y\Gp���}�d,F�M� 6[Q&�a��wf��X��t,o��'�V��E���Q�*p���f%v�C����u��/1t;��q�=�k�M�����U�^��������t-jvN��g%�������nG��e;�����>b�����5�?bt��N�>�a7[dA��0L>:��^�sC�9�N��BaU%O�&�e�3�bb�z������)Q���'��G��KM���2~�G�����+����E^�=O�@N�p��8L�#:RE l�J! ��B�6���"0���.~O��=
���c>u�N��X�g>l��=���*Q��EK�e�G�#��
��y�?0��Ep��fb�w(�Ou:���pg}���������������u<�u�uh�s����>�O�I>sqX��<�+�L�����S�����?3�:����K�V&��q��.������ �=21�x��<s�������o�Y���
�#E�AW���h��TF5�������	�(���Y��] ��y�+��{>��nn��|�����8�A(�&�_8B**y��\�%�93S������n�S�������W7t�(:�?��[�Y���I���BJ���,u���
��c������y�{���32��`SM_�J��y�����s���e�Rl���������2�|"�������O2�6�F��*V�R0=��M��B����m��1����NYn���Vfvf��^���r��\L���}#lt������<���wKm<62��Qg��c������R��P�WIo��H��)�����<���Nez#Vona���/���G�t����}�#��#)��g'��C��>\��E���S��Q�Z�3��N�q�����M��RA! ��h<q6���B@! ������ �=H�����L����d���~.7����G�<�+�I���JA�����~x���$��%�Q�:^���n_�J?*����<���oIg��9�F��b|u�'���8�Ej���}Y-(��{f$��:m�%9�B���m�A�0�/FN��~����q���b�� m���#0�_���1���[�3� ����;o��'5� |=}h��������;��My��V9�a���c�A���h9���Gy��4�Z�bA�n�8+�c�lK
�������jr64nH�������R�������"���;%�����a�;G����9�T�n����wg0�[�c�:��^��=#�M�/����5�$��/����_e����#�w������@H��[��I���@����*B@! �@c	Dv��nB��8���U��-�~P3�������w��������!�C��%>�s������� �~�<S�Cq�"8��g�oo�Svl,����{;{�+k�!&����a�e��3�������?s������Q l�A�����s���������P�� l����r��AX�_��
�]Q��uN�1������sy��2'�e4����ywS%�y�����py?+)��@+xnA�t��b��!���������o����%KymC%J�t��}��u��q����*xEGo{�c?�����7���o�@���������<���f��8f�Uy�yPK �����R������s���]b�0"��}�����M��=2��'Zk�����io�s��N�r�#^�q8o�j�lK�o�>��uc�[-����_���2��W��1	L7������a�6'LHe�ps�I�m�^��B@! �@$� ��!��B@!�qV��p���>�Y���26��=�s�O��!	��~��$��7�����e�+������������4P���\]��3=�$p��tf��� ��|���$L������w67��(Y_� ��(F��0����c����~>59���->$�D���@�!���@����@B'Z��&� ��~h��A�	��M
� ��_~�V�/�Ax[Q�S��0 N5��S���l<2������q���B@! Z���vh&��������0����b�����ZH��X�|�@�I��y����pg-
�Axf����Q��������_l�z�~��A���C���~���O��p,��Crv��u��x�����s~s��t8#|~� �Y]������/��q��P���^��A���I�����g�B�4��B@� q�fB@! ��h��8<�Cr�����
���A\��p%����!,�������t>���x���Z��nD%��rNO��� l����w�\sa�8��hl�������}��9�`��F�o���'Xo��<�|9��?�crBk���I! ��M# Bq4����8|�~��!@@����j��8����L� IDAT�-�����8�A��(*�f@�*M& �&#�
B@! �@+' �V�@2<! ��B@�a� <�p��i���6#z��������1������:Z�R�������~��]��Ah$c�\x�S��P^UI��o����2�j\I�p�
q��1�@��3���v�D�h��?R���$$�1�������s�D,�W�����@���)�K����}$���m��A�S�1aP��^���8����"\�1<�l�6r:��H9������L�	����c$������O>
�U���c~w�������&*q:�9�B�$������H��!8�O&>����{\�
� t#fmv�:
:��7`��Q�P��r����N�G�`2i��}=}(��F��%�n�������/�dK/�@��?h;��*u�e_��jr��.=(�,�<.�������B@! ��hK:-��A���<�S�����A��IM�A��v�Q��N��.������h�ZB������
��Aq�w�
������
w^ �@���m	��U! ��g=��aeQ;�����#1uZ�p�7>�����+���c�P*�{(�������������0��F�Axfq*n��wQqfr�z�6e��#�^^���p��Ax�z��������)����
Y�D��w����
����5_�80���#[�@��l�><�0���s���Bk�>,���S�
���1uX�^U����n
��)����������g�t*�"���wCB���l�Ec'��1-�YaBI<���k:wF[�z��1�:����p,z����v�-�ZB����|���M.! ��B@!p6�wI�����Z�B%&����/Zv��}����"�������\?���
"���I�$��B@��"	����
z���Q���?WQU��O �(�)zy-�G����9���V/�����-��3��v���K�.�"OS�S���@������S��C��$N�9���q~��	B��F�K���W��
���C1UScp�}IL��`�!�=/(�bb������3��7K 4���vaZ�l��p�6`�z#��r�V��}��o�b��<����|W�G�Bt�������-�:��M���Aw��TM�7K L�x;�Uo�����{�!W��XWO ���8w|HL�����Z��>��^��g��J�Ox���]z
��W����t���;�E��e�����B@! ��B��P�V��\L��(���d��x�9�Wm��1t����|�~d�p����_H���Rb�L���Qd�B@! ���
��?���D��@������9���8/��s��|�y�%�->D������I�/o��~��B���x����C����M��/C�3����@x���RX�M�@2D��?)�p��>�8 q@����$H�8 q@����$H�8 q@������8�J�n��?{oU}��?�6[&�$� �)��&��Pe�VE�-��U�^�������Z�h7j�m���Z��J��Z@�EdQ�@B��Y��{�LV2		$�����G�|�gy~>�33�5��;�������*�b
����p�g��G�3r���p��z��d�B~.N�3�<��AA��cf;���jPR0���sh;�/�]j��/�z'��k�V8v����u������^�!{h�j[�1J�������	+T���z��&@�!����v�s��&vu���:Y��R"�����.X���"8�f(_�9D�@�z���#�I8�]���|,��7Q����@�"�/C�4g�jpD7�B��x�8a��.���D�������>��$Z�#��>�\�!��&����}mv�(�����P�+����L�g`���MhL�It��X�w�\2W�@��5����g�s/E���K+P�)�yP��s�DB�������8h��P�4��~�h�V��\��>xM�z�����}[ �C�4p�����	����v��F�D#hGv��v5�q&(�sj�����Z�w����8��Q������4���KA! ��B@!�e%�Z���:p����'���q$�-�b�����x&��y4--���]�v|��{�yx�G��9��GS�c�Rp�
B=j�dg��K�d�D��]T�6|x{�'8;����Q��e�
�B@! :����i�"Lt�a|e������>��;p,3�g�� �3��	�
N>��q()�2?������8�?��6��v�|�K��7a����P t�w�2P
�Q�!�3�5����r�����V���E��+����������������D�	�� ���9e���;���V��G�c�Nt�mT�y����
ki�o�w����
�_v����{���sp����R8�����������[��XXy~��5��c�g��;��I�(Z�L���x0��}��R�0k�������S��
_^Gz���:#.:{7��qF\�^��h��UD����y-m+j�9Df����C�W������}��7��6�=�����D��.���c���j*i��(�?D�������
B{f�]9���(=s.]���.�W���D����T������8����'P���^_�h!�{���_��8�>�(%����}Rq��8�cr���,}m^7|��DY�T�7J�~q'����[���j�!��M(�_I�����>z<�P6��������)��T�{,^����N��@A!�r����QD�+Q������C��[1�Sp�a���1���p!�����Y8�> ���c����o�������c�������7�\ ��B@! ��������M�;+�/���@�
��~��+w��x������<�W���E�������E)��w�9�V6fz�����R3q��V~����1B�6��'��(����~�h���M�"���r^�����C�'����I���;��x������O�F������I}� ����|��M>:k.F������,�	�����p,Hi�lT�1N�E8?L�'��z���?"pFo���������g����aq	�9��������n�g�_���YC	<�N�����@e�������V?��������i��������E���,?Rw��������`����&�����=�7#��{o&�N�M�k�����p���?� 4�?�>zr#aB �J���'_^a+�j��$�G�����]M��w���O�
����@||6	�,v����>�Qz�l �F�rv�p-j�r�<�_��a�_�������?=��U���V&����K�Tcc���G��I�]�p�X�����$�&;�1Xo��~qW�AX/�a�oD9.�{���)�x��9R��I��~��������O��+!������0��{���.@��n� 4�'�wb�����p�D�A���nz��2�>Y���=��[oLlu��Z,}�w������:N-W��������:�o�;s�NprN�����	6"�	! ��B���4�������Ax��x�����0��}��C��&�M��EX�p�0��Y����~��`��z���}*>��"=B@! ���"`>�O�g�h$�-���]2�?2�����/D�v�����X���a��6�?���>�=�#���
��mB ,�>�AT���\�_�}��;��.Ps�����w��i#�P�1��2a�AX��g�9��M�t��M��WS��������Fa����4v�'kj���v���ZhpBk�+-8��������)D.���Ah�,�p|�x���!�O��@X��4����c`0���6;+{ �V��\�����G�����?N�����0�����<�f4{0U����A��� �����3
�-�����WV��//�9ZM�UM����D T=Q2=�q47e#c��-8���A�|L����y"�XG��&�x��,����61�dE��m���R5s����
��'�=�l�N�������msz*{-m	! ��B�=	8���������/��>���w���	���Z���������zvM���7��Uu]U��v�|�3�����y��Q�3J��d�}�+���>���:����O!u���f����A`W����.(�B�e�*�z\ L����0��
�7~�����?��#�:�����fF��3d���7#6�R��t��D��un����h����SV�9}
�G4� t�2������G�G�.� ��v!���aaR��;+���z��	aS�����DmA ��f��	�}�-�
���A�X <�-�������He����v!a-L�qYIUB@! �@'&��6r6S����Q a'^�5! ��B�KN ����~R������N� t/x���5�q���e!���f���:]�S��j>u�a��� �8�� ���d�A����,�u����N,��g*5
! ��]��8����
�sY��^��!����t�> B�1��f���C>��S���Y�XQX��E�Ax:�_�A�D a���� ��R�����[2������bE�EV��[.����z�D�Nx��9\���|M8���L&*5/4�/g���Yt����������`�I� L>����x�eME���lWM��O����v�m�[�,������������}S��������G��q�a�X	?�v/���0	���%y�Y����d2����f�|���X�n9����Jg��n��+���������o��}o����:cG���Ii����om���}I��l����3����Fm�j)�_�������(�h��7�Ke�P7i_�pm�5)/��B@t
� ��$�B@! ���" BX_U��*��=]Lo�������,	��Ga�[��T�>��m��2��Q��f���S���3���5r$���+��e�8/VwU�E�C�T/S����8�0�T��A�A��`�����P�,8�6F���X�l�������0��h�3Q2��qc.
}�d��O(Z��p����W����p�N��M���%�q1z���
VU5k��������
C��%�A1����=�
};� �g����>X��3���5��+��~����Me���jr�s��hE�X���Ig�q�uD��E�9�1s�z�@-�����r�\����kS���C����A8'A��s�X�����V���u�^ l9a�_���:�R��������>t�@����+^��8��:F ����[b���}�w#��-}*��'K��dW^��G������'���,��}�3jB��n-��%V��us
5b]�FV���g�L��)��5o�	d��=��������R ����In}��D��Dc(�Q����uBpl�X��B@! N-�z�-0bj�����
r��jm,���r�A)��w�Ht+��sNK����'��(��9������B@! �%P� L%�1�������nC�Llo�{p���RP�R\�}N��P�`"�h������T�I��_�!����P��ef"Wa�C�������y�2��8��$U��y�$M#��\B ���rXi���%�oO�0� l���}���f�fu�����N�rq����!F�AX{���A���L~:5�=U�@EA%��_1K��y��tFv�@XR����o�\���=v8e���� $3��~�;7��q�P����)a����^c�kK�� �t��2��S�1\����XP�Q�1>��*�|��5��yy����2�]XN����H������V �2ojw���?�B4-V�[�����y��:�7Y����xD>�[	��!FwhL����NMa�O�d��y����#?��������gs�z��7�wH:T��Y����A9�!a�g].B@! N
q����B@! �
q���x4�";���V������L��Y&������oi�k�1G���0����u�_����9a|<��fnM	�����`A��\C�A �vYR� l�'U�8�� ����=����0.�X[^ R���0�{+w�a.xU�x:�A�h���w��q����I��k)�h�Z�x����������gCI�An	}�
69W��.���YY��^������i��@{bGB���_���hL>��O�8�"eM,�k�����HD�m�@H����*L��_[&K����e���[���?�Q�UB@! ��8�Ax:�K�B@! �����AX37�����<��i7�/Ue^��)��@�B�P�{�W��R��J��9u����M�-6[p��mCeAw/?�q��u3�6�[;/��@(�v�����A(BqZO���V�8�u&
������[O�@�TkK�l�[����k���o&/��qy6���W#���B���7��qC(y���O����L"�6�����=��83����j�����\

! ��B��	����	K�B@! ���Z�A�t�B�C�e��i�2d���CO����x��D����Y�oY	�/�rmi�7��
.�e���p��N �}�����'�o���t+��P�����D����[�d�	X?�mw����m�Z=���mr~��xxO�M!m	�)����z���*n��[M&�����d�cs��fo�5�l�
�����T�����eQ! ��B@t:� �tS"B@! ��i% ���wXR��`���z�������U�����n��(d����(���G ��f�#����d��D������8V)��
G�c�NU�i�.~�	������5�W�K`��������M4�B�����~�=X���k�����a�G�B�Q2R���\�W��S {H'k�+�����S��
_^Gz���:�6g��(�j���l&��`UQ�6a|@K��Z9���7�����w1{�R8~u����?�����l3��M�����s����A�������
B{f�]9���(=s.]���.�W���T�>@0mw]=�}gbD����r��']o��&Z^~�����T��:���+�Y�&��R�=���Q�oYX��M���-@���Zp;��W��gp���:�MK������^��I}�<��&��
�����W�z�dz��hn�F�����
��^�9l��|���D��w�N l��w|[�?�����1�m7�qfx�xG��MF3�C��g�q]gM��m����L���p�1���v��o	15��������m���n�:�������o��IJ� �UTq��qpV�����
Z����B;������ms&Z������5};����B@! ���@���0bJ�{V��GT����?�1`Uk_���_C�_9��zv���(E;��>�����
�p�����A��&��~a�{@�6��'��(���4��;��q�G��
'������z��(���<�������z�0�p~
������'R���A`W����.(����=��������R���k�W|����:�;���;������Rp����aM�q/x�����i�6�h�+0�����c��=l~Sd��K�L2��.6�n*�
�9����;q��P�� �wE?~:� -��������z���q=y�6]~�-,!���<�Y*���,z��?m
R|a���6*�gg�22�1�����*gMU&/�,��m~�����\mI����of�:����lVs�5���.���$��a��3�reU��d����S���(�-g���,u��6k��Pw^���X�]6\����X����w���zL��A����?���y�1g��2�x��5���xs�n������f^f�w���!�x!D�$?3z���k�H%w>Z\'^��T�5B@! ���h_�� IDAT� �h�R�B@! �q:,��������1PU��}l�8�����<�������M�eryq�O=.���3IS��^w��o&������(�|���n�k4��Q�E����V8��1D���UV#2�qs0#��x�� <��?/Bq�2�8k�Za������ogseF��0O<�����)�����$�B0Y�$�;�Z�T��?�$���	�m�[��zMy���$#!z6�i������W��~��^������4����u;��;�7�O���@��sO���a�?����!�n.����*N�Tl�@w�&�������%�h���JB@! ��	����^
��#��d}���8G��_r�����%�z������/�y�r�����ZfR��"��4�l�2��ih������}Bq��}A�� a�8��cV*�}���;J1:3�e���i�=�����<�-Jq��y��s��7wb!F���(K�=����S�3vt���F���\��t
�'��� O�[����l,�3��q��~���pS#~�#�f����cAY:���;�]m}zU� L��l��R�C���|srw7�`�QV��o��86�us�f����Ic�0'k����rcm�)����e�E ���jm���Z>���s��iL��u�R^! �����8OkiI! ��]��8��VF,��,>�Z����4t�x4���~L������&�6y.l�3�j*�x�z5��u6D6"�Um��R���~�����9�A������8a�r�\�s�q�8V�i�A�
��9����B=������A��d��+�H�U�S���&4�Ax1z��r^���#�WWa�,K��P�X����=�
}�A��X,i!�~�i5��4
��97��?��������A�s��9kw�����Ax�:O����a��� l�(e;9���N�s��B@! :;����9�R�vq+r�A)�������%
r�������� �'~}j�/Y�	r���'��������@��O�@X���c�G}�zM��%���O(ag�����?�hm���Y���v���t���� �i{��a�w�@8'A���@��e��og"�e���eE ��3(�B@! :/qv����	! ��B�t8����RP�R\�}���@_ �=�<�o5'v��})*?F <V�<=�w^����� q4uq����p�����;M����7F4{0U���19���uD���:/�����������;��l��B�^�:��N���u���"o+����[���DX�V����B4��/I�s�������I��v|o�! ��B@|�	�A8���/�Fa������o�P���cK���Y�~QK@����N�@x���}�gSa}�� ���1�+�u���4�0���_�� a{� ��?��R�O�_����
! �����8O)niL! �������Ax:�8O'�D�]F ���#���&�z�kS���G^����1w?�h(���~/�~�=X���k���_���9��:�\�awbG��8�9�D����Dsn@	pF�����"3��.��;@��w���O���������z���N_R�B����}
	�n������9mOG��tUz�<�~iQ������>���W4rj����}�����oE�q�b�������uM�����[�`�[��<�hy%�2]��,=�{�1��U�XWr! ��B@!�6����n��}�-0bj����W	���_������5J�9�M�N��d�4�Z��p$��CU�*�������h�_�5}Z����r���RZ! ��B�}	D����{����	�s��v.�x�mD�bJ�wql/��F�L������I};�����K_�#��Llo��4
�/�����`�-z����e�����=�]F �q
�+�Fx�'uL��. cO>����^s�N��~F��u|������(�L��~�B ���_�	f� �+���x�y�a��k���]�XS��p}p��a���J��u��^��a$���E�����J7���������1W�9��0
��y������fu�B��KP��)��lB;��k�3|���D T�n��$6�S ���D?y���Q�_! ��B@|��iY8�����U-
����X�8�O?n��5�l|?��iT5��A"����G]
��"~�V�H! ����0y�����w����Y���v�Jzw�[��DaWL�H�B7��!�����z����:���y�%T^wY��Ow�r��_�)l�x��
�%6��m�y"���@y����|�y��b��'���*�`������g_������	�����������8�&���N!:|���.YIJ����(���q��y�TW?G0��k4��5X<�f��t��5�#kp�����J��m|�����)���������I�����| ���Y��{�Q+�����,"]F0�3�����E�?�cGQ��Z~��C�_x����-��{�%��]G���MB�e��y�`�9�z�p���r������=��O�B@! ��_2
Z�����L��:R�Yy\�PO�����Dp��8��g�$����G�
�V?����>C>��Y�����K��d�B@! �@�#`-\J�/K	���O}�Y�]�e��"����kP�o�	>B��}�z�b���Z��M%�5P�/� ����'�z��D��a�Y����>��'�,��r8}z��:��]���E ��%���lv��1�&a���d��A8�:��@��d�}@��d�}@��d�}@��d�}@��d�}@���X�@�tZ�q����|-v�|W\�*"\�f���MJ�Iq5��
����YY�������K� T��9�;�����Q�B�I�lj����E���zo_���1��X����f�.�`��cQ\^�#+����S��;{6�U��zupe�+o~�z�|S���BKs������
�Q
��3ml����XHK��w^�)vh�=������(����W���5��������!t���/����G��j��%�	�S�j�P��he��y�0�:w`�@Uu%��]�����/�����`���yc
����l��n�����QT���prW@�"~^�d��Y(7S��*���n���UMdoo��g��}Gf`����~
NZ*V�~�>���8%E��%������@q�@AJN/�.��b�� �!��H�_Y�{��������r��:���A���>�sG�e�����'c�,���Q�����/��p@��PV����#.#�!��B@! �@+	��h����%K�=�pG�b��/JE	����������X��c}���d�Z�~h��l����Z�)*�I@71��`[&���x���UUa��h#�"���������bB@! ��M@�����8/�����}���8�h��V}�����t�pn��&�K/�.�C��(��a�Xp�Z]w[V��������4{��k9vi���mm���W�=�G^~�n;���u���q�}�[^
��@����SD;��u�0Vq>�1����s�[�E�g/Qq`Y�a���Y���P���M���^q5�����[WR>e���>���r�|��wQ]���n|�T<�*��DB��c\;�C�����[�S���l�+�)�����g�8���FI�H�>��l���.z����!�?����6{�'����ofN)��e88(�|}�`�v	�a��kvV��(�	�[�0u#�c+^�R����p22�����l�J����������@0����Tt���E��W�����}���z���cqzz6Y��V�%�,���"���F+I��w���(!P����1e�7)z���}C�L)&���{cI�l���	]�����8ii�7��5z2v���mc.�z�������J(uQ�f#������P����(������7+H�i ������g�^��:��U(�?��>,���P�{#��nG46^G��`��Lt��c�-�o��
O���w�\!��B@! �@����O1',C��5.j+1S�Cy�)�Kn�s	���p>����O'�����w�70n��O�a�9��O_'rhK���=�n]�'�a�8�+�����������}�K4p���&~����9/|d}��)������[�?������7�����b��I�{>�{~}�������W�6�������}B��J�:��g�*�9WC��kI#	���UU�M���?5��L��|�����'<�.x��=�����}���	��]�������������u���:����rt��D�G������{i�g��^�rz����I���(�2���6���l}\ ��\�����{fc\�C[��g7����aa#�0��p����6��@���;���q���0�9�]Jx������MK��9hySq�SwN�������t6�t���g�?w`WW����5�J�7/�����i����u�G�o$�N<���sE�?Hz^�9��mk���{Ky�|���	\�Ow����z����O0���z��&!)*}~9��9v�O�o�c�}Xk�XK>������kB��'���8���~^���Ov�^vc��)J�<X���2Wc���b�
i�dM��z�����!B@! �@����D_<��@�|��g�/�g�L�-	���X�V���V�Z�����o6*�>�b�s&��	a�@(BY�B@! ���,������A�t]6N������a���,c��o�F�����-4=��@�q��@���Ah�x���@OL'�P���6����8;b�j��U7C���O8�p�_� ��r�x�6��	�c�M^����O��3�r������6p�S��m$�`���y��pN���A�>�A��lX\� ����	�
��`�G5��@���AR��I�C��1��A�m�_�)>��@����^q�0m�h<�w�x��F>��PA�a~���3��(5kVJ+�)=�������Iz6��"]�t��&���&=�����{�n� ���{Si�|p���+n���T�>�5
�y�7�H����9x���ZV���v�xp��c��SZp����^ l��_����q?&������~�	�:�/�@�����|��IeB@! ����"����|�p;Z�*���P�=�5�'��l"+�}���_\'�g�ex������mlT0u��
��9��x=���H��a��G3&&����������~�@+��5)'�����d�H�8��Q����?��rwb����0���a�h.��\������9n"U3'%qv��QU���AXb�~��~sS"j^��a��(Ol0��~�x<?����c�\5TcH�x��m�d\����(��N4X� �UbT��������0!>�d�_ ��s&��:�pH�@xj�!��$a�A�~a"����"�l:�p�|���p(����A��@����pYI��?;�A�t��M�D���@��?�H���B@�SI�^ L�����gY���3�A ��$/U��H�W�9��k�D^{9��B@! N?�������N� lV 4m�~1���\�9�����u�O���T��f��I�m��-� l.�w����A���q� �	��Zp&�A��A�,aCa��� l}��9kr���0Y��y�u�jO8�B"������\&��B@|	���a2as�L��������	�wD��������C��d�����A�1�H���i�����A&�b3y��_M����6TT�|t�d�~��_���1ar���d9��o��^���� <��� �}�ShY<5y�t(Tf:��(�E���,�-~�x��9��0]���55i�
�(�BI����<9mz���B(�m4����-n����5mv9
�]��DxuX1y���`C��r��
�<�$����c�`(�����	
��-nF�e�X�R��i�@�X��k��#Q�g-�U�6��=Z��6Dc���e9������s�KcZM���I�m�\�2x@?���j�����*7���m�u�R^! ��]��8��LI?��B@!���AWWq�*���br�3���������&o���Ea�P�\�fn�W�,�X�_�6k���l�K����8���mq�bx�n7#��iU�%E�3||<�
k��� l��vl�crvlc���:��U��k�8���������#��?�b� ��X�QY�x9�C7�A���65�A�R��X�)������lx	�$L�_m��]�����M�9?��ebvk����y���?~�����A&��uI��}s���������m�s	��~�vTETB���5i������������7�9�6�A�P��X}�V����(3CVB\jr4�/�r���=*W�je��Z-�88���D����6���1ak���m23`r�p�������N������r�B��>+\�q��N���c&�\�x�g0Si����9@U8`���}^�h�m�<��@
��[F�	! ��B�sH���(��=6am���� ��s�[��������9�fH�����B@! ����w^�1�� ��T��
1�.������0�UeK��,�����MEW?�4������B����x`�F���nq�b3��������x��w��&��1�Ij����K5�j�%B��a�~������M�{�yR��� l\]"��?���AxR���I��P����8�k��\B���@����Bw����C���0w5t�����,t�L��,cn��Q��ZB(k���-���4C��:�`����g�9D#���`����~��6v�e�@BL��V#�m�D�UX��o���TS�V��c�DU�.�X�	FyA�Y���B���w�x<�z1��9�9����[�;ZM���5A�

D��e�3hr����z���.���s��%�fBr�B@! N+q�V���B@! :qe&w�5����p� ���*�{��O^�$�
���6��#�]L�HLiE�����t�^��	M&�v��)��7`���p�?Q"!�d4�?���=���Z�A��@O�:q����@(�V�C�8�<�sq�1.�
�3#��DY�gS�v�AbB��!x>%�X;%a���	
���}^O�����`���d�Z��mc�E����Fp������m���i\�!��
������X6�ZN\�[]��v�����V���j�T�Oi�������<b(5���\"��B@�.J@�]t���B@! ���� ��Ze���O���S��_e�D7�'����d�k*�qs�����n
1i���?r3�V\46�4y}���
(*�mCe��>�����>����5�c{O�8��h���8�A��c��O�Ax���v<�\���nX��)��F���0�T�A�smcmE��-��r����l��-B�c�H8�}�	���z������&d��0MU����T��8P����B@! ���:�A�u�Jz*��B@��& ����!��2�mkv�<��M�(�����6gaK3����{����(�y.����c�K|����&^�k����aGPm[�� �8�,.G�{~�;M�4�����C��>�;���a����sp+���I��v����Xn:l�lV[6P��2x�d���Hi! ��B@�r� <���A! ��B@tZ� <��8��J5�m��6����U-]W[�f�s��L���]�<X%GWH�9E����G �]�����['�F 2`n� IDAT����s���*��5f<�������r�8�6�������7y-��d�v>����I�>���r|t�O�Eu��������A8���	���?�H�:�?���Q\>���0��b��Y�����$D�_lW6FiG��{�E��������bv����L�F�����v�N����1F��k�^����E��c�C���q
�E��V�6�������<o4����R�qt�>�#4���+#�5]���n"�|��Z kVJ+�)=������A8�#������74��b�Tqo*���9����������Fx�:\�'c��?��1��7������R�k6B�J���&c=��^��t������g�^�e�X�l��?��>,���P�{#��nG4E�%Q�_����D��n��o&���H�i����`e&1���_g����1����N*�k9��M� |$�>�A����7b��c��Sb��X�;���Gx<D��oA���03���rm^�m^ L:'������;<
s�����=ar�B@! :7�z�v��UD}��.{
k�O���DV.#���5�����3�2�����I7����mlT0u��
��9��x=���H��1|��[�=��|_�,����������Cgz�'�Q������|�����/�����kU5�=;0�M�j�$4�;��U�s���a������=���6�_��������!E�:W`m����u3Yo�:��~�w���]t�C��.&�?�h���Ex������y���K{�[�� a;9c��s�&9��B����������}��U�>!����(�t��]��S-!��m�G�|����Rl2,`��r��K=�<u'+��H��#p���A#���m�B����Maa���r/�9��s�}��T����w�������[M�{B��q$^����h	Q:6�WM�"���9i�@Xh�<�������R�VG�\�@�l����B@! �qv����
! ��B��	��@x%�T��|�f^��g��{u�U�ru�������Yx�S��]�h������|�W�����
i��_�aOn�_�a�����
F�j-�r�#a>;������Pp�^7r�X���a�Wcem����p��7]L��+�!7�d����a��A����j��A������������'������#����A(�v�A(����B�&�\�{W!Gq(t�u��Ea���aY��#<�R��'s�o�8y�0&����B����U�Nc�
c������U��g���6!:�=:�I�7`j]�I*���5gm���
��T&X6
��0'm�YI��.9�OHJ! ��B���8
�����X�u$�H�k��> ��ch���9��f?��5~|��.W`�\��W,6)Jz��W��l[d����"����x�:��8O���8�A(�vr��_���h��6��a��w�s�03#��X�r�ba��3V>&����k|MW���+b!;�b3y�t(T����M�a�U��/5���Px��q�������F\�,�hh�F�M.�q�q�����u�<��+5�f�:����������6y�r�QU.7t�1����ckvN� �6�����Xn9�0LS�������xZ��I�B@! ���P� �P�R�B@! ���������k��N�O
,6�$��_��un�3$���QQd���(�?��Sdj��s�h�=j�:l���
Q6�(�s�D7��V� a���Z�YE ��=��7]ks�����"m#�B��U$���B@! :���AU�B@! �@�$ a������=Qr�w��"�@�������5a����Fm<�������@(�C! �������zz�sB@! ��8�D <��O{c"��)���6�km�N����|��|\c�|-���(W��h���>������G�oF��3�r�a���o�Ll4�O�Eu������+��p0U{����vB�6���&������l�+�i�����g���lX�Q"��7�+�������=�"|S����peE1�M���
��Q��#���:��C��1�]Bx�GDw�m��a������p���m�����k�<o4����R�qt�>�#4���+#�5]���n"�|��
d��Oie>������s8iC�&�6B��k��J�������������� L%�y=����E��m�s	��~�vTETB���5!U��}M���M/�o:��k�xF��g�>�e�X�l��?��>,���P�{#��nG4E�����[���.G�{~R"!fF���N"�^3�D�m%+B9O����f������&����k�7B@! ��8�@���DV.#�����������}n7�O�Q��$oc����G�m�\��������D��
�w?�1�A�V~��B�7��!<���d=�z����A��Z{?��@�\�L�u����\F�}�*[������R�F�~]�(��E_��M����
����Y8�>����&������ �8��Ax��rW,�`<�\}N�;
�aMu�/��)����4"��B@�% �%'�	! ��B��G���.����7���H�����@�_;�����X2a������(�<�G��0vqYa�O����t�,��o#thKa
��s8�`������%��p�a�.Z��A�!�,���0�Z�U�?� <��1���|��>��w�M��@�A��y�?>X��u� �'�i�Ax��/���A8�e�2J�|TWOj����3s�M���IW�k�(|�F �y�U�m$G�� `E���gJ1���[*�����Q=�����8i����-:mE��o[� 4WUb��%���?��>,����6�A�8C�w���\�.�m?nea)&��B@! �@s"��G���pZ�*��o�.��p�^��f%���(*-8'����'5�JJ������M2�����zq�V�%����=�Cw_%�#��B@!�I���(���D��
��{�����&z1����4������������:�v����l	����AX_2��1?x����
kC/�uiS����N4@d�?8����V��@������|���O��s.�x����o��K�1e*�'�|�loK����T�{��h������2l����B���K>��c������5�_\ L����{��k��[�VT�,�?��V���e}u�^�k_��'k��q�l�?���'�MV��?o�5.
�/&]���������6���������C���m-T��q���k�V.���������zn}�\�
���:�*8T���v��B@! ���D�Kg��*��[�B�7e������3�b�O�c.����	�!����~�\\ ���x�k���x1E3�����������tE! ���h/�u�K8���������_4���h�m�|�7`�U_9�S�v�n���z���"\�����_��PD ��Y�2����	�L��0�����D�����U���U+�z�����'M�����O����uz����)���{��2����=�J�18Y���?{gUu����sg��������	*���R-h��V����Z��V_����U|�b_Q�\�����,*�
��[�HI&�d�{���-�d&+	8�<>>d�=�w>��3w�{��_Vz�@x�
�Iq�E���>c�d�@h��',����ex��������[��v���$�k��<|�i����r��/(����A7��k4���H����/����>���[�a�6���@1 I��Z��*
���t�2��E��YU��X1����U��j���
u��o��|x���@����?���qx_��'��t��|B����A~~|����fn�KX�AA@A@8��f3L�����{��*�'v2�g����}�(�@hHI���;��6�{�����;c�8�4����B.G����~���v�C�$��0�'�@x~-�  ��  \����1,zu���z\�����Y�\������(cF`��0�������|����-=�"B_���/po|����j���L��w���=���YH��[����������6��� ����> ���}@�b����> ���}@�b����> ���}@�b����� �����"�S�j#�����	xv�����^Z���5����?��~��n���7t����yHGN �'��������h��eE�����xh-c4"}��v��[����|;�2�n������tK�����q��-ZI!MCo��V����(}�c<�X.R�CL��#���ua�fE�G7�B����d��[;��G����LC�9���7��\pCW,���	��@_�)+��+�}��h;��6�z9I�-�SQwy�&���pj-RY
�z������c����.M+W"Y[��Gp��E��(r*q��Q��"Y�H��H����FO�~%�'���AO�����u1`�����
�W��JlHf3�~Q���p%�Qv*�U#�s_e��<�W
A�*������������J�c�2d	��+�p���~�����e�9f�{��F+�A^�
�^Z�1��  ��  ��  D�%	��F�;���s�'H�\h�;Z�&3e'?FuG~G1�t��6N�C�iS��v�%��kN$��?�l�$��������c�;]���c��F|+��  �� �"	Hm��~2���t�n�ZL��;��c�.��G��%���M7��tR���A�G�D��#v�55����:d[0[_�E��{��q�9�hE��RL[��{� 4C�����A���Z��P�r���lL���1v�\�s����[�(�X+�����|�Y/"��6�)w"�����FM@.q������o���f��k�v\��"��b5�i�(H�0n:���1�e�s���Q:�%e�{��W�w�pz��/A/�cHIF����(�e�c������{���� 9O��A-+�4�������&��$r���1F���:'m�w`������!���[z��G�m�����SX��(x��`�������n����h/�����V����6��uz��g���4~��&��+��S�/W���t"��}()H�)H���D�2����%u ���a`�����AL�����d@����y�p�5St��:e��m�a�?l�:�F��F�2]Wq����=�9{$��� ��/]�S���������}@��Q�{�p����d+l<'q�  ��  ��  �	h7��3���J���&����"��������m�[#-k����v���xZ=�3gND[k�{1����|� �����|�����������&A@A@����4d��&c0���1��mdzB�f;���q�~D\�� ���]���I�i���j�e���%+������C��������%���O�g����}��n����f���\����fh�t�$2GV���� �7b4��]����VYv���H-�����6� ��3�y
}�x�g5�����o��$���[�h�q����]
���������8���	�mq���Z���	~����PZ�0&�Fr����������_��\C���/��1�2��?~�k'�y�@������po��z���t�h
.-
��{�
���D�.����N�����mm���^t+���F4�?6������%����,8����:}��0�0�J86���1�
)��UB�M�h���fr\,IsfB
W�T�!��W����wV
��c=xO��u��� ���G�os'^�/�M���c����=����|���TL����=�.A@A@h(��������������O�|)��������R������\��Z~4��!&
k�IA�0� �������_u�/\�����:
oe)�l�0�(F%��  \��['��LN]�-�f#����2��?��z���aol������Ah]�����FN�1����J�B� l��t����=������1aB���>��2��GrY0�����d�Fq��8�v^��Z��{��naB�@�s����0�b��A����'��!�0� 	�a��� ��t�[FQ����GtA}kK`L>�pj?����/���oV�W�����(B�?�A{&)aM���'v0���z����b!��3j���Hk��0��&�><c3��+�����y��W��x�K��������C-B�@�,�Ak_�>�����[��x��j�����q[=��������]^�����VgGL��w�y���=�,J��M��..���u��k���H�$��  �M�P�a%r�L����'m����B�_�I"!m	�\JF�"�s%�JL�{������hf!m�G_�M����:>���D#������1�,9���r��b��%n�������o/!��I!��  �L@��
�[z��e��L��]H����Q8J}a�A
����������m��Hs�?���]y�=P��<w ��X���eb�Dn�K�����G����
���p�L1z��S�l����;o�
������pa�@���9'n����Z�(�)>Qo�w���*%�id�
�M� ������a3@]
��  �@p�4��
+�	�=q_~�	�_"�����s�@���a�p����  �� �b�,�R��[a��^�.�����f^-_�6 ��pnl ���������q�M
���j�� l���� � l���AX���� l|
��Z ���p�7�������A@��O �@(��3w��-
5&B� l5q�  �� p~�] <�5��k6�f�V�{�h�3�b�h2WUN�:��X��dV��PB��0���Q�0�ve�Z�g�o� �H1*�g'��� �j,?���</��!AaZ���}���TuV�z�����L����M<�V&���k�����(��������|�R����so��9G�,(��$3&MaF{�����	�T%Ei������;��o'5r|}d�SG��(���xC�g�&��"��X�u0�|Z����=���:�����'"k:�*o���Pe�;8����H��w�a'�r����j����'S�>�gdh�m�5��5�������9�����4r�s+14S��6
7����kU
�;�H����y����2�jg��J���W���c^>9���D�Twg*��*��T4A@���@�tj:+�*��u6xt��2����'����4L���y�Dc�]�2�f)L���:��*s�}�HL�S�/��3�\��2��N�#�����2��
L��AHcJ���*5U�~���?��N�}*�3����0���UJ
8���m
�J�y��2�Qy�;L�Vc41��o�
�;�V{t����=�T�6���R��~]���W�U �j��Mc�[�[o`��x"�T�X�W�H���Ry�T���|$�Ze&��L������q3/�A@.���������N�-oH�c��Ae��<���8��4���K3xe`2=B����l6�|�-��l�:�������x��������=���uG��Y��r#�������w`�6���x&�T�Ax&]5���5Q�j#F�.� �o
BVe]X
�q����^S
���/
�_�p1zq��P
�#��m�:6T��;��6�	g����4s��R�pR�g�,���
�p}�T5����o�zb��2b"�5� �}
��(�>�����v'�K��QE���mF�����8�a@��"7)��0�� IDAT�o�VK��ZB�D��*�������e*�h�6Q�@��*�����&G�v9�nF����_U t�x�����%n�ia~�J�m�V'�8�21&�!�����=z�����5Id����+�F���2Z�2��0#��^=B�Dtr�)�+z�Vo2�������l�p]_,��  ��  \��^
�P��:j�:���|�{#�LT��
D&��T�
jh����\s��
_����*��O���L#�}��j����21O%Px�j?7�*���'�j��{YR1��sN4�,A"�K�,��S��7����,����] ��YT�A�*�UZ�E08����!�
}����F~��9�.cs�����
��  ����C 7�I�h���a�A�kzX��Nn���_�������^7������<��3Z��O2�����C��D�N�
��W}�����-�k�v��D����i?~�Q���nY���1����H�a�}�
#�b��}7��}�h��a.�i0���n�@��5����ejr4���!%�[[\�ha1��>H�<����e�4.���Z���/����I�� T��!��W������$a�4�s�tX�P����Q�u���1M�mc ��T���c������@[�i=L<�&�l������:�V�;M��1�$$��X�e�wnrR��������C]g�)���eS���:���""qc'�g������������H�^���ZB���jbZ��E[��I�<ljo����H78�������?�[������a��������M.^���'�EM�Ke�~7�]
_
4�7�����n�%����Bf���[���aa~z�x	t�k5�l'#����Gc�A�Ia�_t��xg�p�a+�9P���$�nc��v
}}?@i:�s25Of�PsP$�Y���C����B�������*���d33�l��B
A@A�B'pv�:zq����I%0:����2��;��T^����������iv���T�_�b�Ct6��<`�y($��0�.U��S^���P�(����	/6x+%�<������[���WfM����\L����pu�����cL�1Xq><�n����m5��f��+c>R�el���#�Vd�h@�!�<�j`�9 T�4�
e*��
�MP��>'�����2k�B"�,<��1�I3�;8�6���r�<K ����MA@��@����&�?�n�}{�[K������>JV�c��`ZS
Bg^�>>�FL�������O2�3�D�)�{_ew�{�y��qC���eq������C'x��z^���v���+����b��$��+������!#���������D��LJ�@X�z� lz}���.����P8�|�`�N�Q_
�d�MKP�/����s2��f��.���\���N�j[}S��������j�{����..�o1sw���2e�_n�V���S���WO������Q[���4�A�J1q����!������Z���"i�����C����N���K��&��r�*�����%�h��
2sx��Mbw+�2*?0m�*���*����|)b}�,3�/
��z�Tr�R���Jg��G;LuY����3L���� ���  ��L�E9u���y��p���V���Q��S������ddM��,����G=����/��r��n�O�r���$���Y�r9U���*��q�TZ���^=�U�@���-�r�S���W��^�'V�������A�[��A�X�LN3�Z��8�=�(j:���:�4���_
�����S�V���#S��,)����������������Q�A@����� p/.����|��-#;|*xbP��J������/�#�#�R��r����������*�s:��p�:R�n�N���9�X1�
����B�=�On@����� l��h-j���uu���a4g^�j��^�z�Q/���/V�����w�y�&5L O����-���G�L
�j[��Bc�-�h)4+��&��Oc�������x�r��Xu��������uY%5h�B��O��!�@�8�1�nH��Y�A@A�������
�����/��j��T���^�Y��9h���R��.�r��z��`�����t�u��j������!�8�y7"ui0���$��w�&��'����
7W��~|���8T(����wdx�����������}L�-2�Y��C5LXl�9�;�FA@����A��)F�93�{M���Uj�UN9�z�����XT�a��n|����2�0o?��m�:�'��d2�������6Fm��������7+k��m��!j6���)��A�_1gO ��B dv�pU�	��a�q�����������b��y ���;(\m���n5��ta`��XW���������L����&nl������A@A��%���gY t�T���R���(s���N���Yr�����+��axj�d7��a}�E��
�c�9C�\:��t6xtPd�MW�`�t�6�3y�Q�A@.����F���?,���N����3��P���E�	�����q��d�@�N�&�s��@8���&j6�eV�0�K���_�r���M��o��X,R;/�
a7������]�<�AL_�s��{��W�w��I�q�~��8T��
.�Q\���:�'�����(�C5O��9��?���
��f�������jYQ�,K��[!%��9��������DJXS�Etv��M�Q�"e�jD_�vX
���
�d�����f��@\�,�'������V�ZH~�(]����1g�D1d����k���y�����k�U�nt�������l��bt[W++[�U�.(��&v�
���tFoE��}u��i1U;���Z �v������)F���Vk"H1�q�=+��56�U�������������*Q���>`�|��Q�Q-�a4g�a��?�����q3vq�  ��  \��;{���G�Wu�IH@�1�R�����A%�������4��� �4����w��b4Z�`��S�,���^�bV-a������:��V�\�k�N�54�)���8�c�M�b��6�J��F��E�*�zx��6/2�ht�6��D�8L7��s����	��  �3�����0���M��x=a5/���a5��_?au�k����m!�0�b4����a�m����������,�Z�v���E��� �8��X�����������>PK�	��"�G8����m�]���A���<Th���&�%H����T�����N��aeQ0w�_x;)3���GSe2�5(�n�M�*�d������=��34H���s ���������$�����L��v���v{��bbG��8y~��p�e�X�Y���@�����������|~���>`���M.�y�����>7^��$������%�X��sz�����K,�l'C��^�=�V#�a7PA��M����!�����e�~7o;�21�y��t��rkws3���s��{�s������������sv�d�Q'����Y�e��n��a�-Z��  �@�� Vy���
�/R@������G�]]�ZR�l&�ILO2���2��K�����N�y�*�$��S^�����\>�Y����
�jB�(%X�n��p-4�~[���,3���/Xy468t���S�1��0����Y���^�K��[���O7�L0���b��{��;�<�j`�9�k��>��tSW���@��
�
�J�i6���X��x��^���BS�d^�T���]C��*����B��Og�i��x����+��ry48��p�e3�ac�]#��  ���?a���&1.�JG���]��r��l�����l�VYs0{�!:�u2��v��#���������0��_{Z�����������jD2}������"^�!�wrc�� �4
OG�/ 2�`��h�yJ�|��u"j��A(��Evv��A��pK<���aS�K�$�������$/ow2�$��}c���s��e{�*
�����NQC*��:z�+��o���GO�/���
��=N��U��"�����k�C?C�14f;zV:;����N���Tk��P�[{X�*P�Q��w.�vWkl�j��d
��u�0���uNfG����h�H1��Y���  ��(�oB��{YR�6<0Ua��|������v����=<_�~-8�B��BbV�s$�:�����X~[Y��V��W��y
_u���8
N4�,!L�,���4a[�����j_���r/cOk�Gi����q8���`|���T^�V;�D;�x�*��D� �nF �����sXn��  �� p�8����)�����m��-7������<���B�S�t�&��������y�s(
��r>
�p�7��+^h���k�yx����/�V��3��h�3�[7��~�����\��U����KtK5pw��mi2O�l:���^69�C�������~aT�P��V��s�c7��IS��^��*n��M �=���a/���uK�Tx��BF��Q9UB?,�����|X��y�ga�����P��+WO}.D���9��4�r����tv�Ah�UIc^A f������i�G�C��Ty���Ok����%2��9�JU���}5D �]e�	/���XenLV�?b�\h��b<��  �� ��Z���?VMg�]en���^H7��$Nfz��/�D]�/�
v��e+��{��V��12���
}B�s�*�:����X��$~,�^�0�=b�M��r�}�DW���4�C�T�wJ4�8qc�m��%Vf��R�k�|F������w���v&�;L����YuOj�h�/U�{��� ��q2�X�E��7g+�U^�+3=�@����N�i���<gSY�
���x��T����0�|�,�k���"1�"35��U��74�6t�E{A@A�B p�9�Y���cE��@9_��]��	#=��5�'.�#3"�����^�Q��\''���L�����	�@47K7�w�K�]nbX�T���F��#t�W
B� ����1�P��� ��u^������@��:�V4�y���C�]�?S�-k�>X�h~5� l�s�3��  �� p��H����h�g�Q l����A@A@8��i�D��s?�B �
���B�S���.^.QXr��1��r�������f�~�[{Z��*X���#�����A@����[������2W3�&�@��OA@�!��� �s??��P��P`����v�'�M�)_�5F�.{c(����2��GR\0�����d�������o��\�o��~�����y�F�A(Q��$����y(V�-P��@��%p>���%��v)Ih���r���{�c'`<�7���.������v0�����?A�.4m�(��6��f-qc�$�������9$0O�������_^����4�W+���gY
��
)i^������L&R��z��s#D�2����%u ���a`������w]!���Q�������L�������������Y-$���.�uW������G����D��e8�I?
���x�������=������Aw���h�����l����s��s���A&����	������;Z�h'����	c9;]�VO02�@�I����#�����YA@A@8��@8���S/>��a��x�%�����S���;��$#����
���rEm�z��A@A��&P�@�0��5��P�z���An-�&_���P�2�{Mte�q���{��=u�.R ���f���\����f������~��G�o� B�ezn�HY�f��Z��w�ZS�zU^��e�=X�.XS��6F�%��/����;�f�>o�����NFn���?wZ�����O�MA@������Gq1���F�w�����v��.b�����u���h)��  ���!P�@�����N���TB � l����������D��VD�3�����(o�	������Z�Ve
|�#M�0�A;7`��� ����@~�b���0�\���9�Cim�pZ/_�A���F�:�2o�
a���x�|�U������G�ss*H�:QpSR���@J�Z� �M[��U�Z��x�w��m��L���-��$�HR�<�����i	jz���������$��t�s��X���+����J��
3�F:t��9�A@A@A�O�u��n�jk��,��%l�u������I)����#�]��^w���JL���������m�p(�W~���D��  ��  ��yE@�n$�t':2&�[����8F��Q�����@��_0m�|^�l�`�}�P~��5wk+���_��M����������>�c��;�*�K����+gr�p��Ey�����\R�@�^�j���t8=��	��a���p�~�ZV�@x��o�����W��apik�Q��[���m�X������5����=$�Y�J�d���k������C���\b���Lm�rr
��W�[ T��)]�YDL1�F��t����P�a/����\F�cS�E ����o;cN�A@A@A@�M{����|�<���(gGG%���������?5����3���1x
�F43�
���I�/$J�A�3�iA@A@�@	hO<��	Nw����C�k�[bZ�@h���+��]�SP���&?���A5�a�/��F 4�����_P��"�t�>q��I������O�m������4| ��� ��kv�O�jX�a��hy���@h���U�?���HkR)7�c�I{z$�g1�|����b������������
�	WA��T
>~�f�����}�!��T�(�'�C��0�wG=���b��@}���m���}������6��B1��`���p7������KG��~��?C+*D�X����~���_���	����n�����Kk��76i�J�-[q�����  ��  �� $ �h}�M��g_e�^�x�u��RA1bZQH��Oa?�	���:#�������nu��D��w\��-Z��9@2�$]���-���a��as1,X�����@b2A@A@Z&������;!����T����r
���P�}�L?���3�����������p^u�a�j?��i*��
����CE
��}����:����> ���}@�b����> ���}@�b����> ���}@��>�"�^�1t���'����;f2���}�7x�E*���ehW�����3����E;j�5�I\r�%g������
��GP��{�0t�a����B.+�Qr=#S��H����;�#���?^w�a����x�
�u!���Y���z�u��>��I ����8O������V--��O��#1R9(
�+�@��A����P6����5a|�6�#�#�������5y�2��R$���A�4c5f�z`"��H��@��]i��F3Z��hF3����l�IC1C^1�7�g��S����k�i�=J�v����k�J��d��k0���w�?)X��5���^��]�z�����*!�uUC��)��>��!���y��O������#'��:��/����\3X1�|'������  ��  ��  4��������(��;]�W}�TT������==��n��������������H.�mB�����[<�lWq�  ��  -����BL��h�%�;���<�2�j��b4{��9(##)�������3�N�(���|�c���xS����)>�
#���#�$��T\����#�h7]P-�'9!���W����� IDAT^V�w�Z�Q�Yu�
N �A:8�$�����}�����86�����w<L��7(�|[��Lz��$?~��n�}�*��6�w8HI�@������~�#��[_�����-���G��[�X7�9��[�b��������>A��R��~g�_ ��r�+lxU�mPh#����X�@(�}��+#�1h_l�QP������������k���-i{�<�{ ������7����������*��_��I��_��u�Fv��f&�����M���.6.j��h�#������~���������a,t��~
�6����U��g�p]�$�������S�U�*��!<�>��MfJ�����?[�W����`L���?���AU1.}�:�v������g���E�������>�!U]�~���:�Ic��>��a�.����ZA@A@���o�s�X,o����S1d��������|��1���p�P�Z��Y�t�H�F��������  ��  �\&Sg����]�W��7 ��������q��o�NFR�`&��8�@q�Q��W<�[c�7��_��u�_ ����[q
�O�Nb[x���6f�#��O [,5����E���0���'��4�|_��Xa�@�)������<Y����;�)�SU L�Gq��3�#?�$b�J|n	�����wH��0��>���R s����&���n
��5L ����'�|�?��~8������&�Ubs]znGk�@�����5��:���_
�Q�"��r���������v�?.D?ve��n�"����Q�@�)������P3�b/4Q0Q��e�_ ����u����d+�����q��a���6��t�������	���0/�u�c�mZ��5k����X�6�	Z@�y;����},��Z@@"������f2kd
�#��)�����  ���C�{��<^��F����Y�
{��d���o@��/��L����-�u�(��^{��Hwv<��0�
y��lx�����=���vf��������t�n��NV�����8T��m�k�Z���F�lg+�� #����tf|F��Ft,��Y#������i�9��nFO�1L l���z	�����)]�!�U�1g
J9Q����]?��h�_���S8�i�Zd
����0��+*��C���]�Dim<2�1����x��A�
��05���}���&�?cy���w�"��S� t���Y��p r��0�&6��0���5p�;����< jq:ZR5��������a�]{��K��}p�o� L�_����(�\�2�$���������'�� +�M������vB�������&�$��8/9iA9t8j@6Sg
n��w����}�=f��!��#���N��v@-���	s^��dH 4�����7�Ns�\����l|�3��[,�u�b����-W ��p�)��27Z��<75��mB�e�m�s���@�E��  o�m�<��*�:fQ�9�d��G
�	��a��ZQ�fW��}�wtE�
X�s������{� F�= �3���<!�~�U��6!���������;���'���z7�$���)z�sO�\��o� ��Xb^��i��Wa~��0a�Xty�������E��Y��a�~�i:��������G_����wy��O/��9[��N��l|m���Ax���w&����(�Z�@8��m-a�aC����M�����f	�
��0>�`��3jR����U�TB l��Q��`�6��B l02q�  ��M���C`�(!F]IB w
�����h�� 3�������<�H���x��~�@�r�BD"MC�\��o?�"�R���! >T�Da�\��"�A:���gn��p�9��,__���]�8�
/k�;\� l��)��b�M1�����i��,v}a����R�A@8��P8k^
�B l)�,�]8k���,���{���2��Y_��a�����9  �>��AXY{�	��4��������"pV����<���#3)an���Bv�����wNV������=��|P,ww���Y��lG����|��K��M?+�]�M��
%�
��$�]7��Z_��=.��}�cx����:���gO9s�v��1a_;3w������Y���=kMy��8�1��\��2kKd
B[���v���2��Z�9:�����?VF�|�?��=�r^���yT��D�O�u�7�����]���5����?x����'�?_k�C0]@�/[�����A�������RdO�����x}������g@</7��0g��k��9)�w���:��q�Z�wg$q[R�.���/pT�����-�������cx��R���h�Z��f$������8Y����x��x��
_�
���k��u��	��H�Z�EX<Uj��l����[\|Z�}bya���.���i����e������s�l�������c"}O����r>����6��+}��Y�b��rp�����G��h�p��Y���oB{D�����{�*$V[�
�\�A@Z6��#��A�z���	�����=�]�����)��1�F�SW���y���Ue�H��)��d�;�}xo;�ew�2���1�0����f�6W���������_
�w�@&�N1o~�op���Y�B�UGxeq9�����{��;1�)w�3���C�A�ju�I�R�^�4h�E�*���A��N�d�/v3#~��#O�������X�I���j��q��m��{}x�p�YL�*�w�04_���e'���bVosA��7���S�Sm��NV�^�.��o0���<��0u���������
���wi��d���o��<��v���h�����t"�?.K�Y�p������F����b��Z>�v���������S3�}���
��q�.a�Q�xf���,��CA������}��u7�1ib=��h!�6����W�C��*�q�����bh�"�a����{�8N
�����Fl,�_���Y�_F:
Id��V�~M���QT���s��E�y�H��f!=���d@��������4�;��b�p��
y��|���I��Zj�o�H��(�g�3F�W��o���3[��r�5p"EsA@�������"m?��=v�a�_�tn�����
�|W&��N��X����+fS����G[���\�3����e��8����G��F8�n=�:C�C������}��HX
��x�7������W� ��ym9�����t���]���2�m���Fj'/j���:G5
����^�0�@x��+�."i�%�����_-�A�?/��a��� \��w�k#'y�*������sS���&$�#:��c��i�:�:��,b�������5���5�����@Uk��LffH`

�C;���qW2�g�����E L1��P%���#S�b��B�u+c�|;��.�ju��^�{!��E6����7���O�������hf�����k�s�=B8���T�@X�d�|V_���)�+l�5�:�@N�V-���;�uT�������2e{��m����X������Thb�������7�Fc����:ks���������m���n��W�,�v��|q�}���'Y�+@�A@��	���>B+������_����f3��B*���f1�ueZ��gPT�����G\��:���1�o����~�������8�t�'���;�VV,O.��=�}O������&�;�-�Z��U�;J�%�K�V���V���#BbO�wZ�����sid��=xiL(�X}�Z�F�,��nkz�t��2����D�Lj���`����>.�y�|Q��@X�H�����,�EH-m>������y�AVF^��s��o������1������RxiQG�W�_�u�7���K�T_51�^AA�<�B�;^��;�����Hw�?Kh	7�@=�X�q�is�>u�|o�\�>��}�Zj=��f�� p!�� ����������>Rc
�~X���:�V\�.��jJ1���e����u��
�;�V�����I�����&�FK}5����Ax'�������8�o8
�-�A�)F/>������O�J��7N�)��pA:L��V����qq�����>��$��xn2�x|n1�������B,���X>���pAc�����y�J3}c$��|��'�+�i^7�~����$qb��D���N^����q�9"��|�Dfj����kN3=�Al�C�8��!��I�1�N:��Om�KK������-(�b��	���������ZB�4:����s���y��"f���g�����f��"^��s�x4��t�����q���:k�0�K�OJ��}�<O���oJ��F��i�b !1�'���P��)�[L���b]c����:�>+`��`l=n2g���?�1k��E�u�K����1�x}7eJX���^�5;���i������
Y��kG�g���\�a����yHp
h:�v�yli9=nMgnw��@X�zj��T���1�Yh�����y*�e��Y���Py��L'����������{�l{';%�h�F-A@�gG ��Ax|����F�����)w��Qp��9Wu)`������K�d0���Mo��B�����N�����Om�D�vI<9�
�4c6@��<0%�mtg�#����������g���XqC��rjI���x��j��E���sx�PT����niC2xjfkFw7`Fe�[�����o��7#���!AvLOMK�*[�����?���6�cI=���O ��rF<���L��:Ue������N���~��;����vp�_�����x�|'W5�o:����h���U������%�x��K���W�����9�Q9���������d2"�>]NV���g��Am�]����/O��Z��G ����vf��
>Q�k����1�9��G]�=(��J]��P��'xmj�����&���SL������g3�!WY#��j�������s���'�����k��!Ti�A"3^i��C����*r�i�q
��=}U�?���M��J;f�	�
+��Z���e����L ���i_~����=TQm�!������U�������{�#����9Ltm<q�  \,�+�Z���2�D��{.	��.?l�aJ���M��������h��k��c{6Om�0��^L����������9�e(���t���S<{�����O}�������R�\�K$K�A��O��r�+J��59���5 �1h_l�QP��G"���5�����~8�����K�8-)������t9nw;���H�k/����)oz\���u�i�����c=�Q�Y��d�f��q�NY��p?���8>;��'����:5Z��37K�;}A�������p:�V2cI���m�1�����h'������$��0�T�
_�� ��RD�/��G��q*%���*"N�IH�xnj+f�������X����S^�������]�K=��2!��UR��)��wu�*����O&pS��99������eO�3�w��@8���kw��I�)F�GT- ��R����B,��'BD��kG��qx�k ����S�xy�{'����Rr�'����,����:���#�`l\���kM�H�iB�����<jM1�X��E��+$2WI�Y�4lm7�@������U>���)qI���������[�|n*S�F��
�pG����y��^�@���8�ew�3&��s Z
��  ��#�� t���<[.��F�H���k_�gt�4���d������#|
a�)+����F�������WBi������~���27�|�&}��wa���i�jO�c}��@�5�q���������5��c�#�)#L�������`fZ����?��{��=�����1��R�\�#��m����l�Uc+�r{��MiC��A��������l�`�P����4��*�}T��a�M<l�����7�������\�����ef����|���ai{��.���F����
�:JG�u�����z,��)�oz����}  �6�@X%����`������U\����L~+l]�"���#��A��'P��p2��D���������3�5�~���E�Y7�M����u�(�W���1�}���8���T:�^��^���K�U�wn���v�~x/�����R�>$�%�sy���O��6�f��_U \�g���Z5 ��L�cg62�hu�O ��y��`����
�j��� �bT
K1���@�9L ��B��������J+����f��pn�j�@xC5��0<� ��=�_u�R�����_�����3��<��=���@����)�"�'�TQA�����F�������O�Z��U�kq��Z��JA*.Ae_B !�,$�If��_g�L2�Y2�x�u��d��.������������ �Z�^"	6��dA���]Yr���^t�0 ��FlH��dH_5�z��`�����,f�]j
o�Y�zp�����A�<[r$&6��{�g56f?_����AD��������	��4\���s�>F����pMH ��.M`�����~b���G��)�u�6S�S���p���NO���������3m����{o��)��  �@�h��)a]��_d��"(U`T�*\,i�~}�N�����=B�C����|����|
����p�B,��C �'��4�]��A�N^��<c��-��ba&a"`�I-����_�EHs*
�"3�=N�A�����11v�����p��)�~�9��W,��u�b�o�T���M3�����L �t���
�#.b]����A�4!Y �PN1*	��/������e�p&I?>!�#���%�=g/o��a"`��}V[�o�=��������-���Y�����R�F���5��`�k��)F#-�����QN����P�m����AT��VT�%��(�������i�����>9�-J�m5.Vt��N�a+!�o2���(�aa�k.RN!����5-�J~�}=��Xw����u|�����0���9]p<�"���  '���r�Z�}�Q��T{��;3�S���B������#��G���A��L�V�q
���/�sI���Ks*��|������R�l��:����n7�T�q���������Kq9�/�ytr3#B �%.~�rM7u���A��}���3��!p29O?�P8��*�>��A"�$����YN1�AH1�A(�5�Y�A8k��)F����#�h�OP��8�A�������pi-H"�&r�(*;�A(�}\N1�A8W��f;��Wp�j�H1Z���v���oC$�b4��0�V���s�5�xn�4^�@����7��"�U��C�r������{�t���������/���vi&�����/�h-}/Mf��j	�#���k�-��Tk��b]��m��0��5��v���<2$��3\\v�FO�)��N�rr���a���A�g�b����d��n��%T�p�;��8�m��K�)��  tJ�#6� ���v���Hu���5���{1V*��H�A�OS��~���0WQ�0B�����Pt�+�;m��m����MAi0���C�����36^���?����A�����A�7�xt��F),���zg���DHw	aN��*�����7J��s��kx���,4���(�=���sY���F_`M�}$�!bH���p�$rFJ'G�L1
���5F��;�����3��#/g�a��uY\)F��n[JmF�Q��8985q��Y��as�R�'mG�d�AX~� >��73Fp��rN�-����xshS��BO?VO�B����v��JB�b��p6�� �����@�D����|:!X������x#��+W�/+���K�`b6�	M��N���u�6�2�����E�_�U9��&����\k��EU��5���dn���Rz����Z6Lu IDATX����J���R���yp�����\��D�sz�>X����2.�@h;da�$<����)�K��7w���u��$�v���R�r��=��Y4���4�_�ry9T�`�f7�.52\�Y'�^�����LLMQ������G�c������xY�E9?���7S��{��,Y��Y\���Qm��!�������*y%#��+td���M�`�DM�����9<���U�)g�&7^��y�������G�R��=S�bK,�?�w���x��Tn����m����WT_JwV��/�_���#T!L�"���'���(
}��ugs����W-Z�%��h?���G^8�[�F�4�����k��������N�#L���@��/�r�=N���*R����r+7-�����DsA@���@��M9_�k��l.�M��S����
�*���(�2_��u`j/�m&#z��_����Y����G�2T�Ya�X����UL���%O���t���O���Y)o�V��!T��?�;{�c�k*&���/^�(X� ���<X��3�>��]�#YZ����U�<��2����X�#�W���L�������w�L�L�?��vn�aI��G����,��'l0q�+���|
:<��|���Yegrp��D���������YL���(n��k����nFw&K_&n+a��G��^�?�����������/ecy�S+��Y,z���#uv6.;�kR��	������\��������*ykq	K7���w���yp;w/O��������(B	p�������|C��Qgc��w���uB��q��)��G���)�iP�m;?�U�d�h���D5�b?K$W��9�0F�9�z_V�����q�����`����|����H�t+5�����ny���gI�
�����/�J'����8���(k#�D�e�V����� �����b}����?�!}��vSt��W�?�G�I
5��q�vn�c��s{sS��L�o�����	���rk��C'�����Mg��~\)}���C�of�*d�e��b����u���f���A��mh��jr�v�]�b��BI�a���5�Car9� ���������x�k\�����4<�F���6��F����� E�}� T���:v
�FB,y���������l��(z���~�������$=��Q,������_���o-]�
�T������V�C�e>���|7P��'��3}��z�-\'� /�:���jJ�6�?��u��l�j6n��s�y�W�Y��@�n^�$���5��$���������hB*�f�9T$��/���3	�Q�FI�8T�	uP���#E���
u"ev�
��1�����g�`���<#Nb��"����q��j�i	?a��VNh%�U ��c$W���Q���~xB�z�7�m}�h�+7�o����b@Q-�[�1���p���G+y$�yIz��K)F[����  �@�$�9B����-���F�25d�;)o��)����~WI������DBi-��E�����Y���`�d@
~�i���OB��x���9��������s��g��x�R[;�>���Gz��b������
����G���h�@(�"J������[��.��6���v���YO6�W��g;����u�~����1
��k��M$����~�:d���#����C��.yv(ONm������^��u/F��O�%t��6�u��RF�������L���H��y�a������NC�>P|�)���8U��@��������������A5g����"� <g/"� �~�l���k��0����.X �t?�~����~�V���#�������{��.��m���v��@\�0����T��{�������5���A8��?��~.>��1~��������D���a�����H���
�T�#���T�����;�s��(��C{�H��}1�-Rr�)�����(E��ne�����>\��}���qF.��T�G���h���6V�w��Y��Lf��1:��l~�%����?�[xr�������d�>��u���K�q;����Oy,24�8���#�L�����Bi�^6~c��}oec��!���:��TK
���
��xX���W��q����
g��f��,u@,us[��+u���W0{��Ew���f2r8E��uN^]W���������p�����I
���B_l�����������*l?������k�xK��
�����Q�����]��l�<$]w~&���q� C|���W �m��������A��(Ij�1pM���{)e'd�5Y}��[�[�w�<i����:��D���8A@�S�@�����J^|��O��(��3ar7n������y!B:@��*��}�O6�9(�q}L�������L��_KP ���e���R\�3���"���ln����?��4J�Zg����xuy5�5���}/�`B������P��/I�6G �ygt?�������`�d\5ssp�q��{%�}u����L.����������})O?S��mv2�r��,n����)���7�r3K^>�'�T��0���L.�,�����1�������<�������}Cw�}��_�� �5f���<���V��]���nt�����r�k�?��Xih;t�!��.����K������5������g���a�N��fY9���a�6�^���SS������qP8��,}����R�Z\��{��t����26�B��By����1>ZVK1�~_������/c8Q���JX�F�;�^97����
m�^o~v�g^,e���x�P~���)F������ ��N*���8[���]el��^Y]�rD���t
AB�����Gy_5��J��4��I���]���Df���q���z��l�TN�-kM�P8���h4�p�@��T��Tvvl��� ����T��n��+Iu����e*F?�DO���	��  �� �#����[R\���!���n1���Bg%�ag����  �� �
N.a�o��;�����o���V����� ��(�m�����F��m��bT������g���3�v���y5��Jl�*���.����t��op�T(�F<k��U���b�c/�����A��d��������/�$������(A@A@Nq�Y ��������`���>��CN��8�'���(�S!��A@!p�9��������?;���
b���!��DXt���a����=8=R�vl�	��c�w��51�%F���f�@��q��;����)]X;.�J�y}$~���2�j2�yB'k���K��*=Y�l
b:��  ���B���r��`��:U���:�s�������lj�'�@�q��@�@���UME��y���q��P������)�� p�8��������D8ec����K�sG�|#�����w/
���{���@(aKC����@�4#��t$����U�#���qT2O��	�+x:RkA@���@��|��B��dc�>�o���:}v�$\�I!vW!v{1�  �@�8�Bs�Q~�C5��,I��&�v����k��m5���l��69Mt��]�"��������S�{��>����?�ke��^F��-���]_��Z+)��b*K'St���~��� ��_F�AH1�3y�}��S���{^u����t)�R�V����>��=�~��
��<���_�I��I�d�������x�l#}^)���������`�/����N���"c�g�q�j�Fn���w�R���6	����Y��Ge2��
�
����SCO�f*����~����[L]�����g����(p��,}�/�Sr1��3}mT�����a�����v�����!���Bqd��c���s{�c	��  ��  �W�k1��I�Z]�a5�:k�0P]|��p���wgc��c��+:A@A@�����X�B�����+��N��d5���B����z������q����$��'l=.��e��W���j�g���T�����������yg�v���QB�7� �%j]�����7�����<
�WTVn��AE;$���y�@x.�M��B����L{����
�K�%a8�d�@�����n?���Px����!}u�h&�s��P��W(��V�A��n�T��7���QXm���^=����)�4:l��i��	�)����%�������Ai�@������J���Z����s�$�����;4�/Cu��h*�X����JR��5������a3r����ET\��<p�O �8J�w����2@c����HY ��,N�=�J_
�m����a������g�G��n5
"d�]��  ��  �OFo��nG��?P�8T�f�=Wa�T�G�i����z�B���8>bU��+:A@A@���lW���
(��"9��������u�@�J������7	�Squ&�6|�uS~�����K��6�?U���q}�2�ka�	��~:��{��&�h��w`��T�y9�=>sm��R`��G�G�E��+����q`�/�|��.9h�?`�7��T=��wP��"��K0L7�P�D�����P�
���w.�{^M�78]� a�<�^}H.������a����Q(�����~Q�5n:�2�#6�@hz�F�����l;�~�a?��z#�@hy�&T_�
�����i�K����<C'�<�J�;v`�k!�{nFy��F�P�0��y��:�v��(�+��8GO�6�
�O?C�t�!+}�����)C���A�v
G�,z����`a�:���9�3�@Q}��7PIo����x����  ��  ��IG@���g$�	7��Q�>���OQn��k�Xw��=�'�����_6�I88����r����W_����1�?��<|��A@A@h��Z����i���^����E}�5hF�p;�	���;��ED�0��P�����������n�w�S��*����`71�z�O	$��N)v
1�  ��  ��  ��  ��  ��  �� p������Q�5f�?�mi>���$�w�o+4��(]�(qi�p�F�o���\�bJ5�������:t��A8*7��=�R����$(:�W�F[]�]Q��P!�n)��L�	�Q��B1���������(OT��Q���D�K��};���h���������D1
�a��:\��]Rq���u4��;1��"�r��jT���S�G��q8�U�S�|��d�
��Uk��!���M�z�b�j�ZJ���<����y��;p���B�u� 4=����w�{x�]����X���+T�,\��Vb��/�|��?���SO�B9v4�����o�>���eI�o_��sp����Em�������.����>�~�:�Q�9[e�Q������@QzE��x�&<%��wlFe����P���=�oby��  ��  �@P( w0�!RJ!P��j���p���w��ozW�?���Z�=��)�S�����QT_��y��(sG�Y�)�J}tq��  ��  ���j
��cp��b?�O��l�OCs��V_�b�4i^<�v�V�.���S��bTF#��.���@��.T�mO���m���c���]q�h���j���K�F����gx�T�0�+����>R�����c}���M��}'��P����;��y��o������.�m����Ka6��va/]]��&�L�4��B��|�T5���P�5�=�I������Q�����:mXW�@WcCuF���kj�e`�{%��/���;��:<��x�.����zw��������d�h��|
�cA�����3i����z�8>z?=WO�G���#���)�����N�E�k@5<
���'=��v�H6��������%<g]�w�T<�'pn���.X�[
/iw^����b�]}�|T����-�B��y�D�����'E�q(z�G����.������IJ����j�#���|_Fx�5(����9x�IxJ����yt)���ZQ�[�������  ��  '3��+�����CQ�P��q�����1��f�����J��k0,���KI�������_��  ��  ���E�z�o�����xQc��]���T��ut�#/����p9��Op��������
Rf\C��y�VlB��'�V��g�`�^}o�I�A��c����L���3;j;���������N� t�?�^ �z�?�O��G-7R�����
��>���L�(�3C�t���A[�k�*H ���A�~o9���B�����B��N��j��c�{��A���	�����\/�V�0;6������7wE�JBm�j�^�o;[��'�x��b�����GR�q�������xY�C\���Q�C����a��u5��*�[V��
�����i)�w;��
<�O���A<#9)Z�����������A ���{I�p��A�q�Q�������B����)&���y��:Zl��0mO���Rs
�'�J�3�kxcF&����";���N�b*��  �@'$�8o������5�J����jm���$����Lk7�J����%�W3E��H_�t��d�k�����x��$f�?_���	���o-�L���N>Y1=A@��x��Srw�O ����[��>�
�q���O(�m�u�,\��R/��������j��O��7����;��\r:���i���A�F�{�8�_T�����+)C��/#"���I�iS�g:��)��� T{�$�����
����:-,����k������h�N�p7���������w^q���x�h�^tr0����l�;"���_(��F���c��3������6�4�[���[�����_�9U*;�%�I������m��o�9~R�9���(w��5�N����_.�`E�.�=�DV�4a��N�{���|���y���/��O��}��Pi����w��Y�,��  �P��D]�-�Gjo���n�C��;��^H�b��	JT�Pm����y(�9k<�D��i����1kql'��"���m ���"NA�P����Z]
�1�����}�v~����x��]/�LE��`��9�'��tp���P��5a�g��t@�4����,��+�Z&6�A&Fv&.��j���a=�>���tF��H.�:[O ������ a\�^�6a���B l�0�	��  �@�h����H�a������-QN&��d�kS���@�x��3A@:�@D��ugpz��j�W�4���^�R�q�@�|�
����H����(��G���bt��a�8�	J1�=�a��v_
�����}5%�$��A�G5<�&�yY�_�DWyS��vV���{��1uv_�P%�]	�
��)Fj��@(9c	�oD�AH1�Ah�S���AxR�	�ta��N��p�%B��m��3=��  �2��%a@ ���'!�/����w�����A��6DLG��ia������!�k8z�Rf�����@(��a�ma��gX��A(#iH1z8	�>',��S)������mW8�Tu���yzo��)D��>����,��ef�����I���w
IbzZp9�$]�~���?�y�����>�?�J&/���a�/���N���ZjgdNn2W��m�����0/��3u!7����*�k�0�Wc�'X�?�au���w����F�{����Tf��~*�!uU�/�����w�y��r3�{������w�H�n����n^�m��v
������#9�2QUW����>�~����w*��x�\��[��V�W�!Y���xl��Tyr��*��k�������){���q.����+2�%��M�8���D�i����9 '3���%ssoM���3#�q�?�"���^�_��*���;�J��.-��QAk�� IDAT]U���-|p�����Y�������*"�	s�m�����Mf��#�)F��jI���S�]�����F��7q�<������8�K`�ESA@A@"�>ala�l��1;.��_�]u<8���d/�%�Y\����}������}��_`��R�*����S1-[��uL�������F��U�y����(�]��9z�vQ��At��3%+��x���]v%�z�yi��������d�>ow���i:nj���Fp{=�-�����2���L-�h����FJ����������C���������h��7�!;Y��^:���0"�|�t��P����(K����<�����dQ��%>J�v�0����Y*��AX���=6��q��H:$~7��1?[ECVY�jf��d\��-�:���������R)����p��B�O`�U�v?�`�?C��<4D���P�����b��fYA�>������r�d7;�p$�
?+������#.
�k����h�m<�����V/���o�z(X��FZX
B/[����a�?�}��yj����V���\L���!�����0~��w�'���x��6��  �B��;���	XT�����I07B<=XY�Y`���M��T���������aYh�����v�
��t"������4rJy��:��
��������0P�P�ss$�p�s||�N;��+���b8�C���SaF�j��������b�_*H����i��d�����P~t�5�����}�A����A�?� L�S�m�B�`TKi����Eo������/3�d"�ejrpQhm�iHA/�w������k�EX��I�.T��9���q_y��@8&KOa���I��CC LV�cvQ�h�yyY|~��^����0}U�UG���s�y���,l(�Y�{Vn&�N�7�0k���~���ro[Y��ep�p�M;�@X[��U|�8�1yY�Zst��NN2���&��z�RG�� Ft�������|>$�MOba��
���z��oL��_|�,Z	��  �@$�#��AU��&�S3��bm�����5��^�
�-/a%V���b��i��'�U�@�~�1�����I���cU*��CA�G��b�U������*�Jf�J�����<,�jfNq�:�s�,��;���fN5O����)��mN�|eaI������q���5,�#�,�s�^��(�.�H�>nX
_���$&+����#�E�|�1����G�'���o����].�h��O�=Ml�%7��B��MJrk=��p��A�|5P]��H��Ig�c�}�������D�����������UZ����a�a�/NA@H�@���NZ��xy9����� t������Ww����z��X�o��$��d�T�T�02����W0M�w��A�
[o�:�m���&l��
���tv�O������:���;WwI���X�S�Ah�0ky%�3�ycL*�����Z;���>4��j+�}R����6^�+����XM
+.Oc��Q��0$�?�a$O���7�2�@��Ns���?����L�4�*���Rj��|���3��n��� �D����[K�3�A(���3�7�Nf���0Y}����*x=5����~[@�I6���4n�����p��!j��������Ho��m<��8�S���4��P�X��q�����epG�Q�r�~g%�vzY��(�w����t����t�Y���y[�����z1���(	��0�Q����S80T�k���S�zX���Y{����]y�:/m�`a1�7��_LE���J���S��.�u��������e��=x/��Eal�;Y��=�2�33��0������U`d���|m]���Q�$�
���-�Ku�������q�t��5g��9��  �� PO�}�x�N���yz��)
�^/k�[�h��'�;��C��K	U�6.��J~N�gh�a�c��
u�G�6k��0�������i:�=��l9c�����'����%J&�xz���S������`�j�3��b�f7������ql.����V���3B��X�����&^�����^
��|T���4dGqF�l6'���~���E���f�/�Ob�,l����9��M�������$m�1tOe������N�+S��o	hxt��{}�M�9��-��+#�{��8#
���r����Z6O���^���A�a��jn����:F��VY��,�Q��?���!���?�1�� ��q���Z%'Q�'�l��%�������3wx�6�����dK��7K��1���iy������&1B��j��>j|�<�'��z����8�����S�E�	���6n�� ��������r�^/[K�����a��x-`���j��q.��]����`�2Q���  �@�tb�!3,�z��r�	�����.�@NA���T����Q����km5\_	���{����8����Qk�����������A(����I
���������D^���aO���������c��Q#���*���
qM
�������=�O.!]�{�)$%�����t�v���g���il2�CR�O"\`�5���������`zh�R
���F�����"�/��M����"�q��7��b�q�T�<D���'���F�a"�,�~92�
���s^=:�
��6)-o
}VE��~o��1I,<���c��#L �������}���
Wi������^9}l��32����# 2GH?�
b1>?j
B���E��>�`��Ka���V:�b*A����G�gI��IiL���'��vO��mA@A :��c� ����\k��uu���[��%[���0Q4J$>y9V�di��e5n�{Xk���FKas�9CD�b^�:?��E�/z]��X��y�<���2�����<~�����d��a�#}�U'K�:Yy�M���Z)U�t	��n�p�YI<�E<�����3Rx='8�g��k�87l�C�F�����V�����C��Tu��bd���;1��suzV�10���Y`V��Q��EFf4z���T���1�D	��r�(���u
�����S���N1�8}���Bv�J����W�y�w@<
7���C��������56�o�r���G�6`h�[2^&��  �@D���!<�d�S���/�����.��y��.��\+������&j��u,��)�h���}�Wa��c�R�*�Y����1���2b�������68w`MU���
U_#��s������(���S�*���c���{�&9�����K��{{��4[��c^W�u'P��C��cw.Vc&�G�����!��g��v�E7 cN��q�
#c������:MoO������(j�C��r��t9����O^')m7�^s�9���g���b�\�zS�)F�bN�k-�����V��������,k|���y�@�@�F���4�R�7
c���C��n����m�.���7�,4�e�=JQT��/x��HL l�9����j���
V��n�����S�$\w0�pa{#���	>Kf��5!�0����5�6�]s1��
�O0o�9����8CS��d��K�2����=�d������������h#��  �	��@pZ1l�kU�	�� z��<������mJ����!�So��"��.��rp��1�����GY���kP�l���a��Z����G8����~4�������fvU3�>]IP�G��I�jFf����aR��{�0����D5�"�NAis6�#�h�z����"!�<�MS3"UZ���wS��x@�����7�O���o5�����Ne���.����DFNL ������2����B}�Qy�X�����>��N����D��D�W�A�5	DM1�zzHC����v~����x����� eF����C50I��0���9 vm�zLA���G ���/J)F��S�J��x������_8�+��r�e�]�h?|���ZI��,>��D��y$p�>��-J�mu>/�������66�!++�MJ!�'��0�5G���B ��#3����apj��S���&M�$U)ib�o�R��_������*7$'���_��D�x����h#��  �	��@�LaaC�M#s4��)�k�t����\ �UX�o�,/���`��
��(�����J'k+\��t�m��c(�;:���^������E~���>���IC�X�O��#���apj�3���$]� M)ib�o�R��r���o+�~��I������'&��_�B��0b�K!&I�A@��@'v����6X+��[+���7e�k�SY ����A�6�Ap���L�t�Axdk)��X@	�D�6��S�����8q����I�y�GhA�DR�FrF�������q��^<�Sz�4\`��[��SNO����_-?:F-+�	�I� ��AX]|�>�����6�;Z��mc{�b@���|)F���Mc��
��yz4��R��8�+�gZ7�h�~�f"�q
^_4�&j��
<��{u������|��J{�GN��jui���9MlB1������-St-��  ��G ��!
)��;��������������v���-u�Sk�xmb2s�/��n��P��$�b4���<���f�5�������$8�g��TUi���$�b43#R���<�p��
�<������,N��R�VUX�,��r
[�L5�O30����?1��,��vC�����q;�
����V[X:(�������MI�2T���K1j'�LC���R������1��'��  @'�Aq{<��(<|�F�)F�v���Mo��� |>�B�@�����p�O�2/
����xG������y6�Ah	{M���/��P�@>�=��eA���m=��%�0�'.���o�8���:t�2�ycL*���O`�Z;���>4��j+�}R���$^<?��}m�������*V�����[)F�����n&52!Y�?�����j.����1B[iy�`����F'�'m����B����� ������K��cR���
kl���f��d���u"�00���)|x~*S�
����ZX�������xY��(�j�sTw���%?���N6�Z��2q_o9G���3�-���L6�k$K�x��,>������j	O��a��f�Qp��L��������xiS�	�o��)F�[���2^����epG���l���;P��n����h�c��r��v���&r|�u������5�S�������_y��nNS��n?j���}$�.���d52%]M��]��VTR�#)�W�1m'��������  �� �@��m� ������(yt\u��h��XVdc�^'�)F�m���J��d�T���\�,?M��g������N����o��R=�_�Y����a:f$+��^/�j��-q��������9k��q}M�4P�����KA����*�w��l�.����������Zz}�b��d^����I���v��ogmP-��������UE�F�r�x{
������+�j���f�Nn���v������j�%p-:�����"4<:���]��YdeWF��Ep���YcamO���J�/�*<w���
U��a��jfkxa����
_�����q��e�� �x�{��i���D���c;s��� P�����uu��n���:����RC�������=S���/d��1b{�!3g��0i��wh�iQn���c�!��RY���F-L1�������L�CcL<�U�k/U���q���)F��v3c���z7����aw��Ev5��r����o4��������)��W����^A :�N� ����j�m����|<�f���B`m%L1�=]�^#�i�mn���
xP~���BX����a�����������6�	�B���0��,�js7=�k�����M�j�A5�[;����G��T�P�����H�M
��Q�=<��
��1j^�{�w�^�P���,�P�~��Q�Z���_���Q������_Tl���x2O���4�]������Ak�k�
W_��xA����7�?)�a%vkF��W�
�{�5(����9x�IM� ��m��8m�G�G��P+�q3O��}��oc���Lc���)F�Z~S��w�	��IvQhCjF���3	�Q�FEN2��E(���/�-l(����:�2�*�OIc�,$�������x�o�_�X���y�*���L^+�l%�U l"6���9yX���Y����1�*6���[%�������,?���������0R�L�wq_�Q��~MJ�bZv���_\7-�HA@"h?���mX�0 �5^e��qU.����c*�1��n���}���T�/P?�
B�)������M����x����0�����1J�f��E��AL�^SO��Q���])�tF��q�SX3 ��Rg�i!#���]�^V�T�����(��KS�_�q�JN�36�}��%�&(�����jfA�U��|8��m��7�{�W�/F-�F�%t���1�%[TGh�Mh�@��
��c>���Z�	����[��)n��������s��A\��42�w{4���,F�$A@��5[Gi�A������A��?y�fg�;����
#4��
>H���B��f7����}o������_��6�av�����G���p����� �cp������=\6�'���D������jC�p"��2(Z������������O� )m7�^s�9���%����iMxJ�q�z�u$���Q�Jn��wYX^��5c�'q��2^��.3�pGh���$1=�����B����X}��+d����'�;,���;��ps9���*�.������4��#E\�=T �z������>��"Y���ML�odJ��P��Bi�6����[�dV1������0�z���	O��u�~o
����\�B����F���#K�\�"���n�7��e��/���q��#TD����v^�V��E�~ANf�K����i7C�I4�	����
3��S(3�yP2W�P��q|��[O��Q��UUF�/{U���5������'�xg_�����qJq:=��-g&3���xb:���&���  ��L��B��0l��>\���$�bT:�����?�X\�!;Y��>z�����Y,��_���~��c:�z�p��4y�cv/��T�%�z��u-�}�m+�#\,�g��r'���N��l-���2#M�*"��zX[h���NV������S��>Z���.�2W���wX��������*���6�f����_�dY�$�(�]��9z�w��C<��YR��3�>��.f��1?[U��9]����o��4����=�vU���;^�v�����.��~��c����?4Ne�e�Q�����V�?W�4{p	�PU�d�a;K{bR3#S���:&��/��<�%�]���X�q����LK����H����.7�
���������DMvhE��l�@��Qi��]���7���_�B�@�|v���!�s���cF
7ei�
�����C!6a�9jfV���&&5��7��  �@�:��7,3���o���8\��7�NK���������C�07	f�h����%U��v�a����i�x?�m��['R�6�-m!�2���;���qn�������`L����� li������'����LD�@0����$��  ��)J�����S�X�  ��  �@0�N� <7�!��pFrP��#R8C�k��������y{�@h���r�g����w��i��r9�;�3�P{�$�����
�����-���Rg�SaF�:�G��R����^�k������h�N�p7��z��;���G����!��g��v�E7 cN��zb� ����)W������J�V~���mil�����(A@A@Nq�'Fw�����A@A@$��Ax��� IDAT��A���.ByN��pB ��=������aB��u2�V��l^gcFg�������$��b����WCM��'��  �� B@�m1��EvFX���[{�-84��?}j�����j6g��s�^���8_A !r����%�����'�R�	�j��B ��
��M�+�S!�g1��D������O�y(�u��c�t!uO����
��  �� �(!&JL�A@A@h!6[[�$��"�M�����:l���G��*,B7~,����>�D�Kt���ZYI�D�~Z�m�����6�}�7���43��+���|)F5;&�7��K���*9��w(uu!)FkOx1;������C�����ZQ����KJ1�s ��T_��7���{����Gw���Q�t$�U�b�e�`-�).V���T^����;�B<c���Z�P�v`��������
wGlk���g����8-B��	��t���k_��$����k5��v�U�q�q�Q��e�s�����|�s����g������%	��  ��  �@�����m�	�^�(�i���&Zj����m�n.[v�Z��#A@A@����
��0�����x��R1�H\��>�
�q)��[�%{p�u��o��
3H��2~�
����s�k��:�L����Q;�Xkq,}��kq���]����8_QY���/v8��c��>R"�cd��;2������MF�[;�
��9�f^X[C�9�r\��.K�8�C>ON��fp��&0&� ���i�A(	��h�L������#7 J5%��R_���b�������7���?���Bu&����sV&���L��~����(��g}�%�)]���`��Z/��n�J\e
bb�����2�')�t�?~�w�<g^.���Tw�r�2PxI������3R����k0}$��Cor2u��$^�A_��/�@X�b��@h����K���uPY�����  ��  ���A��������!v]2������mk������:o(��hx�j��E'��  ��  '���9~i���x}�;�@�zzH�@����_�s5�[�����������`":)���$�O=��&E��<V���6;^��h��5�?iR���}'�Zt5o�rDG%N�Xl�����I������g�!J
�^��<�n����r^6���q[����@�k��,P�t.GWc�9�^'I������=�J�A(	����������+��.�����Q��|����k�D�������~%�e�b�a�	�=n��^�~�{��������I�u	����P}��X�}�������J4=��8������������Y�s�B��U����x��O��5�����x����*�~�����P�����������EmY��@��w�\��F��[(��o�7K��n)�A@A@N%
%��yx��	ojT_�u�g(�u����k��x�)������x]�f�^�������
E��+�n����8���,k���Y#��A@A@:;�F�k�Dls����~�++����}@ �V�Q�~������
A�p���}�9�6@3��m�Y�oo�=��r����9pd��}�z���)�S�X�  ��  ��  ��  ��  ��  �� �Q:�@��C��@�lu��vaJ���H�b�C��y)����{���������
�v8�P��?w�7xk���e��6��BF=����m��������Qe�D�7�<��W�
o�~���N8����G4h
*q���L��������;�a�"M���ID� 
K�@����]{,���5j�
+�@A�K�4�&Eva���{�����eg�]�2�<O�s�=�s�y��{N����lsstcs���~��s�A�U�������#��������n����<F3�U��]�Vm1u����9x6o���5Ic������2�a�><���/�5�jj��=����o�W_����*MS1^1���Q���lx�-��H�v��x=?��l����`j���3���Ktw.z�N��V��:������?�=<V�.��4_��(�{`������t�
�:��6��;y��������������������4 ���������C�<C��W��C(����TE9�"<�Wc�h��[\+�C�dai���D�;}G��l������&p����Fs;�]�������Gm/
�/ua�����O�u��5�n�7x\Hi4��w`�ni�O���Oxn�#J�������U�a�`��|��/K�SS{���E�H��qE[w�xl�o����������`��������/,�������^l��
�Wx[{��B�����/)m���r*|vm��V���s7/V�x��"��Q2.��I��}E��D����h�������.�	�h9�=�B�X�1���_��������:�B����s0�V��)���g�����,�y���&!1���oG�Z�'��7���Q�^��v��E��=5�Kj�k(��������]���lC��]{��z���������������������@�P;�N�??���=�������lb�s2���������Y�/��������{ai���e��
�&F~ZG�7_�D{��������W��}�tc�
������y<Y���
���p��d~7�}�Wi�|�zB8��~)^���8��*�^��{L�/�[��xb.��^���C��aT��OCa���0����ye+|F�
�9h�~
�~�7�����N�|��/��p���.���J
�f�.��wci�g=�7�ac����s����JN���0��qgbLN���+��mx/t��������it����7z�	�W�<��dw����]Y����B���56&�����\����1o
R�y�	�|������d&wt<��MGD@D@D@D@D@D@D@D@�B���cN:��m�����}���B��9X�>|LI@h����j��5�u��'��vS�7V��RAX��`*������V�����t�]���:r��8��r�����P@������?�t�$����V6������������GT������&����g��������`h��;P����9�u
Vn	U����
sJ��;���^vQe]7��vV��>)�[a����
B��	��c�V4�s�x������[]��,�� L����[}2U��>��<�'���}����k��=�3�c;}���X��lae!��5���/��Q@��V���[{�f����w��}a}ei���(���}}�FZ���F�2������F���T�cd�� ������VJ@X��i@� �������@�L@X�
B	�0�V�jM$ �����c��1��KG_S\A�1��]�>F@���j���zk0�3ZHa�/�Ha���:w�]�]������n1���	���qs�
���_D�Ax|%g�/B7��o�����3Mgd�������w���D��]at��hi��
��-F�� ����/��A(au>�5�V*KM�������(" " " " " " " "pr	�� �����p���6'W^A������e{0 �
�|Ia�i��D�AX��u�w(��%P��'H�v����k��x=�|�O�������Q��_A����o.��x�Ff+3��q�p����@�J��|���u����2�����:yiQ������Dm"%j>�,/���~���cO��
�j��Q	�l<���i_��G����e��o��p��Yn�\�K�c+w^m��S"�T{h|��!&�����������\�2�#'o���5/8���h�[�������'���E�v�����*,����rg�lX���<|�5�F���������4����X�#�9������g��K�w�dr#�b��g-������KId�v��|��h��i��q�\�.������8����c��Q�%p�p3����������k�zz"��������w�B}]>6�G��y^�(q��s��+�E�g��Z.�.��Y���t>w�d�*W'(\mS��^�	��^���G'��hk��STn�l%��
�)G/?Y6?�+��p�`.C�T��LQA�a;z�����V�����++:��X+�b\FTaEg�Aa$ �mHbO����Ugl�:Kz=�)�6�
B�������1>����E��]����
}��^��O%}dIaaq���8��f���0��3i���j��%�x�]:��.M��T*wD[U�V�$ ��@+�9�h�b��{T�	�,�dZ.��8�������9~1u!�����tf�4�����������|pX�G�y>2S5��z�e��-dOe�
��_�U
[55��@������4��X{���|>)�)����U�v" " " " " " " " "P�AX|���C�[�VTA�KgC��9�`��W�9����l�X5���y�
+ �A�_s;q�x;�����'�������kn=�y��0b}�T�����
f,u��[�-n����s��p���0�Lf0���l��b�o�6�T�������4��"��>\{��3��Cc�LzO��g���_/�a/ON�����l�4~90Vt���[
Y���+�%083&��r3���8%��7[���(U[���U�L����Uk
�|�J0�3��K)\*7��3��g���w�� ��0���������L�`�`@g��"������d�|��
>�U������L���:_N����l�~�N��j��=hI���p�yv������Z�d������H����x�0�u�������H�*�Ax�z���`�_��<��q�dt��<z�*��#���`�:7/6����D@D@D@D@D@D@D@D@D�d��
B����E�T�G~����sj������<kPy=Ue���o�����x"��(S�{p^���mu�����p@x�A� ����{����1-��.
�
�B��G������j����'2����>_������^qO^�����,(�zy��|����m�]k���2aT�a842��[I�n����(���T����yGn�y���|���mG�y~,���N�2�;:C�J<��A���s{z,��<}�I�G�s~^�?�G�x~���3��'�#��Q.f��-F��n
7�Y�v6�~fg��v��L���������8�;��*y4:�B@X�V2��IB����p�_Ry��zl)W�������������������&�g�|������T����{�G��!��3
�rd
@(�z�����e��AX��H@}�Xp����w�u��5=^�����9X���95�����O����h�r���B����5�3/F76���x���y�1�Q��&��%�������S�r�F��&����Ua��c��2�DY�
����WN����AfPZAX[[�������CF�4�����(n7���Q�����\R[\CQ�����������gz�V����Z�VG;������m'KnVR`�������mPf������)-���Y�U���^@}6c( ,F���{���lpGn_l9���<��nO��A���WB8����G�.'�(b~��>	V6F���8�) ���ekS�O5����Q��tkt�gMV}EHK�K�
���<$���)x)x����w��W���5X�W��h�����
�M
�[�["��U�uhv�v�
* ����my�����;?���KadY�,���z?a� ������A��:�����Y��b�*X��9	K�x�� u�*5�������an_g����\��D����a`M��g�l�����>�l���F?Y���4����@\�?D�����������������	-��uM�{O���:�kF�*�m�2�P���
> �������?�ra���:l7������?�t�$�q�5!���m�
� <���`� tx���<�������,�Vs��F|z^��l%�j%g��WF�GW�:����O�Odp�Aw���/�����#V��mR�������b�_��x���I%�{%]����Y�L�����11�+�����0�����������c;���\," " " " " " " " 
O n� �M�����!
R,JT(�b��&�>���#��T��a�������:g0��g*Y�
�x|��;&� ����ZA���IOO�;��)�:/:~f*��3�-c�=O���h�g�3������]����ds-����.��1��hS��E��{�0�.0r��Dn�c$�f����a���&w�S�Z^@�g�\69������Y���K��X|Z��s���?��d��O�38���.��R���<��33�P_�q���e��gN�����e�RZew�)��S��N�>��(���F��������,������:�G�3�*X�4q����y&�^���������ko=���F�{���������>r8 �y��d���b	f�>��_0�=/�'�3;t~dd>2�|z_d>:{vx��O.��bt��.v���Tf���{��U��p�g�,i��e=�%>�����n/	/����^D@D@D@D@D@D@D@D@�@�V��3G�i���&���o���
5vGBt�'x�����Q&���OY��|���$��
�jt��h�d��d����:�g�x����<�w�,W�N9:$�M�f�
�������8����[���
�B/,���� ���p8���:��LLk�<b�A�"E����A��u3��>�u�\����m�#�����P���_����#��j�o��U���H����c���{�0�y7Y1�4����N�j�o@��.����#�����B�O��2iQ�3F]Xrv#�����O�q��_N��[�#�9������B8 ������������e�������U[}eB��'�/������L���gq��X�S���t^ [����%" " " " " " " " 
]��3k&)=�0��
U<��8��lP�o�!�%����r�����
�"a����>m~NW���<�v@������vF�A�$�|������TF>RA(�);�A�!���1�����-���_�?_�q��w.V��VfF���MC��
b- ��X<���K�|�5�M�����+Y���)\�|eyx�M��������$p�#^9�$��`��������"/[�T:v���=v��������.>^|�����aV�;����Ghp�{�������xwk����>������:[W:yi��y����UG#��pe3���� p��i���T�_��8�0�Y7�'5���l�k��+o�h"�;4��cp^FZ�X�2Qg��"�<;�N��{�H;��]d��(����������N52xX"o]a&�!�~�\�������@�f.n���V����" " " " " " " " '�@�VB�O�m��������FY���r���Tt�]��aq[���V�?���U�����u�
g'�����	k; ��������Ia�8�:���(V{�����jO� ���T�;��A�+�����D����I�yD@D@D@D@D@D@D@D@D���
���:�R���T�We�T�	���q3�={(��sLw_@���0��/���M0 ���*�b�"t�)�����L����6o�32�MD����cRpS,�0}'J~�=+�[��`�
�&l}Mh�9%��;������;�C� IDATGNY�
������K��{a�p�M�H@�8������������������TU �+��'T;9����S����[���R+;	�lBB������<Y�����X��������
8|Nc������)\�(�J�$" " " " " " " " "PN@BV�����k���,�b�V���I@�����Y��% �[o�[�	Ha�YJO" " " " " " " " "Pg�u�n$a��j��Q���_+B������~CM����
�����������������\[������3��r���������E?m�������������h��
w�
����%��##[����|��h���|nW�-^���c+�t�Afx�W���r�Jiw�K7Rh4�pN:��K�����1�P�k[z�aK�����2�-�;���>���n�;"PcCJV/���������5k��vG�z-�����i%{(j:��WS��%);�A�!���1�����-����'����������������������@( l�)?|D������~��U���p���6�p�p�u�0�]N������u�n���n�;^�6����U8:�����m������hW��)��\�����!Q�
~ ���u��H@�3.$��D��\}���vE<���k���1n��#�5t_���t�v�z`�����w�x�kl��	��a(V{��m�>%gzI�y�����q]k���� b��s�a�}�N����N�����qs���jR
�]��7sn( L��P�KJ�$�?���E���ZL������=�U}60>t.���F�9�;	�.�7��K5$��x+�����S�G3��Q�������/�g(r��������������������@(���h�o����Fc{��H@XsyHq@�1��]���aa6�$ �
(z74��sS��s*��4�#^�~U��YM���l1��h
�����:�m;��^��o������xV�k.�bS���7������mB�����
�{��f$�*;vR��l����r��G� �m�c�h�3z�q�G*�`\��O)$0��P@HRc�Mv�o����"�����6���7�~.��s��m;c�d"������+
�&2����w��������/���z�;>Gw�`h5S�K�������h�;�����&���1��|�>	�ncrj��0&LxG��x���_n���(L��'_���~����w���u�+0-u��/P|�5M!������������������������jB����f
�����fo^*���������k�A�a�t�n�������5{����)��'\	�/�t�����J*���8�2 �_.�������������������������4l������EZ����;����
8��h�%��o1�U�4T����t��!0�`����Z�+�sZ|�,G;�
%��N�����E^�s~����
�-g��h��`�St�c��>��5������C��uZ���Ful���$����DQL�W�v�@����L}�Es��b���0.���@kq
�s���`��sHLFo����(9��Z����8���
p{Q���zz��5���	d���yjR:���?�/��X������S1
�w�f�{~];�O����X;�B=���O?���������-[c0����g�F7���/B��a>%P���S��qt�^�yk�5�1�E.����)�|0�jeP�'����Q�����u;�B;���(���7�����-��7�vl�������S%��Q���|��-c���D��[��O��9�{��f�� ,�������"�}S��.���;��^����h���
���h���g}o��~��0c�����J�3��IB��h�`������Op4{�_/�z�Q����{���6�����,/���\�x��3���0��P_���G�o������LLco�t�M��^@)**i��&���������17���C�kM[��x7��?������)������������fWJ
�flv*�c1����oB����|-z�������������������l�?�\
�����$]3	�������N��p%%����KB�{1���K�������t " " " " " " " " " "?��)���D��ua��"�[nC��M�����	��$�K����������V�7����vl�����KJ����ua���va��6rVa��H?��+n+�1u�� 4-^���v�O)
�*��X��1���h�@���W����clrf$ ��%w���	<?TA������L�a�������/���&#������B��'����p���I�P�i��rm�W�A���SO��q
�����vD�������3'��[L��&��9�a[?y����qgt���w�~
_��m^���Y���7�����\L��$\�W�9��-EE���fL{�8�/������WG~�]����R�;�_C���4���^TH��0mzch���w9���FL�\��������������������4XS�p������������	�^��Z���&���QqNP����
W�A�K��G�uF�RAX��`*W��o��J�	F-��y��$�jy�~,Mzc�pQ( ��?k�<)��
�����7���*��5��!�J*����"V`�Dm�����9y������$�aY�C���-:���l�������r�do=D��]��9/u4T��6:u"������VN���n�z��n��}��P7��m(�i-�A��|��~�g�{�~H��^{���r�����<��W�
����6_���2�D��g!�6oUv�W@����7����u6���Q������
�}�����W�� " " " " " " " "pr;`���Z� t�o/E���6am�{�*��v�����6��TF:�3Lup�c��:�7��Hap��z�hl8���x�
��a��
B	�}C:u&���! |�����wa��+a%���8�_5wU�^��KO" " " " " " " " "pt�����m������4 �m$a�p@x�TVg��p�Ha
�z����AX���w�M���|UA�;|C��1��=�����vM�mHq@h���|+��P�.*�sJj��pa0 �:��v+[S����
�`@�~%����U� ��Y��O����������H���@8 �V��[nG��
��~�QA�u���g�Zae[�������e���X8RAQ�x����.B!
h�a>w��%77���T�>l�#���]������@s#�X�;3g5�G^.����������p��<t���P�H������+>{nj:#
�����[42Z[���&���
4V���f���W���>@(�N30l���[9+-j�������f.���C�}+��-6�L��N7/��f�n��	)��C�E�A/��w��j�s�eg��sc��9)6?��K+��MRY����,��=We��	�3�BW{��~/Nw�q�/�V�k��6&0Ty�>���\�6����Z��?M�3�C�3V67G�K�������^�o�����?���3�$�z�qB�O35W�,��}Hk�S��� �{y+��
=l�C�����6��Z�}b���\��yX���lb|Z�42�o�����|f������\G;�Ja�.��7;Ja���
�v�/� ��q�BL������B+z���	����a����v��*WaJ5�Ga��uya
Uz�L�/��[b|E�#��3����2�%1�.3�f��d�jGPe�})LT��
#�����fe�Kv���M�$�j�\e��#�o��g���9:����_8 �8�����Q}G�>�c�r��\�Uy���"F�T2r�p������)6���:��C�R�5&n=(?�����a{D`W��0{I��x�?
�K@X��!mE@D@D@D@D@D@D@D@�@�3+{���c� �y���<f�c�#�A���sx�U�]fJ*��b!����3+� ta��F%�����rr�TFV�T��xC� ��s��AL��
� �Kc�&/��S���`���tz�>T�p��{IF^���L��1iZ
7�
^\��s���H0�rP��2�mx|;=<�p���3�F�P�U�:�`���';���V0VB���C��L��b1@�z���d���^����������6�����������z]��X�vsB�{�C>^{�����cz��c,M�k���L����v���L������y�_%Ah��;�LX`f�;��Q�3��}~���~�C���xmX$��R@�1��\�����t
>:,X�"�g7v��:���J{�O�x� ���K���o��r�-���'`c����f��(X���$�8d��S��s���P����p�d���������ra}~���~8 <�� ��
�v�&'-�u1��T���x��#>�^�_R���R�s��������������j> ��=���8Ma��I�iRY�������e>��� '��Z2��+��?F��*,���W��f��K;��>*	]�:�W�{UFc������kG����`����=���
une��v���$�J@H��=���ej�YO�2�����%0,z��2~�^��hi." " " " " " " " u$Og�������Z��pRG5�^�)�%M�M�#w�����-y�!��U:1��3k�#	�+^t�~�=���AL�����a�������3OJ����-F;�}B[�~�bt1����yrM�mHs\��l1z��������+�Qs����
���p���������-:���Y���!qY�W�^qv&�w0 ����Z9��{���z���+6����nC�Ok�:������<�C�C�W���{p��%���������t��n~�_���g�-t>�Ym�t�gbXO3g��|fU�5cT?%�N��D����.F�������<����A#��vU�����%��$�4.;#����-�J��Z+��g��n�� t�J�s���4x���A�1 �U	Z����em��D�v��:>�����-R�QiZ-Fi," " " " " " " " �,Pv����C������5������5�&����qoZ���u���O�u��� ����f.���j�����C��+�����=�"*�����a��.��������j��RAYVRAX����N�3Xa�H'{��o��Y����PE��Y�%1�2#�8�Uv�}>���|;F��w^!���{T"��4�-M�bnS#L�b^Q'O@��3-���3&��>���7���@���l���g����)�4��7�X���mn��)���RAX���r7����
����:���8},uzY������-�y6XUxB��A(���y�QAx�-�Ldk�}���8� ��
��Z�
�����15�{�D��4���49�VP^�Q$
$��A-�G4Vh���5�0�c������5�f�;6f}�@�2����0~�y��d�5?��^�y�`xo�V��'�|���*+XWP����*X�d�}.(�N6��q�y���E@D@D@D@D@D@D@D@D�>*� <r4��G���0@��S�sy�$�i�@����5��4�Q�ra���� <��)W��=���
������>���;���h��M��wdKR:4b��2j�[�!Vf/q25����h�)���,���q�p:K��1n��oH`��F2la<On�����5[�< j�y6��y��D�5�.e�&x$ wo
PIn������S���8��n2���|����\FLW�wJ2��G�=�g�S���7N�%R�7����o�2���%��c@'�7?K���o����H@�H`�]VzG������^*b�J��,�\/��*d��D>�l�ep>�:��{x�3����|�|@���)>2/�0�md����&�����3�N��gj��K����������������������4x�x� \s8�%F;�$ie��F��y�a��FBt�d�yF&5IbR���H}���g�����m�3����0O�|�:�AG�+�M���fss���&���;t��A�_� 9�����Y�U������AXM�mUAX����?�g���G�A�_����j��nfVra3\�W�����mPgVv�]�	)|�#����i�3uK����{�xmH���xk����g�U��	c^�fa����iR�n����.�Lv�?/����[�����0lmq1��c=n�Y5TA�$����*��G�:^�c�WG��������-jT@X�:���p���L��i^{�x��H��a�3D{Vc}JS�#���5��K���������rI�F�+9�F�U��E�d���`�H�x(�w=����%��NB]��3c�{]Qg�%|�^����v�
���A*��P	�bkq'���8]������Wc�"7�x�v}�lT�w7q��lL`�T�E�4��v1�;/s���U��33~��K�V��^:k>q����������zk�ZW������*;�mnf���f`�@+�^e�w����AY�67���b����f.a���f�.��q��r}L���u��#�g�p�0#:�������+� ��P��������m�8��O��3���^����>���.��uW�R& �l�|@;������b��FF��0a�������+�����y���-<�`���\��\D@D@D@D@D@D@D@D@�H �*	�������]�@�d�����~�1V���\��/n��!���d+�&��,��N�33����=l��t��x�E"�V���.B9���>RA��a�������3O�h|Co@O��5�
��Za�7��h�� �3�������u\%g6�'���������������������,O�'�<�n1*��U�(�e�Xo�0* ���7�'X���FM���v_��`��)�n�o~�����5�n�!�q��S��w����������+�Qs����
���p����������Y�,�g�����������xa\�J@X�#a �-D@D@D@D@D@D@D@D@D��������6>o g���H@��~���_keF a�R�Y���J`�W6z7��p����N��D��g!�������c1�tJ.���qU�9�������������������@�N��0+?�3�y������]����L�cQ��k$ �xJ@X��h�I@x4�j��@�j>U�5�
�8���������������������T-^$a-�V���oL sI������m��6o� ?,*���E?�m�m1�}���~�3�N���������5�?�*�����h=�J�]�����+����
0�?P����!\��O��u��e�d��������/ex7\���S}62t�Nb����oF���]��������������I��<�S���=1
����>0��
�[�q'������hX���]~�����t$" " " " " " " " " "��nXO�����v������\?�I@9'��E����}�a����v��~�EXR���^'�yo�l7c�v
�1�z�JN�:(Q��"�����l�0u��q�w�������������aF�����j/����E�����Q� IDAT��g?s"��E( T�;
s�L��w�
<=�ua��O��.�M9�f~����
�'��S|����^����p������������)��]����LL����e%m����p�N�_�����/WS��?��j��QIk��y�Xli������3Jp8u|�9����d2�8��o����;E+��6t8I7������kLW��0v��s����Sn��G���#P����"�������������������������-��j6)���Q4k5�{'��K���RO�>�c��;���Fc|�N�����#f@X��U�����k0��s/E�����]�c��(�MP�&����f���#�a ~�z���jj�g�nFMi����Lf�B=RA�K�'8�=����Q+u[SL���/{��>����:��T*�/!�v	���g�Z:��4L�Fa�9�`"���15�������L����0?z���QmI���j~�������������9~7�������������>��VM��:pJ�S0J*����[(��6�����{��`l���s�a�?����h�������7^ q��$\|9�MK���7��CH��\)�(�u�����aL;��w����r����;G��������������������@5Li�����b\5/�Z���P=�Y�>F��w��v�|J�7A�d�/���b
hZ�:=�Z)�21��u�|E�����7.�z��[��������������������������	-����W��|���.Z��k�����2q9���GU��L�����5�_���Q�z���[����4����}��mZ
����7C�3��?�:�]�����=��n��[����.���	��[������i=��a�y~�4�!L��bn��N���0&%����j��X����_O���n����En�:�uT�����m&�z;�����)U��a�b(�-�	d�����i�����|���6���u�H�wo��cs�~�����n�'�W�<����������������������9�)��V�A���1�b�\Xu����Zb�I]B���og.����L@�v���lD��J
�^��I��t������R����,��4G�o����J�>q2�kQg��Z��Tw�C�����C����~w1�-���Z^�$�\�]�o���?�@�����!}��m;��/J�S:��{��TR����y��F��?�?��n��\8���]�}������{�������7G�W������r���sw�.\�����'�I}��Z9��4��x�k���'fM�y�%����qv_r��k������+X��&��HL<���|�5
�g����]�����8PmM����jo*��}��%56:o�P���x���K}$O_��8������{5������}(�����udi>�9x�x��Q�
	�2�g���u"�'~+��"0|PI@X����Wi�[�]G����m2:����r��Gm�?f�����`S���w���4 ,� t/�"j����)��b0�:�T�u�y1��7�%P���L������;s�����R[����]�Va�x&�y����Bf��c(�~�d���g
0�
�����K
� {��>���ict�Q���C��=��X2��Y��D@D@D@D@D@D@D@D@D@N:s��t�C�v�������Jr�������4�M��B�\�Y��j�j�G�w�	�?E���4 <��>f\���T��a��j�������rm
�m������{O��������Y?��A?��'n?j���&)��*����n���4TAhl����(���6��:O����{L8o&����(Ox5�����k��cZ�m���g��*u��
Q\{Sd����1���1�B��
��o3�q��o@�����$����|f������BD@D@D@D@D@D@D@D@��!�	^�tx1����������uo@�����"��oF���Si�P�7-������x�&����m��g�]O���Ia=VF6�
����--�t+�8ja���Y��P�
�&$ ��j�x
�$ <��k�r�(CJG����l{k~G9	�������K��
��E���x���������l1��w�9<�U����5\�������
����Hh��Go1>�P*�m�=�����+����TV8]'g@xl�7xU�z���" " " " " " " " "p2�+�B� ��9/
;��}8 �ua}�����TF���*.���4f��9��0�2����]�����{5^}Nc�B�-Y�����2r���F�i�����rkV���F�6�����*����S/�W���-��lb�]�Jm=��v3~�k����+���)�}e��?����r�a��9zG\W���V������^>���1�������:�P��
�J+��LPXXb�2�<��3�%!j�}<4���]~��y�j�(��$G�)��->G0��3�zxm���{�P_7N�rM��mCc�AX�������8�����(������$�����'SF#" " " " " " " " '�@�U:���C!��p���4�1����OK��-j.|~��P��-.@��k����^R���\��a>/7}	�qmc~�?�*��?RA��p@X���f�z�F$9b5�
�k�`e�5�0�U#o�T���
��0�����,�������H�����=��1O�oK��~
�J@�1�t�e���;G������������WT$ �Y!#O'����[����.���`.�=z]eg�EA�Hh�Q���O�&����g}�V1 �^�b�������.a��������
�"'�p���1|���yxz�A?P�]���Yr~Md@(�����
��|�W����,?s{x���v	���3�7EWz4���������5��7����~�����A��S|<z�	��Q�S���*�oC� �������O$pM��Cy������#��4���3�9������[���B�XO����t��a���	O��V����+�|d�n���K�jQ�c*��mf�#-�����S'?p��I<���ul����K��~��������b������%��`5V��P��)E����w��|� ;��%^�����}��a~<eH" " " " " " " " "p�
�S��MYt[�������o�n������Xd��^�_�u-����Fn��C�����+,Z����u\S&g������<.��LJa�O�TF���+w����{u�|c��H8X�V>�e���������b���	�D�����SA���q��	<XA�������N�sC�#>�?{h��?�����l��P���1U���<1��������p�e�U��p�������wC���7����V���$�J@H��Y��]Y�������5���lehjE_�57���W��@D@D@D@D@D@D@D@D@N�x� �������<2�����i�t�<�R�������GL�������MY|F���Ja}}�"atEOp����wu�>�m���h��Vpx�����9�>�/���5x��6��[V���+oG7Zp.�IB���>X:��{}�}�o�oxo"�R��d�Y7cLm������1u��Rv��#C�����Y~N�9j��
�|a����a�T�R�����'�]�&uU6��
�#�����/��;��'<�hb``��00���.�����|�-��m1k��0F@V�c�,���n�������{����o��X���[�c@�J�J!Do�����.m�fd�2��H��*��}�I+��(� \�m�{5�e�>r�R��7�eu��II�7�w���)�V�[��X�a��6~��F��HVS���Q�-�lh�iQQ�Oi�"�o��������������������TF�U]WJ@��T��I@g��~�m
��g?k6kd�2�;S�*VZ���3����3�Q��)F�����.*)�B��8h�<�> �w���
~�m	�&8�{uhb���l\
��������t(" " " " " " " " u O����4~��b��^�����_}��55���pB�RAXK>�-���L@x1)[_���������
��SA��4����j#c�����[�������6G[*�
��M8��3�Z5RA�������L�U7�L���S�K`l���S$\:r���]��p�b�
����S����O%A^��?��b�Uvf_d���}>`�^��'��fc�&Z1���?F�W�
�X�]����'<P��a
�a����������������������q	�Ua�'��y|fO���q���l1�~Xs��b<��G*�6������|�>�P*k�B0��de�Ia��Q���eN�}���y�A��v�����"�g�J� ���h?�io��U�I�����]�}�ck`t(h�y���G3��a�������	Q��k$�
�H#���r�<,�R��/],M73�-C��O�����"�=�D��f47���.l�`	��,[���;���������	K$lYd�X\�V�����-Zm��q���H�{�V�b��$��l�(�n H�B�:�������L��2��g�����w�w�����8?~�X�[��g���6�>b���������B����B�Z_��(���23������������N��c��9����PV�n���l�]���v^�����~=u>�{>w}a��B+��
/:y�E����]%�[R�v�������.�������/W
! ��B@! ��B@!,��� ��3���\��Lw���c����S�{�+�����\���_��fN��U���5u���'��f�w.�!%<�f:O�G��k�,��NV�I����O�v�wq~����"�shrx�A�@(���P}�� ��OQ���7E����A�������_�=��{�)J^U�a	��W��05y{�=]���������&t��;�d��]7�N'��r�5�n��6<mg�K�q|�:5�->�s�L�3�{�T�f�V���+���"`t��/�E#<n��u�G�������3�C�}*�~m\�@TN�-��u�>��6�e�
x���N�[�^W��IT�yX����"���(�����1��\Bi�
�6�����
����k'��B@! ��B@! �@�	x�W0e7�m�S�tK�5�vro&�D?������2x"=���������w8�����1\,����OZa�_��^'\��WG^��g
9�8��CS;���,����h�?���2���1��U	�-/����x��e�4�:(\1Ie��*�Q����^���7
\����a+�A�����vVns��a���2d����Y���
�\s���������D�Lff�8��@�q ~���E_:8���x��?>`a���X���I t��UN
$��c_�O6y��5���WY��B�l�����.���{�*C���k�������T2Ug��V���,�������9(�^b4�����;����
Lh��f.�:#�\hk��2L! ��B@! ��B@� �������B�}����]&	#�C�n`w�{�A��5'��������$�qs�0��i��o����m�<���E�����)�\t����o�VX@��/a�zjO�@(��&���0��Z�c�q^�A���YE�� �P�����Um������j�{���>$����ac18��V����Z��F! ��B@! ��B@�VL�%9[1�z�^�ae}������*;�����A�=��� �x��`����^#n������L�����.nM=[�����-qW$'! ��B@! ��B@! �hQ����H���x�Q ���UJ4�!b y���
w@2V9H���6�P���R)�$ae�U�:�#B�q���F�W��s1������q�j��JX! ��B@! ��B@! ~"~�a��,z}VT��f���^��u&a]�5�5"zy6������Y���+W�:z(������T��/�aFSD��)(7�� lJ�2�B@! ��B@! ��u#�S�F��������6r�sf:)��V�R�N�z�X���'	���%WzO8
��e�9���C1>�BU����n4S������!���Z���u����:���Q�i�1�^25$5y;���������+/V��hK��t2���R���`1���=z?����K
��cG��w�"_]�2^! ��B@! ��B@! Z	c�A������G�g�������iu1Q�t����
>Wk
���~�T�����P��K��^��s|k]f��[����{�,�^�f���w��4�CGq^1u��P4���<Q#L��!i�����+�
���.�N���^�[�}���QC�y��b���7��n��	���x�	�'���X�_����i3�^��
�5�C��`�Sc�?�����-��
����n&�����@}�bl��`4����1��b�y��>�K�v��V��t�K! ��B@! ��B@! �1!�����U(�b"��Gq�7�
us{���q�'�e
��������.8��6�v���j�����@d,zb?�e��S�p��������Z��V�|Z���~��X�����O���^�:���{1�����z�~�9���(#��s���|��1	h�_�c�ClW�K~�-��1O���.���6���6����	�)��g�I"�$��U8���u1���+t������]E3�a�����T ,}=%O����@k+��5�]S���P�j!����e��?����dg{��(h`�e6Z�\4{�������m�5K! ��B@! ��B@! ��hv�v��O�����T�	9�!��A����
��^w�������7������F�+�)y{1z����rxw�}��L�M :��k��CIKa��p|a��Yy�<�S^���!�����r�;��� �y>����<��X�����!���rTE@��|���<����<��(�Gy>��Q���T���"�za��Q�)Y�g�	LSG`��	��m8��T�za���C�^�1���l0�
���%~�2l E��Rr��.qDL����6��G0��b���f��w��=�BI�&�����������o���i�	���y�4��}���=����0v��9��O��r�tj��4���w~?�C�`�aJ�)J�L������*�� �S�hT�h�5[�G��2u�S_�d~�2�K! ��B@! ��B@! ~"tK4����DCwb�t���Z��S?��L%2��)�_9��=p_F�z�l���Z�����9������$6��z�i��4��C(Y�����0�4m�:�~W�6��]E��Q���z�X����~���P���)X�i�{��. ��KQr����d:�G����~�G��l��c�:=G�S���b?���\��p��\��z���:{�A�G6c�D���������������C�����]~���8m��{�*�T
]1|:�����~%���1��w��#��B@! ��B@! ��B���#:Q4�/8LF�U��<���'}�e��%b��I8��2��0�nA IDAT�����S�O���c�|!��u��p����\Z���L �A����t���vR�{a�|U�[F	����s�?���^�����b�
������1�7��e!p��qt?���BG�)�G���+F��x5*
��/��y��?�4_��|���?F2���w����3�B�	:6�t�����L?�D��%����*�a�{��i���D^�6�rE�������*+�h���'�y�^��SU��[��4c�F�%��I���5L�	yNc��JhV�2�B@! ��B@! ��h���)���m\�$��$�	_���������{��!!��u+�@�q�=�8���V���9X�?���|��R��i����������k a����� ,>�gqY����xc�x������l�Si��<��,���!��t��2B��1��q�J o��Z�m])_tB��o1\�H��������)*��F.a�x��+z���6���a5�R6B�~0�d�B@! ��B@! �@�p��!�z�w_4~�lty����0�S�O�Zf0t���-� lFgd�q6�@�D����gkt���m?>�e{e�RB���<��>Okf.a��d�B@! ��B@! ��D@��?�p�8�q+��f� ��zaz�I���)Y�*1���B#!G*��\SU� t	�8]%F������������1v���Ah/�A�q�$|��&8����/dk&��a��W5B�~����B@! ��B@! ��ME�#&�}�e�O)�����Rb��a��A��6�����;S��Y6���GTu����~gU�
��D���$�@Xc��<�W����Ggt��0}����
Ie7�5�3n��q=���rO��\�����S�]?���i�����:��5���k�����=���-{��lT�_���e�X��W+$��/�S����p���'���
]�2�[���-Oo�Y�D���*OOV�k�E����U�;@���:��Wc�/� ������u�A����:O��Xv:^�r�r��U�^�G�_�
f_��~{�uo��*�7�>�AXE_B�^�%Yc�w����GfT��k���\��2�B@! ��B@! ��9O�z���C%������4@e����0�������e��p[	k:IE�WO3��az;������^�g�0v]e�C��S���N�s��z�r3s�P3Sb�4p�����`_���ol,��$5\ezR(�n����\g����0�����kK;8T�2q���c��_t�9W5\�
�a�A����A��BM��F�k��^AG�z�p��a�\���h,�0�Wpq�cM���9�������%����BM #f�l�X��wQG�c�G�)�JP�2G!������\�(���n���Wy�O�[�����dQE��p�+h�]UY$=�Cc��r�����F�a�}���u6����Z����k�/�r�k'��jj)f���f���������@X��*�! ��B@! ��B@4���{�p���Y�X|g�#=o��/���vNWi`�/"�����o��������wSw�*�^�0\�C�^)�����d�@����S�]ga��a7J�
��.��A���<B���p����.�G'#����%6���f�CL����k��D�'$W
vt�F�����e7k1��^��\Xv�v���j<�^a��*��!�%��,\�1���������/*U�b;|�Zc�6�-O���@�0�
��F*�p����5���s�]�D�	��Vy�r���{���u/�l��~��>G�h������U����`�N��������+�����[a��2NG�i�[g���
��.Sg�c�&x������Z�����z;u*������2�}	��U�w�%F+�?�>�R \�����l�C!��/�r�u>����W���&8��{��B@! ��B@! ��B�M�J����/���0��3��48����ot��@�[�Mo��g�'Z��w���uE,i��+L�Bl��%Fm��*de�v�0xw�+F��;����g-�N�n(��?�~��O 42k��9�tsM:��r�V'��E������$q��a}#���K�y���������<��lH����P6	c���=�5:]�I�����T��
h'����^O?�r�W�*sD#���X ,�O�j|�=�/���u��'WX���$���W���/:�/����o4NV�N�<���6�r2i����������U���_�^w2;�LL�2�/?G�O�
���=�Kl�������z/��w�����B*��
�>����� ���{�����I�_x*��
|��}�K! ��B@! ��B@!P�@�B'�+`Q�P6\n�W�5S�-d�Z���	e�_k'�L�?1j��;��2�u}�y9V����������2���p�]j���~%F�F�DC/���{��6�#y�|W���������������K����{B�{�%8����^����7��z�i��4��C(Y�����P{q~�M���&�D������~	g��Y�=�@��������k a������`��C���9:�r`���eX��R|d��2#q�_<��KW�G�����l�Si��]�p4'�>G��Cq�j��T������5��z��*\#�+L����U�|���2)XN$9���-�������%O����X���U�'V'.�SOl�Z�[�������:��B?>�%4�^�/��w��@��Z:�����`&����
��W�X�����������B@! ��B@! �@���AXV:T�WW��L�N0rI�(o���K}���+G�`����E��+P�S�\_����B�"��<�U#����|�U��3��3X&�
�O��{���������i1��GG���^?k���A����� ��Yy�IUY>0�i&)1Z��{�&�`�a�
�t6�������lo�����k���Da��
3{+D�m	��N�r��T���qV+zK�.{JeZ��O��1����y�����M-��]�{��-Gt6������S���E �|�x! ��B@! ��B@��'P]BW6�|'�s�#������]Cxg��_s���VF��=C��E&��Q��,j �����
\9�W��z�p����6<�]�=f��A�0���A�S�C���oa��{����c9\_��~���vg
���dA�6�_������gy�m�	oYI�e)g���~V�c�0�j�_�����WySO4L�Qjt��Xg��O�z�=���[b��GUf����W$�+�Y������G
���y����q��P�2����8�e=��b�J�4��w��W��Ue�h�z��(�	4���Wb4��l��������Q
fj[b4������H����@g�N7�\$��B@! ��B@! jG�:a��{������������hWo��^^���m���v�uz�W��;��^�Pp�������2���`5a��;^+"��>�X%F]B��L�*� ����U`_��=?i������o�� �y(1��#UB\�����t!7�,!!�
;x�,E�t�_D���|��LG�%v���T���:
����%��
�N�O1��x������������{>aC�����H�V��_��O����9��'�y�]��SX0CeZgq��5���I�	!��2�#��>�W���:������F�p���z��8�a�!���4�Pb�Xe��
#��\���5������������	���aC��-������+&����_:���<=������y%^[��XY �������|8C!���[wi<����<���w�`_���|��Z�eC8k�/A�w�.��o�L���%#)'�6�@���M���0)��%E�:���(�u�n�����x! ��B@! ��B@!�*��v��3f����.w�'h�1+��o+�rK���b������Rp�+t���8X�Gg�ef�~�ac���t��Q��2�A��z�`���`���;_u�g+�����+[K������AxA_�3���K1�����X�g��[B���>�+�B�h���^=�������XpDe�M�����W�,_��/�A�{C�t�~�� �4��
���o����^��^p�=���������� �`��o��vo��U� |���ps
=K�hx{�2����ena���6��"V{g�� ������KK{����Vyty�n�GN&��T��k{+����/*����5�������8���*�M.sV[25Ae��
#��.{���mUd���J�r�F�"n�X8��HaP{Y�����K0�M����4>���^���AXM��(�e�����:���Gd�B@! ��B@! ���@�B�c��@;���u���WY��@�C��AC]o9,x��E�����X�������,�������a��
��F���O��
��1tt+F<���hay=J/����pZw���y��2*���� ��n�8���F�fY�G�&3c�x����{�w�/��3N2,Fnm��TV��{�Qqz������l�����FV�5V����y:���Y�_gk&����0����
���5Oo���}{+<}���r%bk���`�G��qF�T��D���}����	+�y����0�R��v��?C!w����cfW��B_?q����=:7j���gt��6Za���^�dP��+pN��u��mO t��V{�@a-���H��~���{D��Ue�7�R�e���1`=�!�����:+}g?VaZ_��'+\�_����~-n"��B@! ��B@! �B��������v��88Tb�\���]�M����i^��%����w,�f�23e����<U��_���Y����#��UF��1F�������<���5Ww
6����K aV,9�Z�B��&f_��.e��9-���Y�}���b�Nff]h��^�b�����(<%F?$d�O �����_B_-����yz"rt� z��~�*�#��c=R�����9�G4}��D�'$��`��A`����<g	5�������PfB@! ��B@! ��?-�� l}t�%FGG�6������ l����u� ��K�9��u�� �9���@����5����7�����V����=[
�
F����v=�}i��d&! ��B@! ��B@!��%P������������A��'QB��U1�OM ���a%D�U��R�c�����j�%V�S����A]�=Lv��$���M>2F! ��B@! ��B@��	4�@��j����Q����,hM�@X!yD �������@����U����5v�`���s��B@! ��B@! ��B@@����"66�s!~�����[k�<:>���=�8�3�+�	��B����������5������b�����@�N�1��W�����^Jr~,����Am���/i��(�/��*��� ���1�q/;�~S�-_�����Qt"rV�s�h�K`! ��B@! ��B@! ��hQqI�������E��SJ�p�8L�g������JN�=p�B_J��Cz�7��z�i����S�v��?�����^��=�9n��OV��`8Y���|�3g����O31EXQL�1�=0��b��z=$����D�����O <�����k��L����C�:�aXvQ��Qc�?�Z��8o�������W����(_���'�?O��u(���2�B@! ��B@! ��B@��O�:lV�-�?���=G34Oz�x���\V-��TP���J�{�4�~���XS�o5B�M1�a��c�������`0���Q;�b�x����dg���c�8Jf�����a��������}�����kQ;����b��9�"�+�@��A��W�� 4��I�����H%��_q�L�������"�cZ�o��S�����T��$J~u���X>y��;���Uq�>@s� V=TQ���.E�����E���5�a��j�n���A�� B@! ��B@! ��B@!��	h!�q��9%}~�����c
�����S[x��NzJtG�#����2��i� l���s���)��w�X���!�C�y>��Q���*
���|>���|>���|>��C����� ���r?�������~,�������> ����Z��P;�9y�����s��T"FM��&���(�'��TU#Z�$m]VU��
p���n-F�n����8���|�	Cq[���:�B����-�+5�
P5L�	�);0��B��a���L�����'aK;���Ih����Z�
t{py��z0�u��-(�������c�w�gS����Dr�B@! ��B@! ��B@�x�9{���
!�\�8�ls�->�s%A��������h�g1t�xi�i��mB5���+�\Y~�ZG5=�����?��d�<�a�0������L�I7cNY����:�
���(|w���1���i� ��'�Wn�1��vl�8�2B���-8O��z�<+�Fh�������={!��y�'C�S���aKK�vl��
����s�ra��,�t4^G��7��_�A3bN]W��r�B@! ��B@! ��B@�Jzh{
��I�����"6�F���0v��3=Sb����Q|�uT�J,#�U�A��:U�����|-�A��=k�<�� t�^OL���~��pu}��02���8��R��$�����f���Rx�(�b$r����L �����v�u�
�@�n���c�'��H����M�c?P��
4gv]S>������_5��v#��pem�����0Z�@�YG�p��4UO]�#��?���ZRb����e*�/�;�tcV��>���<��<���,QB@! ��B@! ��B��	8:�&7$-��&��%L�`��C�]�=?����!�rp$aJ<?p��������=m�q������������{��Jq�0����|=��C(<���Q�>�X�B��V���1�n����.�AQ�����\	�\a��?]R� �?���h6�������d;X��~n"�DW���W �\������b�1�[���
b	2T! ��B@! ��B@�����d���;r����O���mE<�c}�ZL�2B�2�0I��fh��Pl=�f��AX ����v�G"����hCZ��%a�m�B@! ��B@! ��B@�JZ�@8Y��x�z���A���#�T*1�+�	�9X�'�8��#�0$�$�!�@����8�\%F}=]%F�`z�������>��x�@(B����9����@Xw�-�W�W$W
! ��B@! ��B@!�! ��/1�Z�j�@U�b)%F����j�%F�A�����o;Y�N#
����k���=�Ii�vo	�0��b#��[��r2�V;?N7���T,@�gv.�#,��H�&�\����N���u���\VU�+_c���Ag��}bz�\6��]?W�1�
�2�@�gd�\�eY;,z�����^a�#I;�V5}�Z��-��Y�}[>�r!�[$3����	Q$E���f����cW�{\B�����8������w�`e��5�v}�*\WZb��.$�����l��:��!��C��k��=���W����m&���23��X���-I�~���1��lv��%��s�[���$ IDAT�������s
�d��,[g�v���uy�Iu�c���8���wP� ��/a���Z��;l�c���(n���������{�n�)��B@! ��B@! �@�	T+������Y.��]B�56��?k��X���q�wM���'?����K.�a�-Q�
���������y����7{�z��x�Q���R�����H%�7>���v�@5��j��zzJ��$���\)a��Hp�A�����A������$��~��y�����otp���~n��sU<Z����m��(/z�<���:Y�W�{���S��p^b���5�t���1��e`�jS�:�V�6�U-P �����RY��������G�����?d��3#�H��QFh���	4��������V9��j �p���\^��,���1?�$���fb�CY_Nd#oI��+]��7�g��@XDB��;*�� ��R L��������=o{����B@! ��B@! ��B�u�R ,����),�!�z.����Qx�
��������*~g	����0��H����I/��_����������� l��F��hR�q'�����X#~gdD'O��,z���&��g�����4F<if�
���p�2��01�{��z�@8b��;o3������}�9��s3_�����Sg�J�.�������g�'���aF~���V����O�������,,������mh`�CF���������o6��@a��z�
����===�ZK���,������0�2������w����\�5����8���v<C�����qe{�q��7 �go�-�����9y����mF�N������\���{��c�0?G_�#���(Wmj���c��j�����uR6�1��\F��7+��=3�J ,��'r'����v$�M�:����������@�����~���B@! ��B@! ��B@�'P�@h��A�os�����kpN��C����#s�/�-��9��V.��3�N7���d��L~�����sc=�����IO����^!&����y�x����}N� ��.�K������M� L[U�VX�����Z��/��v����z�JK[�lZP�=�+�v����5��e�����--Z����u��_�S��W��'Yc�V'�NC�a���C���kf�[(�+]Z1n��_G���.3{oW�c=k����,[j��M��z������a������t"�(��gy�W�7r�?�����!��r7f��O�is�F����Rx�������n��5;rY���}�:G�-"���v�wIg&�K��F �ryV��~��
�K�=�M�c����\��+#�p{x.}`�Z��B@! ��B@! ���@�J�w�t�uS~s�_c!*�b�,��a��g���
c����a��]W��&��^��7���m���SK�A����W t��\�#[������'0L�u�:�����)c}�yef���y�R�����������������l�v���}���1�oE=]VN����(��B��u\\i.�PV�R*������;t���n���U'��G_9P�A���`S�R���s�
) ������%,��[�@�������y��0&$�0jp3.�$���Q�s�U�k���
�  �5yN�	y%�E7C<'�&H�q���(��"�_��a �f���K�&�'�G��a���|%R�Z���B@! ��B@! ��B�A	T����Lg;c�e�E�L��n��i>'_5)��#mp�0`��`rj)���$L���S��(>��A8���W-EGk�<�A�����A�,����.���}6�}�2k��Y)��"]�������]g��d��|��/"y!_������~w�
7�@X���[�3�'~E���)�<~GGf�7��` :�'���M/�o�|�{
I�W����HNu@\[V���twi]q6�o\L! ��B@! ��B@� U��$uN.��E��Q����bd��]ygf���Ax��� l�[��A�K��$l��{�i&m��P7���AO�����2�*on62��>����-1z����u��sY?B���v���~���T#+�5����v3{���-}�S ��^	����s*��n��@��7?���s���.1Z�k/%p��"W��+.U,�Y�e��8;=%F����$gP�T#�e�r��'H�%�W��������Uy=���x�.��
9U��^-�����7�K��|a�aP�e�B@! ��B@! ���N�z����N;K�8������q\�-�������+U���|��pY<�k�n���{���a09�D�o�~�T�A?�x�����<��������w��A�q'���s�R#��1��I�<DJt��jlZI0���[�gvn���
o��������_����#S;�E�@����M��`q9�Kt��gg��4�����7�A��&�����7�72����	i?8Y���E��:;m;\�+L1������]���s'���`kv�>�A=���&�K�z���r������tI����sS+����T���a��P��7���[/��S�0���{+>�go�azW#!.�N���"��."dB��}f��;3/:���:2���dP�u�N $������
y�q|������p�f�������3��������Mw��<.�/��&�U���d������i����S�����s��(^{�+���?\�[]�"���NW�ar��hoIQk�n�?�L����0�]�+��B@! ��B@! ��hLU	��7d� '��/�W;�w�%N��W*���!u�qF.�1��N��2�n���;�v���P��&�����H�������m��FA��uW'�S�Yt�'���zr���s_��}�7���q6��G� �z�6���#�^���zbz�6a����!i��z�q��O5=�QO�.�����P�r�A����d��2�����ns����S�$_?B������0O�R�,P����z�~Z������N��@�R�W?Z��O\J��K�KW���-P |/c��fd���3��~�z0�K��Q��z:��������}�j�V+�S	�_�}������ax>���y�W����������f?�R������r�}��A��h���x:�+�6��~�e�B@! ��B@! ��MA�J���$=Q��J_>C�H��]%F]/�^���w���r�����\@`M+�^ �}N-E �� �)�����S��t9�G�*��so~qzw�I��9��8Y�������1��2�b�^�2��^!���&�}�@���m���nv�v���������A8�7*y��|����9�����=at�-w��r'{�+�7Ve�
FfY���A�]���
;�~�Z�����z���d7���B���L�����<X�9*�.��=<�`�+���Wd���ylI�-L���G3���+���;KW����|���7�-���#��kz���&��=�V95�@X�3��|���{�	��r�51<0�R�vWN�V�w�y���9�-^����4zU*��d�G��v�/f�����5��r�����\>��a\�/���k�1��x��h��q-��B@! ��B@! �@c���h��/?�f��
�|����58�3�q�XQ�sr��3��A�w,t;?�)WE2��z�)��]��=��?��K(Sfu��+=Rc�_5	��H������ ���7�H����������	�}�dbz�4��p�G~�sZp�|���tF��~�]�u6�vV���1fk=1k*1�zV���V���9�����B@! ��B@! ��A�a��ex������_�>?}��Ax%�����>'��Y���n��8�g�9���Mj?J���D ���z��
AQb! ��B@! ��B@!��D l�=����"�@�,�p�Vn|��S_jb�_��rx�a-E �Re���������k�,�\"H~~?7��y����/��B@! ��B@! ���/�f3����4>��B�����|��jyMK&a�����=a������������+���,�! ��B@! ��B@!���] l����"6�&���ogL'?�7���!�����4�������Z���f�+F"�~f3���A
�#��C��e �����`Z������,��
����Ma����Y��@��|:�F��� �~��j! ��B@! ��B@! ��h��m{�v�$�U��5'��AQ.��X�52� ��@U�b9�5/������|�C�f����jn�x�kG2��a�<���q�^O�1�������F�a��WV�c�n����?������{/�,��O ���J�	���D��>w�tv�F����H������-X7�+o��~b,�}d#��q��RQ���(W! ��B@! ��B@! �@�%`��
��8O�j���7v�=?Sb����Q^������+O�����A�f�-�Z&��a�;��W��;��6�ts%�>���M�E��;b=��"X�)�pL�I�g�0��7X.�\��P3O��|�SiXv#�I�`K;��FM$7�9�
6�^\�2�^�8"�\O���}�(�yZ���^���A�C�^S��B@! ��B@! ��B@!�Jh�6�t�HI�����������,l%+h�i*�1�0����vL����~B_>�U��! <+�r>�|��!�y>��1������|���|���|��������A���~�����c��X~?�����}@~h���H�c�w�����~�Z�#�FD��qZ����V����B:���]�g�ys�Bqx$@K,�O
��3G����1�-���QU�b���{A!(t@��G9���L�'�,��n���C���������c��~G3j�?����m1������r�wB?c�w�9GP���?=<�b! ��B@! ��B@! ��O��n��?��^��z��{W�����\�jh�9�pAI��LG?{��#c������L������U�nm��;6n�Q� ��)>�k�I"B����t[^�7���_�%����:����E�� �Wd}�G�)!���p&��(!�O��(��n
C�Ec����+���c��d���g�GH�����~v?����Q�����0t�N���P_x��/�2�s�������B@! ��B@! ��B�U��b(�[l�����*����6Xz���(Y��O��:zV*��%��G\T�a��T�{����L��m8J��^2������~�<������WL�t�����E�����K�KE�A�����~���[ T�F����)���.�����~v%������W����;8���c���vi�EEt�y�~�{~��&���S�}����;����������O1O�|�y?�������u���e�! ��B@! ��B@!�lJ��"#*��-��'��,	����?�s����nqX"����v���g�G������Z���d�g~F��'C�e�c���GK����!f�5:qE��,�.$n����g����'���\	�\ahl/B����L t��&��T]B"U�]-�Oug ��Qy�������c��>����L���G��P�
! ��B@! ��B@�s��5�#�S�`AN>�{���a��pb�3�9&�B�3�!�^h��Pl5����AX ��=c��~<���\,��4�-������S! ��B@! ��B@4�b�bx�����~�u��AX�
~qYB_���4�@�r�J�~����
����{���f?U�A�������*-W~-�qE����a=H�����R! ��B@! ��B@�s������e��J��z��q��m����%F�A�w�,/�?��'��Hb�H~}A�D�,�����M���>��!F�d��o�)�����`2SOr�VxqJbO����$�u�5.�{�p}[�,������H>S�Wg����	g�ym�u�0b�)�~^e��m�cp,7�����y�����l;����%���i��c�� �U������*���(`�Y���`��hnIRd6kV�{�?���%$F���x���3('�k�Oce��5�N}K���������s��	�����r[�w^�|E�<�^&k~��|����uLh�#���
N�}s�V���a;`b��Hn���	��?7%��V��r[1)�78�Gfv`V'�C���������.�����Y�[����F��$��9��)�?o�g�+�6&����iQ��K��a���5����f��M����0��<~e��S����
Vv�u�p�\��ypR[����T=����,�6�����X����Q����X�6�����:�r��hf]��	�<q�+���.����;�S����]6��{�����-)��r�'�
��B@! ��B@! ��B�E�V ����ky����������\wM����Q� y�Y�X]�W������3���-���l,��sV^�>���oueR�Y�z����Nb����h��/ h�|��m�qh�w���&&L���;#��}��2�����|�lr�5�8.������qf��{Aq6�-!B�=��a�;���b��|��Y��o/���m��K�f���x�.|>$��`����)�W\^8�yVbCdZk�[������g0��\>��J�8^���^�xeg�	-�I�ks�i�aN>7=z��g��Y��Ygq���l�4���{xg����b��8H��_(�<���y�R���K����<�_:��{�a+�,�����uc��Gz���<�XwI��H�����`+$�����7������������cH2yj0�������J�����"KE�-�c�=����F��+v4��a�
�e1�~��/�	-ik%��<�����W���mi[h������Y-! ��B@! ��B@4 �*��"��E&f��Wq�������6�*����<.vj,��#�����2��&6p���<6�g��>\`�#ykE;&x����YL�[@��:	���'q�T#��&s���5����Im�(�#��������x;�3�^��#�s����������3���'v��������k��}�x��d�gs�9����oKE��J����t��Tx��NL�:�<q����?j��h:���3c[>C�'�Jw�b�][r�h^�5����V���G���)R��,z�+�x/k���{����pVL	{!�H�����3;���n��S���<�����x�I��VW^2���#3���:�	1�����f���f����d�
b;���p�a�TX�R�������2��/�'kf$���s���.��5�t�������\���)v
�����{����<�-I����d���Y�W �fd���<{���Y&4�l:N�7�L��3�.'����m����3�_o^��g+
&v����1�sA��G����;����<�dB�`����=��F$p�*���%F11}f'���]c~!O�����1|�v,#�s�F ��� �����7���M������F��+rVYb��[��N��1�-I�:�YV�l��T�xHf�e�B@! ��B@! ��h������'��u���=�Y��|����3B��-.�P���i������j���D��'_�+��;��C% IDAT�ci'�����+��������A���h�-�	}\��C�������1m�xi���#�@>sg�`Y���\��:X~O����I�0���y'KH^������1�>IW}�8�k0Q�A�G����<��oL�ruG&y���/���N2v��L�����;�r��0�5��8�+��/��*-Z���~`}{�����<>�����k��L����B�7�"j����Zkq����������������.j+J�u�VkQ*b�uCPd_B��@�6I&�Lf�=fK&�$L6��{�A2�:�9���+�����������\N�:dey��mup������C3]��m�A\��y��Z[R����?��������z�����������O	-�n�9�Lm��hw	�_[C�����0��zX���\���a'k�W�d[5��H�)T�m2��o�������4���-_���������eV�-�fF��i�Z9��Q  t
f����Vmx���p����������h��!��
C)�p���I�c$��l�0b9le�+������Y�[�h�F�9��<����$ �w�r���y�<s��m���$�e�g1�$�%	H@��$ 	H@��$ �T����_%�5��mO����ZY[������Ys�@�������<u�V�| �^�}���pU}�`3������A��x(�R���m�g�J��P�	
�s������(��1�����y�UA������UI�;�t��g�����E�������_��:d-�����U�I�D�[.��m�z@KL���Zb���1�S1�K���������E
���0�G��(��}��[����5���v6�GX�L�������Z�<����R�������R��q�h�	<Z�����y2$ l+��!�:����<cV3rb9���71��������B.�yL��G@��C��rf?Q��pK��t����C��g���$fL�g�hs�^s�/��t��U������M���u�k���g���2\���� ��6y��z��>�4+2������.��( $dY�33��1eJ�&&0c`��VB]B7wd�s��51��O�e@c>)��I@��$ 	H@��$ 	H��
���h���C��16�S��������(�)���Y�a��j�p�r��' d���[*����������K�F�\�}K%sn��0��X&�5s��x�=3����g^����[j�2�������HJ�
�ig������h]1U�������GB_e_t`�Po|��Gb��[�0 ����M(+g�������{�\��]������]��;���-����������K�:x�����uw�(��&�2�Ej�Z	�l�6�o����V���am1����H�����K��6��t�mG	#�_)L����R��E�9���p�g�*;zobGZ���Z�n�c��:�12��!,�8���h��QXmg�V�g���w����	Y�,��@���zI@��$ 	H@��$ 	H�h5 ���b7{��c�z;�o���K%������h���}mc��������P��Q���:N��?�:������K��Yx��
�O�;g��5km|���W��J�!����~\��=\�����^C*����������E�y���
�qD��6*spo[A�_���k����V���^����T���
������^��d��������.Hc����C�?!�gZ�����>�%�M
��������>1�5<��A'����X��v�b(����������|[�m}	M���nf���or��.�9�
�����Q�������)����4��_�B����t�UAXbt�P��&�C�6^����������pF���\������A������0�����������
���8}�ki�Q���PtN.�����0���$ 	H@��$ 	H@�@�h3 l>�jO�^�B{����������oY��E����������������a.]`�����H��^8�UL���R������������0��JhX��3l�
�`��|�Sz ����@������{-���I��*C.��� �Z��������oNNaj�3Q��L(��eE���SS6-8x��}�����pO��:�9�bYp� �Hh��? t��������{�����S��[j2)�%��g��p��L���3�9������r+Kw��d�������|{e9�d���&x�v:�����[*Xc�`���f�H.�`��K���n���N���e�"��C��w��'[��B>N��u�b��V�y_6;/�y�	p�����8(�'�������z�]��#GkY�����d��l<��|N���2}����������q&�9NV�w�.�2lN.�����933��g�s�<��W��{Gyx���%P����������?0<X��r����@�b������x��N���v��kY���3nIcr�'�V@��
�g;W^;����7\[���;���������.���k��e9X��/�%i,{|�����Cd/���;����&���[q�����ca���4.���=����u�<\����%0.%p;\�x� ,�k�K��U	����MhXN��$�o�0��f��H
���a?|�0�
�}��	H@��$ 	H@��$ ��+�Z@��+%��J��3c���l����"�����k���&n�y��Cf���2���+s"�x�
q���B��W<��4�k���K��	���� ��D�o�C7�>�����������#��b!�.4����s����c+�����x���4T�y�����1F��������2
B���J��`-4�>����K�F05UF���M���=�g�����=��-
��0�Xoap�|���
��X{z�C�j�����������R�O��O9��k���p�A�$�%g�sJ�)4�d�n�~M�n�}�K6�����~�|Uf�p@��+���f�01$�A���Kw��������\^�����e����<�X����tl��6�>��gs3�(����~N}���x�G��vo]H@X�����pS���_f�����*�8��e0����B�=��s������P�D
{va@vF��k��������C�=S���R���a[�@7�� �:�,������K����C���;���7���p=�$��k��v��:@��$ 	H@��$ 	H��
��*���:�+~3��/,	�Wa��=��/��
.��,\�1�a�v�x���xwWW]W��W���$3�l��UH@�[r4\����_3�����i����0��� �������;�*Cn��VA8gU��E�-�}�����.1�k�pQn3�� ��8�%�����+����R
�d�����
�`�m���*���2����tn���&��n��-a��J��9(�F�sCtU�� �M�Y���%<{���6�K�����?���m�}!�6������[��^`b��D���������W����+xmk�o�>R��:5�9g&s�Hc���6;��}��W[�Qid��T�������$9��>� X!���b������K1s���<xa�����b�G��}k������]ep����������W��J~�����k�Q	����4~vA����RX�����@[���ej*W��������l��
��]��P�������� !wd*W����S����
�L��������kwI&w�n6��j��Z�k��:|�?� �M/������*���@�������x}uM��{�%r��t��j&5�
�W���K�sH13���,���yqE%K��vVWNL����1#%�;V�$ 	H@��$ 	H@��$��Z]b�R�{K�,�����x��rAW|?������\�Y\����m�yV<W_��w�5���7_�R�cK�|UCN����2�fd;�|a-��<���,_��q��d��+�SB�' P����S�[�d��c����g��7��t��]�����������3MO���'qF��1v@���e@�N/5?�@����Q{���WAKL��!����
� ���Q��I�1�L��a:;�9��'�T5�v����+ 	H@��$ 	H@��$ 	�	�v�A�'f���t���Tc@x���� V����=;����0�ss�+��#��0������N���HWSs��$ 	H@��$ 	H@�@$
#Q��6���{}#�]��H��.o����|o[��f�������y�f�% \��^�a&��.����;,�Ex�+�����-����lV����a~��q<8R�1]�@��$ 	H@��$ 	H@'�@O�
�uF���[_�pJDm�N#�=��\U��
�#�-aw�M}vV@agu�$ 	H@��$ 	H@����@O�}��;����;T��g�	��p�l���������������sp����L����_b0��)Z�a
������Tel�`(�U�������0��(����vz���s�i���pF ~���u��% 	H@��$ 	H@��$ 	H�O��r8���O����?�x���>:�T� ����������7����@�����r=�z'q��J���cK.�TqU_��)D����7S�ka������/�jx/y��$���!����������0�S1�K���}atl&�R�qQ���
x
8*w�Z�W�;5n�9��S.����xfN'~�V�e��PGK@��$ 	H@��$ 	H@�@����*����)$�������Y���C���SZ���_���
�h�~o����x�La���X�^J��Op����g
QF�C��2�D��c:�&��08�}�xb�p�g�9T�|k�����#���1�d����q1��/��?�9�Nb����w����O�E�__��~8��wN �_13ga����_p�9��%n�b�������^��$ 	H@��$ 	H@��$�Mp��P�{>��.������E�u_����	��s�2f�|)���@U�*{���TGLj�w��<������<�y������}����>���>���>���>���>���>���>���>���>p"|����_n"�^�s���~��������]M�c
nj���0g�HL�p�Wn��?�����Y����O��<��"�_:1����d��.�]�qu�X���
'�s��Op��"j����[p��`2��d>
��/0�����`[���y��w^ �:�W^��Q�';q���*;��$ 	H@��$ 	H@��$ 	H@���B�
g���Bu%����;(;��.��Oi�I�:"�^����/���k�`,GV�^SB����|�TF4�p����DR�0�s��PZ���a�����op���J�On�4e.w�{^"��E�k�q�����E���;�j����g�?��~��s��D�,�jc)uK���1���	��'���G��>��w�w�3-	H@��$ 	H@��$ 	H@�@��'g���v<�G�>6d%���=E����<�g��s��o���{5vf��^YAh�d-�V��4��Op�9���Lj��:u�E����`��$l?�.�v�����3�%���*[�aX��/ �������8��Dr��U�zo��Scn�`'��W� -����c�c���9�����c�����j�*��C��������$|�l���%}u����_��j���\g:���C�1���}���$ 	H@��$ 	H@��$ ����o�?�[��)��������>SAXs�
��p�/���
W���q�)��lx�4u��=�����n\���%��/��Q�_���w�����w�{��B��c��������C{�U@���o����M��$ 	H@��$ 	H@��$ ���/G�#����*#�4�3�=>p�
B�M?�
[{* l����<BaWj�/	H@��$ 	H@��$ 	H@h��E�����@a����K�6�A�s��������a� �.1�>-��!�8Z�a���
��������{�s( ������g�uv	H@��$ 	H@��$ 	H@~���pLX@�l����jU���_b�*�N^*�e����n�2bb����I&2�{M��<n���eA��M�`[#��Z�
�a����6���!:��b���aAu'� t9y�R�k5��sA���U�q���&)0^�^�an��8�2k�x���U�n__7�K`����;����h
d'O3����G�6J��2�rI$S�2J���ab�<@6g7��G8�v���y6|�,�'������6�d7���G���a��������@ ��bq�G1/R��
���2����`�6��mh�A
�3�0���J�F1oRM�u��ln!��#n4��x�������}����`���R�����u��A\�`�=��^&	H@��$ 	H@��$ 	H�KTA����L�!l��A���N;�X� �%�HN�_�b7p�nI%s�� ���Y@Xh�2��Ni����v�Yy�����i)��j��E��N�MP�h>����:���rP���*�a�3L!���Xf�O��p�?x
���8�N|��p��e��-����a��?r�����n��&�A�<I.�5�W6f
}Ab�+���]!����HQCH����e��IL�h3�$�S����c�/ ,$�k(s����!cT@���C$ 	H@��$ 	H@��$ 	t��*������0���U��L;��\Uf`��D.\VW3�������K53��s��z�Z�V:��Yhe�9���\�����\�[aa^�+=�*�pgUs2�7�H��N/�YYPwL��X����a=�q���1�x(�����)�i,HV���~{��+��$�H"��E9��2_\6��*��c
�v&`��9<+������y����Ao������lry80.�V�J�d�PU^	�r��Y�y5���>"�����T_����}!p����e	�j�f7$�'�L�}m�T�"�<MV�P5�k431�l ��M�K��lk2��y�|���$5P���0Ru���$ 	H@��$ 	H@��$��� �N���Va��q� ����fSM=������z�o�N0r��n�������T0��[�����:y.��!���U�,���x������O��bYj2���.,%����*��;��BZV
��R�%S#��[ou�
���������H�JA�{E��0������XI9+����B�R��W&�34�$ih@��W��'�6[V�{��7��CM�������V~�H�$%d����
��yD�#�~�C�s���$�$#U��j�����t2|#.�d�[	N�f�������hz �ll����g�af����<L!�
#�K�J��$ 	H@��$ 	H@�@�
���{}#�=z+�<@�����Pk�5i0�#+p�)��fN��H��f���0�.�������=/�����������\�a�"l���6����8����%�8
���S��-�e����#��
�\������q�o{��!`cg-�k+�T@X���}�q$$����u( ls������ l�F6p-��;����-C�B�J�,��=05$p�]�}��
.�b�5���������4�s��i}^PJgb		?[�Z.�<����U#yk�6������W���H��F��$ 	H@��$ 	H@�@w4� l�Oy�vu�3x���Y�������h]1U������0PXgdNF<s��d )��fg��j*����
�����5S��A\��d�}�}���0�wf�=����M�3�������+ �������$ 	H@��$ 	H@��$����` IDAT[TA��W"��08�`R�s�WV��4�W^�a�+� T����=��$g�+L@���J�05V�56nYA�������Z.�	t��0��gr��t��}������L���2m��;��an��ry���3�Kx��k�������INx��\����,1�2��/1Z�c�n�ti��m�����
�L?��TAxL"5��$ 	H@��$ 	H@��$p��A��Oy�v�T�����+�^�����
��Ky|*�<w���x^����h���
���We-j�!����p�.�Hb^��$o�����U�,�tP�g��V��� 1����������uhcAE_�:�!V�Y��
��Hbn�8�\feA-�10�{c���+g�sAF����\�/?~9U�zh�7a�=1a���W�VZ�y|�$�s�G9/r�7����2������.O;�'Ii�c���������	d�!�c'�*�F����t	�r��88�������2�9|DF`�/1
�J��d����L�{U�)����bf`�}�=��Bo��� �l#�)D�7�T�S�d��D
�����$ 	H@��$ 	H@���_@��o|�34�{���?Y� �-{�S���v�A�?DG���������L6�dw���m��-��n��
MQd;�6	���[R�\��Y�Qd��::�1`rZ
/��@��;��
E`�'^@�b%��G3sS0���0K�6�?��|���!���v���_��7x���&�����Fvs-5a���$�\��N{B�*"7�H�^������X!�J�v��������$ 	H@��$ 	H@��$ ��h6��Z��08����=���UArw�
B�	=�����*;�Q��aNJs�f{�gw�y���e�.JMF�J����(>8P����un�Zj����}�(&'�rsr,������D@����k�:^���
�f3W��1'>��fO�j[��m�ew���)�,H���}W��)�i*����q��~�r�O2"��v���>n���������[�x�)�lx�l2�I*�'�~2����)�M�@,g��[�l�Lg�B�p��GY�P��;��H�*l���z{��8o5y�����*�����z_
�#����$ 	H@��$ 	H@���K@��%y��T�����}������ZA��DG�ZA��Q��h}����]}H@��$ 	H@��$ 	H@��$���+��S����O���-��3�j������r|� ������=������
�x	H@��$ 	H@��$ 	H@�@�	����,;���o`@��R���o��DV�7�a�����
��	��!�}L���vW��l���;o�Wc����$ 	H@��$ 	H@��$ 	�j�=y~������N����:�l��$ 	H@��$ 	H@��$ ��P@��������[<�]�;7[`�����dO�~��FmQ^�{��cHz�a\�r���L�;�j�<�N'�s!���`K|��D���K��;��8����7Wwz���s1S&�Zb�\':Z��$ 	H@��$ 	H@��$ �.0_u2	�<���c���)�I�����?f��%�T'M���=-75lm����s�'_@m%�I��Y�{M	�w�F�����~q�Id������������X��G�{�?���.������~�/ tfO����q��$���'j�aJ����N�[wN ��_R��
\��t�#-	H@��$ 	H@��$ 	H@�@��^���S�>��<���
	�n�~o����x�La�O�b_Bu���<�����7�J����2$�o_��h)�U��1?��^�������{O�@���`HI=f�a���T���������K��}������~f��w��xor���@��,~x%�W_�k���E����v�!�<��$ 	H@��$ 	H@��$ 	���#��s������
TA��wFHap��V����|�~������z�y���}���>���>���>���>���>���>���>���>��'���^YA��l���8s��
V\��I�����m��x��YAm$f�d��F����Q���
<���D�30���S��`�]U���3���l ��.W=��p�8@���S�n�C%�L�aj���������`�r�}�z0��% 	H@��$ 	H@��$ 	H@�@�K�}��P����!ez�9ED}��9�b{R��<}������Oqm��{T=Q5vjN�#9�2��O���W"��;�?��-����~�Gb���1�qWUQ��-j�$��������2���2��}�c�X~����������Y�-'����8�����YJ@��$ 	H@��$ 	H@��$ �F`x6�f��C��a� t������p����SO����[�����{ea���Z���N�w����o0��x�\e�F��1�rbr�� ������:��C�C��NO���w|aMV&�����i�1����pg}N���0�yKD�R�po����`��~d	I��$ 	H@��$ 	H@��$ �>/��l
�K3a��8��B��U�}��'�g*����Uq��\	����G�Ot��������$�;�]}=����PAXi�Q����h������D�'��^����H��	H@��$ 	H@��$ 	H@��7A�3e�;G���*#�4�3�Q��qU�������6V�!���
B���,�t�
���U���$ 	H@��$ 	H@��$ ��h��*���*��D�9���<���D���� ��
���
�sp��
�{VZ���/j�b������L�'#�^��=SW�&
uCH@��$ 	H@��$ 	H@��N4���w���1+����oB� <�z~>� l�����
B����<ZT��:�I����8l?����T
K���^�����)�5��d�������4�r�(f�C�r�y�P�����q��0h(�
M$��Yc��eL<��x�P��M�;)�/����I�90�c��.����T��o
`R��a+O������)�t�|�a�r &�����pN*9��6}�p��uy�����:|����13�??���1�j�\3d�������2��������L8��Ff1��|�.6>�E�Se��c� ��1��m���0j;�����\]���b�8�>�=9�����$ 	H@��$ 	H@��$ ��,�
���z� l�4�A���v����G������e��M�y��e�YF���4�|o���d�vU�������SV��[k���u%|mk�a$7t ��1�U��|!��cd{&�k:��a�����r�����g&����f�L����
��������f������^�09�O�bR��@h��4++���^�H��F��$ 	H@��$ 	H@��$p����/�*���UAx��nN�iaZ�h�����V�y�������� t�r�f.;`������O�7�r{�����z~�����W���8{�P~���$S �*K������'1/����n�;y	����\���1��9��1o�`n����E{wq��5c��0�i*������'L�/���3�������~kGO|��G����l���l��mg�.o���_N�l����0s�����q��h!?�^���SY��/#��d�
�����,1:JFH�f��$ 	H@��$ 	H@��$ �H@�=1UA��5��
B;�6o�r�rRc���qI�-1�����;Y5b"�m�t��GWl����t���@ ������i������w�D�Q�M�/M����c�2<��!W�9�]W�ek�I7���.m���n��*XZR�2��u��T�I
�����s(��I��C�gm}�C[�A�o>�����?��o��Cl��,���[cFS0:�����hc���}�@��$ 	H@��$ 	H@��$�3� ����B��o�w�Y�������=���;5'��|��#�K�4�������m����	6'	������q��-�^���������?��I������D�'��^����6�p�V{� �}�-��e-[�����cX8}47��
kB*�:r9���7�|��1�jyv�.*�`y�6����a��6<��H�P+�����-��7#	H@��$ 	H@��$ 	H@81�V�����0.�?3n%���zM~�[r���*��,uOa�		�7X��H�f�����\������0�gR�T@�c�D���T9�,1��<kv.��29&
���dg�~[?���/�J@��$ 	H@��$ 	H@���T@�]������ ��Zea���7*����;� ���/���9\�&������{��d�L�����8??�q��@����I,�]�����+��;;7���A,�h���:�����:t��Q�g�*��
��v�T���m��m�5$ ��9t���(�g@Xb4q,GG{�UUv�N�Q��$ 	H@��$ 	H@��$pb
�]A���6*�&�?��W���Wa���� �{9?f��o��b���?
y�����%6������[y4foN���eg��"�<P�����7+�m���q�d�P~�O�7gl�u|QQ���L��4���_S���|�l-!+�D1�,;=���:�@
y�F�eD9Fg��2�����,����L����l�;����<�������-;r�G�+8����*�_�X��#rylPb�<lN;�*,�P�����} v�~����$ 	H@��$ 	H@��$p�	������1�����
�{����y�h�J��.	��p�����=���/j@JYt��[���Wp��=,�o��I�������f�����9Pz�C+Ol���U���|���y@���G?���0����?�������L�?��d��T9Z�p��Y�|��L����8�`g����z��{!* ����#% 	H@��$ 	H@��$ 	H��h� �vr-� l��{������������UwU�Ni�����/��8&�������8������B�R�����e��73m`&?����
M�
���`��"^(��N�01&#������D�|E{�,1��p��-�����GsC\gD�
�l�4Xy����z��n���)��G�i�������vaQ����T�4��G������������bWI1�*�?u9��\>(�2R���Tag�/	H@��$ 	H@��$ 	H@'��*{�z6�'�=��������z��s���E_o������Q��9vd:F��$ 	H@��$ 	H@��$ ���� l�G�� ����qn'��UA������a���3����A&M��������|�����9vt*:N��$ 	H@��$ 	H@��$ �v����d]~�B�m�Y�o3��d�����[�4�e/�k�x'��r��i����0B.5��$ 	H@��$ 	H@��$ 	��
{��* l�|�+���6����C���nU@�E��F��$ 	H@��$ 	H@��$�'��e�3a�GE8��wZ,������#�':c:�q��]UI��%$Xm$,����G��$���2��}N�'���
}��u5�kIY=��7�Dt.5��$ 	H@��$ 	H@��$ 	H@����n�!cV��:��0.�
��s�i���>GCy�gn��;���S\��p��Uc��d��r�Y�z��bFO%a�8���Q?���b������&���k7�_�Or�?��mGQ��{�>McsHz�\�	�t�L�������F��$ 	H@��$ 	H@��$ 	H�Dp�t&�	��T��}!�����\�/���T����e��[����]�
C���q�I��.q�8,�q�]<����D�fa�}�L'**�����N���T?�p�7Tzn�����so!���
�[� ��l�)D�u�]��������$ 	H@��$ 	H@��$ 	H@�@����.����<u�� ��[��
���Z&��$T���>����C�=�|��1������������������9����������������������9���~���~���ze��d#�����p�`�K���@L:�[>�]W��L�[A7b�A����5Do����.|?)�8���%D����/�Y�O�	��1��O����sh5��{a�%��
��S�b�:����Ig�5��������]Tc	H@��$ 	H@��$ 	H@��$��	�'���|n[�A����SVD�'a�:��a'��Y�����=G?'j�*���Tz
�ee��z.�o���&������]r���b�����V�y�gp6u�|���2*�\L�����1S�!������v�s��X����%����3I+��i���@���q�F2<���$ 	H@��$ 	H@��$ 	H@'�@Tf6Q�\���(����R��(����z
��gjB����Uv������qt
Qk?�b�nL�Sp/-�=9���;;��0���x��7G�����������7�{��~4����v��+�
�n����`0�Vw=	���:�$���E���������=�`�5���C�% 	H@��$ 	H@��$ 	H@}L ���J������]�7$ �v�]�n�� 4V�������%�����H����#��=v$��jj>��I�����<���
��S�8]G����&y�^*�d�*����t��2n{2���z����O���$ 	H@��$ 	H@��$ 	�	��\<�&���*#�4����7 ����P��>��� % 	H@��$ 	H@��$ 	H@�@�NVa^�@a� ��C� ��a$��q��]�M� ��!	H@��$ 	H@��$ 	H@�n��
���}��[��8ZK�v��Pa�m/1��
B[���O�J�PC�dr�)&>^ZD��)�p��?��"�_(d�m���Y������;L��������Zy��"^�na�������M��H���O�q���,}dSC�\"���������NeVm1�V��v2�������(sHV�yx;�C���g��;xy�a������8������OchTS�����03uJ��X���h�b�
�L�������iC�{Vj�8������5l��r�w^��2���,f�����K��a�bX�_���Y�s������w�n�x(7
��{[7b��
���g9�y��cTL�0�����,�����:�<"���XN
% 	H@��$ 	H@��$ 	H@�!�
��Pm_�� l���{�RAXo��W�3� ��g�	��&s0����!���,e��|o[[�5������D�Q^G��D%VG��M|���,�1C����1��t3C���`������gi���E\�B!���u> L4�iu4���9i��V��~�pKd����c@��@���A����p���q�9�y������y;`H6��<��F�8���93���|{�����{P��$ 	H@��$ 	H@��$ 	H��TA�U��G�m�uO���n��Up��#x��d2��������������1�����c��Py����
k��4{s'����*�[���]���'17�@G����wv����UGJxpQ>o'���0��s���53�y����r������d~� �>���dtV1��;y�T�����\��\v���k���+*���{�`nO�����������&��=1�E���������f������C$s�r=s IDATn�*����f
f�������\�V�^5�&����ws�?k|����,�.�n+������n*O��W����v�!�#% 	H@��$ 	H@��$ 	H@])�
����X_� l��[*�U<��N��2��W�5Y��R��� �������l�����5���'7T
�Z��p���l9w<��v$ �.m��!M�{p�N�X����wS�����hcU���������2��#\r���\f,1�|^�k��'�!�k= �*����+�����z;��K|z_�F�����u�~����:��=�P�g��W6sr�{;o���{�����h��t����$ 	H@��$ 	H@��$ 	H@�!�
��Pm_����x�������D�]C��=�G�y�A����V�ovaZ�4� ��<�~:f�*���\�B�`�6�pA]�@��l��*�i+���@!�)�����6��
�5 ������LC. ������am�5\�S�
��� ������{+��mC��y��>j$	H@��$ 	H@��$ 	H@��@c@xv�|�]��m�bn���3{M~�[r���*��u;�����e��c
?���`���eB[���B��Tc�y��~��j��9*\��z�����A��$ 	H@��$ 	H@��$p
����/�*�5 SAh����{X��g��[A\�r�h�.N=��[Wm���Y�`���L<p��
�t@A�/�T	�����v��8)��NVn�c�U��mJ��L;_
�zhg/(��
9����z�w����_��u��0�������l��l����f=�`�$ 	H@��$ 	H@��$ 	H��Pa����
�n
�Tb��W7s��T��6���Dc�M�����E,�P��a� ��'.>�����6�����I�$G�������1���I���������1���xjZ����������a��fOb�83�F��b��BZ\��i�Y���{���������,��?S�����`A)�~R����,���SG���4���-���1Y/Wd�������4L@/�{�x��d�����:V}��V��:g��G���v���=��sp�n��g�f�2����y�mv��[x�:�'��&����'���L��$ 	H@��$ 	H@�����*{�b# &��
�{�?�� �LE\�B!��&���:��2�y>���l�7����������Z��p7/7k��n�r;%a��W>�o�wS��v���U�;������L4Qb���0�DL|w�x������0U�������D+�������v����a�v� ���F 	H@��$ 	H@��$ 	H@�; �Ax5����Q��S�':��Wa��n��0p��Gx��b^/�Cz"�M����&"� tVk����xe��=V:$�k�=���y�����e�_^�s{�d�O��Sp�X�6�pK�^wn�CG���:J�8w|�=7���b������>;�#�K�P�8�Y�Bf,�*��u�5`db��tn�v67�x�
��-�
a+x�_�,����x�-��=���o�gG1��-���:J�;�:5�k��2*pM���$ 	H@��$ 	H@��$ 	�f�c�{�-x��[[{�������������^A���q��������n�V� <�c���:��$���bj�N�����NI@��$ 	H@��$ 	H@��$�)��a�<�]����g���I�x�Wa�pwV6?��ba���<S���s87�S����������"~�j��f{�~���������$ 	H@��$ 	H@��$ 	H�;TA��������'��2n{2��-��|O��.b��:��Y���S	���U��so��O�( ���C�J@��$ 	H@��$ 	H@��:(����p]x���mU<����W������xg��\S^�Ntu����"~����<+���wx�9Y����y���3z:V��$ 	H@��$ 	H@��$��
�Z������0�l2�w*p��`?t��3lv���qD>Q?���R�PM�'�7i�t�C�L��k�?0�}H��8�=
m�m7��lU998��Et.5��$ 	H@��$ 	H@��$ 	H@�C�(<&?����u����bn���oH��
���z ��b���qt-Qk�P1v�������x�,+?�P���8h2�e�"��~���G�z�m����c��#H��>��9�v,��U�O������crI�\B���P��s���4\���j$	H@��$ 	H@��$ 	H@��NP�wf�L�!~�����9��(/$ <����-���������Sp�SF������	���q�T��#a�67��.����L�y���`�	��'=�93pN�����5�}�_���B�Y��q�7T��������r !�6�
r0-_B���R�*��i�*�Z�zI@��$ 	H@��$ 	H@��$ �o��!9��x��������^YA���p	���;*Rut��l�/p����B�}.��s@�=��s@�=��s@�=��s@�=���>ze����Q�Q�����t>���`9L��/�ao_�e��I�����r[�u����
�OtT*��oC]5S��/pY�c0�`LGLvm�3�d��}�4�x�J�NI�'�����?��_��S�G-��ZK@��$ 	H@��$ 	H@��$ �H �*�����d/n[51C�����s��o��:�<��=S	T�ZRz|�787b���=z8n�?1g`<2���;%��q;�	9Z�,n���+&:����p����wn�Q_{���$���q��}w��m������w����f���5��}��������S�����$ 	H@��$ 	H@��$ 	H�D0�B�S��S��sSj�O9=��`.������>��#�j9���{�������fp�qm����{
��}���c�9|�Y��6F�O��l�y����jlo�!�q	�����#��;3q�����e�n�$��>�����>�<�su�Q����,0?3�K�;��������|$��=1N(��$ 	H@��$ 	H@��$ 	t�@���a�hDH@��S�g  �q  �����&�r�`a�{s�`�^J��KWp�8�����c�M�q���O������~��5�uV\��
mjF�R[������Z��<�����V@���S��$ 	H@��$ 	H@�����Kr��m:��3�fc�7 <��
�o����C�WW* ����0r��[* ��J-% 	H@��$ 	H@��$ 	H@�	���UAW��j�a������_a�����) ��*��
#�RK	H@��$ 	H@��$ 	H@�@d���=��_b���=#�W�"Yb���<������? |�]��U�RMo9���q�\��Kc�����J�-9��Ykl�����9��&�
6���eA��M.�6�sGb&W��mL���nM1����md�9�9�(>�*%9������,v0�RS���e�5ko�k���lS*�%eqsL�B��x�����V
I��T�g�pU�(������l�T��'��|��LR��������eG�7}��A�1����s���c���]�K�GY������k�aLdf|?��Oh�����,q ���?��^�)���:
�����'%��|��jKx���KtW�`nB\���w-���`�O�eGm������GY�����d2�$�=��� �u7��J��V+�U=j�j�?k-�hk=Zi��.X��RkQ@�VEDE@d���Lf�Y~�l�$��$3Y����������Y�u}���q���M9�uM��k�� � � � � ��@�������}�_����?�������_�4��!F�Q�_�B�r����6���rFhN$�7����z$�M����# t7�kNE�>m��Yg���ag8|KJ��C���0�����z$�}�n,������.���a���r_(�k�t0F�}iK
�.�bt�k����p.��S�vy�b[tS�]�	@��$
�6�����X�=kK
���O'�07u����+��7MS|������q�]�e8@@@@@ �T&��'��a'j���m�v=a���J�C~�{Z�t(/�@3�v=�Xs���,M1�6�,w����r}j�������u�5�!��1L?O�����jWS�~^G���G���qS�����$S0�r{�[�n=������
��1%iF� �fM���b�
�o4���{��ivs��W�j6�&OAT;���#��%_��eiZx�uM���j�^3E���%F�������=�?X3��1���)C�e�����G]c���,����cG>����G�r�la��6��I��~�nJ�\C�V;���:��������)i�=-Cc�m�2�i�� � � � � ��!��q�%�T*;����P
z�x�����l	����G�Z-;j2&m�G8`j(�im���"-ph�>������DGU:t[s85��R�����n���Z?*����C�0�j(�;e��S{�6fd���s���5��e���}���2w���Je��R�����;��I+7�����Y;�h7��5� ���:t�j�.j�Q{f�eJ�"�@@@@@�*@aW�z���=#k���O�g���.�n]\]�r�uhr��JJ���4MONiY�2a���a�f���Cu��a�a(L���nmY�r_�y�jAw�������*����A-Uz�}6:	1����M���D��-�H�e;;��%�k���c�v!m'��6s4W'����h� � � � � �@�Z�A�>�����:�v�o�^���I
�����9PA����;���^�V5:�ic��~z���=���������0Xg-��_B)��`������Wp����EZ������;��N�Pd�n����mh���U��sw�.����)���������% ����X@@@@@��D�
����T�S@���P�_�.���RL��V������H~Z��.��h��h�K�����W�_�����A[�%FwkZ�;��������Lf(`s�����};�Wa�<����F�5���N]\���|iw��C;�>�������c~���~m;�N���ax��/3F�	���Z���;��"� � � � �������ie%�}zk�[�GS��b6�+���k,����:*���K��#��1� X�����L���j_����k��G�I�v��v��mu��~��
���\��&k�~��_u�^������4�>Z�X�!��rM�(U^�0=�nSn������r���PyT@(�#T�g����9���2�r�S������X�[�ZV��\�G�I��4��>z� o�}�nOIRz��W\e���R����8�^��m �&�z"+WS�!�_��j��Q�G\M}��hp_������9��bQnp������u�Tv]�������@@@@@�#@aw�z�X�N\{e�����K�������
���U����0ICLM���v��_R���������\s*J�isoF����lJ�o�vE����������
�@vW����*I�K�Z��b�?��0f�L=�3Xg7w�~����tdR�>mj���0�K6����z��u	O����f�L@@@@@��  �
���I@��W������FO4�h���o|R�9M�-Y�c���������O���Q�o|I:25Ks��������7eZ��Y��MM�1Eg[����#-�5U���2=�n�L�����9�)�-EWN�5���=z��Vy�Xk���n�#m�`�>�����k������qL��uvjfTUaBy��f�nr����|Mi3�������q��m+TJS����*�/K?�e��V�����g��uT�S�Y��d���\]l��{S�����-}��]p��B��=:^>6P!Z�g��,p����L�JI
Wv��@@@@@�^  �U�.5�& ��y9��m�|"��i5��5O"�	{�&���v��&C��~�;a�����b�A�?��5�lij�={2�� �Ic�� � � � � ��h���<�����]���������'�j����?������}�|�k��T�t[�������	�H � � � � ��\�
���%�LB��WA���U��s
��,����V��7;�w��u�����:��0Q��A@@@@@�G�=bK�I��7 l���������Sbn|��8�
 � � � � �=  �!\O����/N7��C�����������=]j��#d^�D^o]��3��d]�y�w�
#��� ����c\��V]�{�|��r���K}q � � � � � p0��7Z���Q���bN���&���F�Ax0���
����-�;F6Y����Z��O�e�������J��D?{#.��cTu���v���vr��X�I�O7o�����dY�4��@@�����T�f����ar���MT�Z�7��K}q � � � � � p0�?p���������9���=* ������k�������i��/��N�N���?�����W���:E�S��Rl�mzM�M�����|���l��g(�5^JIR��'���O��"��d�*�y���
)����q�,�J���n���TN�[�����c����w�xb���+�g�9"� � � � � �1L�s�6g��g��j'��x��
��1i��'���+Q���������x��=�{�����x��=�{�����x�h�������i�|o��!�"���K�d|%2{�J~w73U�|�q�G��78��?������e�0���yje4��i�Z���e�e��?BI���|!�����-�52lZ-C�H���8@@@@@�/�iY�>Mn�6i�����7nT����H�]��S�h\�T�1J~�~1��m��
���#	b�����B��U���b��\%_^����|S.��v���=&CC��E�g�;X�3.�w�Z�?X���y����*R���W��JZ������F�� � � � � � ���#����3/�o��9G�w���=���P����T�}^Y;/T�1��� lNq�`Q(#�j����� �-Y#��iv4�S�?M2�v����*����o�u��Q��s�����������U���u�T�M���G�~g�N};Go����>�K��S�N���,����J��\�������~w�� � � � � ��SN����4���a������]bq0]��������uA��`_�����XT$MU��4\�]_�;6;c�R�n��uH��������V��2r � � � � �t]���1j<dL8 ,!��+�+C��N*�S3Q{j�
��>$ ��s��#	FIC � � � � � ��@��0��h�Ca��3m� ��~;�h�WW��M�9PA�=���
r> � � � � ���@K@��0V@�B��������F-1����<J6��{�QA���
���^����$)IS����#�t��j��������^�-������%zs�[+K������ IDAT
��t�$�~:��c����J��s�-}����<�76�]?%[�4oyMhl�d}g\��x�4���{n��Lw�q��-.m��G�n:����G"*�����������t={{��k���j������V�+#����z���c�������||U��k��f�W�G�u�)9����q�Z���^�,���������C3t�	Y�fl�2��:�kh�n<3OW�����U���VT���Nm�����y���������Q"� � � � ���� ���������������=�UA���U�t�rW��mZp�!��}am�.~�X���B��A������*z
�N���@�I��W
��C[B�m��u�Ku*�9��ah��XZt�uCuca��p@8���UN�l�>C�� �I/.����Bag�O�a�
l^���\-���!�}px�����b
k�����q)�_��
���U��Kd����y]\;+u��+���@L���"-8��f_��_�� � � � � ��PA����=;��#��uc���K�z��s�n��z�v��)��������W�����s��I�������i������*/p|$�����sruuQ�,F�fk��{�B�O��S,��+k5�w%zs�]f����Pe�����m��m*c
�U����������eZ$<�VJ�wg����V
On9{��=��CS�����5������:�9�$�
�~L�n������&�%�+u��+�z� ���]5�x����5�S��}{�&��
�U0"K�����e�_������u�2{�������u�85���z +t]B�J�Z��/*���,�����8:����
#@@@@@@�c��T"���������yw������s����u�������
uz�k���J�%�e'��w������i��On��������}�[�_��>?<�P�`�
��W5���3�������&�.�*):xS8��#�'�#7*��|����~+���Z��k��^�����)��:�r6���uzs�K]>mo^R���w��;�k�a_�P8������\�O:c^m�������:���u�� � � � � �����o���� �����?}�}$��Z����q_s����EE��Tu�X���0�5/�i��)�:8M�GY���deF���$ tW�����f�m
����Q�}hx����/|��j	;
-���Io�^���Z*URd���0:�z�]�.S���m�^�_��2���m�t�3�1����z�����1�n\�Hebg3n�����]��(@@@@@/����pI��U�W���j��R��ye��P)�Ln�>r?C^q8PA��}��
�P��Z��nq��mN�����J�`D��qi�&�������t�f��>7G�����lP����nQ�	][+t����=|j���MJI6�bl<��0X1ijY���UdiO������#��e4*3Y�&�-"� � � � �\T������G�>� ���mk�j�+�����zh��9����Q��(��N���v# \���FRKE]T�m���0��Ct�=z|�
�������R���Z��p�v�qVD-m����N?X,E�\_��%F�������b��l_=�= � � � � ��%@a�b2�
@*�yw&f���J��)����L\5�GQ[�;Wx4��tM�6�\R���-�:��JM���+5��
�4X��dmYzTn=�������K�5�*������z��
�����K�6WN,�?����@��MZ�E��-�����=K�(����`�P=:1I�m�\�F���J�W�jeQt��Yh����[u�*��s���R�suU������S%�{�u����<����PW
{{�Z�~��.wh�����q)�V�������^��X���I��@e����[k5��J=�5�U��[�
W2>�������w�����UR�����d9:[�
��H@��[��@@@@@���1Xb��k��%F�Ui������{Nvh����������+~�����S�Z�z���������u�6�K���&m�l	�t��?������h�h�����*�ZU�i����b����4��I��;��qh���a��$�������7_�����0i�P�V�l]m�ka ��Y���\���	����@@@@@z*@@�S���G@���*�Z�G>������X/�[u��L�8��1�R���U^�yoV����*�%k���z��4e��u���j����1c�u�q���\#��+���z��r��S�6�'i�Q��x�
���S��
c�����+B�����Gf���)Z��6�UW+C�m5����z����w@��������Z=��I���Mc"�``��c�5�_����6JJ4�%�Ma���w�O�������{V��������3��@udHR��Y�^!�� � � � � �@����G�	#k�F����^�H�[���}McB��'K�&d�4�U�������!� � � � � �@_	��yT��>Wc~�*��+k��J9f���W_��W->���%�*;��{��h_=A�  �F@@@@@@`������!a���Q#X��f��v���Q��jd��@���!� � � � � �@�1x��	��.�sT�	�� � � � � ���abw�����Z-��k5d��F���[������v��!�����v�f��U��u��7�-���k�q � � � � � p�8��WS��Js�����-�{�U�}!j����o��\�o��1��b����g�<W�^���S6~�hYH��iE\^�ro� �u�����i���l���xc�pedU�o���i2~��K�q � � � � � p���~��nW8 <\�9U�^���1?+* <��{�o~5���x�3 +���+���
iH����+��aJv?#���T��1���K�7����
�����P�L��a����+����f���|"C�]�S�'�!E�U��9�H��.��?e�b��r��4�G@@@@@b
�3r�=�t5�>T�-�� ���d@V��xJ~�dtq������=�{�����x��=�{�����x��=�{���������X�W�����-��*�1Y�e2�������L5�$����7,x������O�����9�d����
��XS%��������g��� � � � � ���/+KM�O���m���rM��������Kn�4t�K�R��(Y���:R�����_�� ��I������*Y>{R������G��wg������<w�/���J��I����F�/�);S���������� � � � � �|���j��\��"���z���/���J�g��Q"�u�L�T����l�rd��W-Wk`�i��g@V�K>����5��o���������O��}��2��,�v"'��3dMO�y����+�U�!K�i�E�q5�� � � � � � ���h:�45����B���R�Z�/�P����Z�
�s�_7 � 4�]+��O�2��,�Fj������*���-X���+�j��0.>NF@@@@@��H��w��
���Rw~��fW��"��N�y��r��B����*�S1Q{*�
����G2CE@@@@@�*�: |_�;?�
�+5�������6�q�h�a7���C������z����i@@@@@�#O�x5�=&����@@���0� ���2���e�Zb4�|`)�@�f���@Xb�y��*,��C�]ZR�����8���vc�p�%F.���S��4�G��Y��3*M�b���l����T�����Z��������m��ai��x��L���U�2�����un�Son��*�
3Rt�az�S�����A�mv��=M�*������m��u�D��V��^�����GK��P�vmQao���� � � � � ���@�*�?����,��SA�c������Q�)>��v��y��u�=����[��M���FFR�p@Xa�y��m_���ulT@nwD�I[�6�'i��l����m�U�?v���@Z�A�v��&k[yc��m�����@@@@@@`������{�y
��\d���Su�5�qfq�C���^��R���`%`��0��|>-�P�Y;S��t���u���vt��L6H>����B��L��g�u�5�`$xL���#�u]�I�T]^���]����sX8|t4h���-���G��� U��t��j=�nW�Q�P5c����� � � � � � p�	����C���PA��< ��>���R�;�k�%���M��c����F������Q���VX�3��j��:#���,1��RP���N���7�Pwf���h���_y���2]��Rq�u{�F}j�Kgd����P[wVk�'��q�����B�����@@@@@@������/m����[����a��P
�k-�a�
�����t�S��G,Q�c�a���ka�v>�X����&�G��c����@@@@@�@�
�+u�'���mv�O/Rj�$��>/��+d�=J����?�:��Aa��C�+�Pe�Q/�n��)1��5�����O)�7���rM�2e7�Z���0�K�� � � � � � ��T�!v]QA�5�~@��ZsRZp�v�
.���Z::O/
5�a�j��CAf���J���}@QA���� � � � � �T�E$�q>k��>=p�]�ee	\k�O+����/�>>G��n"�> t�t��Z1��w��hD ��������������>YbT��Z^����O����V�,��|�Z��%[}:�Hk0����i���� � � � � ����M	�������g���q6M5���U���r���ks�QS�L����7�����u��.���M��c���wT�y'q: � � � � ��a�_g�8���*o����
ZP����4wh�ni��p8; ����+_����nmp�4uX�~2��#kk5�c�Y@_u�K�nq���Fmp+4�C�:�(Eg��k��y'q: � � � � ��a�_�}��5P;h�|o���,���U������>=_rW%��@#���0a]� � � � � � ��	��AX �s�+u�'�9�mv�O/Rj�$��>/���2�c~��O�tt��Oa��
a���� � � � � �����	���qr: � � � � �T���	���qr: � � � � �T���h@�F���i����"9]q�5�"eI��DH55�\�7a�� � � � � � p x���G�yz����[�������Q�����z�L�����d��;�)�mn���w�Rd�����T�Y�k�(��AO���.I�������?NM��H�^������	��a������ � � � � � �����������BWlS�G���7�9�el��@�����
��M`���Rv������Qg��1TI�~Y��k�_I�d���#���|�3�2������H��=c~�Y��������$Y^~NI�,��Q���8	@@@@@8�|�yj<}��YcdX�e��P{3���rj��=�D5�}Da����@H^�@�����d\�G�G��x��=�{�����x��=�{�����x��=�{ ����R�O>�g��`�6�>If��>V�#��2��%����a@
�� � � � � � �F��%�)�'_��y�|����[�������(���2��\���d��h��c0����x�2����F��bOh��
�H����������jZ���N6��%��%�8B��_K>g���=@@@@@@��H.�7�e\S,�Q#e����z28G�����-�N;^���|��J�2%j�����B#��<��W�����{'���u����� �m�Z�[��7�!+[iM��q��kWX��R�"���l�_��38@@@@@�����x����0e�2l�l��H�j����a��?`�����^� Xk�zV�������4e����O��"c�]{��#dPM������.m#� � � � � �@0l%�1Y�����_�N�����|F�T{���������U����*�V���{:z*7�YA�Y��������N� � � � � �d��pJ��p����L'j��_A8�lC�ob*\A�����L����p\�� � � � � ���@Ka  |��
�9J>�`{N�?�����7�p�b��{�K��as5�[v����_h���>MK��7����m�A��h�����O�>�!M����&-��N��[�������>���b]�,}v�QK���?/sic�I3/J��o�jBF��"f��n}��u_���
|c��J���aV����5�<���gi����[�du�~��CO��J��u���4��Jw���e_�kr��F=xX���1W5�%��&4?�~�� �:1�+�>�G�x�A�m��5��t���T����y��	_:��������[�u��J� �>��UEm&W��Z�nh��^��
c{2s�A@@@@@��������������O�'�����PY�N`�s T���F��TR��7Y?9G���H�F=8�\���}f��1�������OL��O�kBJ�����:e^Xs`���e�'���kL*�W�����,m�]�
zx�m_\�?ih?�`{��^���2]�����b�x5m�O�V{[��1W���5�'�!�WO]]�ax�CNC@@@@@`������.��h�U����c�����mx�v�������a-1	�]�����j�����%��������E����2����J4�O��[����� ��}���C�=7�kRt��l���*��>-��J���F]�X�<9���=������Z��{��<;�a���3�?�X4!� ��z��@���������y�S�������z�!�f-%W�G/�/�
qTn�G�&����a�O��Q������~_�����q�`�M��3�f�5��N�K7|�Ro^���nIi���z�xn���j��� � � � � ��#��]���A���B�/�d����������������n�5�B�?/[;��Q�����*)�
B�����{]�jn-��a@X���
Z��Qk���~���H�Kfjfp���%F�V
���U���K{v���b�&�-=�z��:��9>�G3����������������Jten$����tF�^����]�����iZ�V���+5�6k�B@@@@8�E� IDAT@��������w+K������j�v����JM�i8<T'!`o�����
������:�a�{��v*�Li������[��/ �W��0��F�g�e�Z����t�I
W~z[���"�k���Q � � � � �����p�����<G��ab�<� ����|��X�R�"������,O������lm��%�7`������V�AY��_���)&��FeZcUJ���2��+6e�/�~�4[�
��F�t@@@@@*�x!� �@���K��q&�u��7�~@���B�	�.]yB�V���M����l��fa���?�����������S���1�Z�j��?�[�N��)f�����I�P�{g�,
xh����N�K���Q�mQ�vp����PR��j
���[d���r�56����yG�6 � � � � �[�
�.��Ia�F�f@X����)F�XV���6x����Ud����y@������UGe	�kn�V=Y�+r��gyZ��Ug�,��v]5�(��������o_tkr�{�fh�i����$W�[�e��]a��^��
cC�o�E��s��l^��{��}���A�k��Z��'K��(}a�t�]/���	����^}��C�>T�U��{��������=����F�c�l�C��N�v��^���U~M�u�]�s~��2�F�^mZ��������:Pc�5�eE��5��\����|J�M> � � � � �  QA�����+�8�>:��j��M���ag����= t���t-{"]����]��4������C&_����1��\-�.Y��������T�?�k��Q�'���:S��
�~����m�Vt@(������:T�j�F
�����
�K?-���}c�j���]� ��������O�����n@@@@@��x�#P�[@��
?KV���Z���%�������[4&FfV�q��z���������*)�
B�~5�����wHE���J��{��9�#f��j�C5��U�6��4�<�n���	��5�'�{,���A#�WK��]�6hc�QcNN�U��k����5�u@�6.��]��k���cg����h�}�q�Cx�Ao������w/����-�X��'�3�y�&�NE����Z/���G��@@@@@8`�xi	���u�n��:e^R���������;��c��A���
-[��{&��� � � � � ��+@@�E�.B��a��v���[*���4��a�ff�dr�=Q���&���2�56[;��(3��8@@@@@�N���������P�pXB�����*-j7~����<����2��z��r���9�
.K*���b]�,m�]�
�xn���\c�������h�U�V�i�����|��W)i@@@@@��Q����W����P�pXB�Z���^o~��(���w�c���0�����;���
z��&��NA@@@@@�@  ��>���������������<V_I���@@@@@@��D��'-=J��F�r�/Z��pJ�j�'���d>�Z%O���������
�X
�������G��WV���k���d�n�����k��@@@@@@�X�W~������o�����l}g/�sc��p�����:O�JI/�*��N�?%3����w����x��,��������
1)Y��OT��7J�)2:����m�W?� � � � � � 0 Ly���%��d�d{0 4f����S����]}�������u�U�uK������H�������	�?���<�<�<�<��x��=�{�����x��=�{���A����p���g���|<� � � � � � ����Y2s�U�W�A^��2e���HM��e��Q���Rjt�f=F����������/�?']�cK�I���fKy�2�v��}m���A�D�"����xF��&t�4� � � � � � ��C�d>�;�7���<A��
�����T
:O5.���<�A��*y���J����B#H|�f��S����8�$yr�2,�O�'�y���6Q��EQ-��d��6y�����N{�j����V9���4��Q2,@@@@@@` 
�O9Z*JR��.s���C�Q}�XUn[�A�����8������:�^�@����h(���2��o|��W~W����a������w����@������[�/���L-P����"�n,�����9*�xT�cMu��u���N���dM���Su�a��d��
:h���������-.�H2i��T�v�U�fQ����D� � � � � � 0���I
��M����v�s�}_���Ua���L8���2#�����'�j�,�~�I}[)��*��B�m��&>�O�o
W&.������! ��Y�k��2]��K�7l\��q�U����w����������6=��l���G�@@@@@�]�;�i$tP�{��0�s�qc�� L@�]��8+{�q����+t��J���c�]s�$)3P0��kG�C�������t\�^���������k�/�B�I���{�htj ����n���F��u�����fg��~(nL;M � � � � � �@�F*��o"��+�PA��I��
�9��?\���ND@�cS��|��O/�c�������R���I�]���������S�y�W����/��aM��	v�1)U�������\�_|���;�����I�l��(����^[�����P;����2����Q�7����u�#M�]������_)���6=qU��J
}_��\����<K�[k���N-��jX�M��d��vk�����Z=��3�d�5I���{��itRK��?)���y5��u�=2w�������!������l[��1����z=��C�m8�4zD�n>!S������Z��X�;.�K<uz��:-�������u�?���A�cH�}�?l�����Y������@@@@@@ ,���0���������Ob���
����wm� �f"B���+����f�sQ�������Zg����VaV8�������[�3��\�=���`��5�����Ki�t�. ��5��[�:bj�^���,����UK'����;�`5[*5jq�f�1X��
���9������6�Z�X�����KKt�Wm����0����.��������\�g���a������cY���_��9?�`��IC,��+j�`�h�3)�������]�:���p`<�@@@@@��]��!��� L���QA����	h���=#�t���s34�Z
s���	���	�v��8>Cs�'���lh�3�����L����p���%F��
�Q�^U��'E��N*e�Y'd��GX4,P������Juq�^���I��os��/q�����:�j���
-�Z�H
�����_������J�_;����K:v� =qX��r��e���Q���_O������k��7�4&R	�t����k���N�l1S�.��k�������D*�RAX]�����g#�zlz�N���5e���B���p@(�U��b���J�*�U<���ey��1�i��]�S���3q�-!� � � � � pP
t= �l����Z_�����
��{����YA����+����Uk�����Y��a��4
k�F��.|@u�f?U����������A�mtiyq�j=z7�,g�sD�6��^���%F[�
C����T��66W�EB����7vQ����B��E�t�����f;�����l���f8��������
�m�Y@r0�Z25�����5i�I�^���e8 �0D
��%�U�����Azv\������I#� � � � � �k]� ��
�H���?��h�}��4���}�:	����K�psx+ �h��
]���ai��^���Uv�W���`��1v��G��G�/�d��Q{�Q����2�p�]'��PUar�����4� � � � � ��A(���0PA8=��^��W�={g�� ��A�����
�V�������pu���E�	m�����F����V��a�%��c�01��0�=�o� Ll@(E����R��u��zf\�>>���� � � � � ���@�B*� ���p�;ap��:�b��/+BK��#��������]\�3_�������#������.�����v{! �r���!%E�^,�������m������������}z�kyN��|X�3?p�������)6E���k�CK�&k�����N?�� �6��z}|��\�c����Q@@@@@j���T�W�x`W�����s��L��#{�M.��SS�*������9��a)`�����WThAm���$K��~# �\��K\�=�@�;�l����w7��������}~F�����YA�NY�P�5Uw��k��$eB3�_;JZ�~������
�����yv= �v|Y�I��t�	����)�&n�G�v5h�3E�z��;u���Z�4����u���N
�k�zy]��_�Ri�]o]��#"���Z��ZG����R�o��3kj���.�F��k�i�]o��ch^^�vT��|�W���j�KadYQ���^��ek�9�6{S��g&� � � � � �@�t= ����+{�����}�A�Y�QUp�����4��}{s�`h�Q�NlU����{�6�q1���g����f`�MJsk�=�a`}�5k�t��.�v`1l\��q��U����P�(���7v�����e���j���5��]��gO��i�*,�������Mv��U{��M�h�R�1Gd��7fhR��.�
����V��=�q9@@@@@�)@@��(��+�r����*��V�|a��� /l�Ba�GW\���C�l��W��k�W�z��<]b�jw�]�6��`K�69M=����dj�����-�/\�[�*:��T��=+k���&��St�a��vb��������WFFRS��3�9��N��TK�&����:��t]2��:�����iK�[���]M�0rX�Mg�O��#R5:XQ�q7i��z��-.�4q�	���)�
�v��]Y�G7E������c)�������m.m
���C�t��V�����a�^��9;2�pN�.��+�@@@@@@`_�������[�����~���	���k��^�����#r��4����w`_y]���R=3._����zx�� � � � � ���(@@H@��& <P��r��[���������c��J�Z��{.*�uy�?F� � � � � ��� @@H@H@x�?�MMZ��ZX�U�������������W��fek�9�V{5����@@@@@�Y�������@~����ju�s��p�=<2���y��
@@@@@��W����0v@��c�^������@@@@@@ ��i��d8<GM�����;��~�*���A��*y���B1i�Z����<'�USM��m�,����J��E	u0TV�����0?}~y��*uEB'Kc � � � � � ��+�4��3*���2��/�v��~�����fa8 �<p��^��L~�������&����a}($�M;B��N�q���:*+W������*������Q��!83� � � � � �T�=W����iT���k�N��dk�T���u_J�Q2r�j*�V����
��5�}��r�|�<c2d|�e�l���s�yH_VFn��*�>$�O��S�����y?�Z����}�������>�������}��#����G�>�}�����}���$�������w�hL�q(m�����$ofAK��D��76� � � � � � ����*����rmY'�����!26�G�V�o,%�U��$�[��O�o���|�����V��
.'jH>TJ���xtx����E������������AE����r7��388i�������8
C@@@@@�g����&��K���
�������L�H���M��F��;�{o��S����_-�}t~��7|���N�l��8�h�	� ��=��B�S��!�o�F�K~���z�D����Y�e^��{�3ZG@@@@@8���/��2
_l57�y�L�9\I�|%y��=�����_=?�W��{%S�
�		Wv�|v��~�O>_e'��n�X_�\A�_����������m������m*��@�Qq��F]������k���rm�^v5��&I&�NL5k�-E?�{���@@@@@@��4��d�O�+�pa�F�[.�e�r���&M��p��S�{v��_����\*���~�)�00+��K@��3��G5������+����I*����� � � � � � �@�t= �v��?��;�Iu/u�ZM����=k�e�-1�1�pa��z6��YqV����3KN�T��RS�����
�I�������M�k�K��|:6+]��4)%}� � � � � � �(�P@�����k����dY���
�D�:p�	��� L����
�.��N�Q���y6=����_U;tF�A�I���j����n��_�u��z���L��n���I*
p�O��p��A���j}�G���u�-Y�g������[���j���f��7����ztO���&��C�����!|=TR������P��6�����ZgT[�d�E�F�Y�����~��w������\��4[��e&kti���qe��C�:�����n��F��������s�����N�f�����{41?S����2q79-!� � � � � ����������0g��	� �kyE�����y�l�.9�v�tzq�F�
����|�������4�����`��P���2��?:�l&U���-ma��
�9T	�������/F�5N�F�6jv^Fs@���������������VZtD8��>�h��]W����1��q{]��.�����������b4 � � � � � �@*[cRA�>�6y����Up�J:Y����c}M�f�C;�m�g$p�n7)IwfYtEjx�N�WOW9��z��
J��]_b��i���:�rZ��l�Z���2��,�~�nVQ�B���C��J���������uK�]wZ:��Q���p{�C��{uZ�U������z�K���5&*��M�����lK�b�eY�p������[�D�6��;Z���3���B@@@@@�"@a���=;����a��F�������V��u��@X�TE��������sw;�������]�aMS�9<Z��������e9��4m�IRf�}�

C�n���1-��F��;���� IDAT���6�+ ����Z����-��qG�}ZXZ�k������hd���(6��BV)�;�w������6b � � � ���g����*���_�ggwg�lcYp�M��D��&
������D�C��kHbIQ	�����B�"EB������4a�e����s~�3}w��N[��k����9�����s��^��M
P��.�+[2_�
�" �����(@�P�r�G�T��!�A��{�PA�������T���/|@'U>�u�
��r���f+�hp�?4-���P&�l�]�
�(*5C��^@_����4|�%�J�Uj������E(@
P��(@
P��(@
P����W��;��7������@����H�O��]�T��,��}D�
A��Je�{ e��[!$m��T
E?�����mB�����������b�Z�B�B.LL, �8{&9 �o��z5^)6`�S�r���t�Z���0�;�'R��(@
P��(@
P��(��XA��ca�+=-F����G�&�������-4�	_Ah����s6\���gR��6�_A������R���^��.�z��*�B�7��Z
��H�����iG�k1�MC���/���
B�t�7V4#����lM��H���peW{cq��(@
P��(@
P��(@
$,�
���=YA��
B_P�?�T�4i��t��r+.d��������p K��JoU[���'k�x����n)��	��B+U�0����iJO�fw���b�����7��\-�<�&XA@j3:��2U�_���G�B��K��3'^��a�M���t���K}�
�r`zN:���BD�;\x��OZ ���w�@7�g��tU��.����Kn5��{��* �J��\e2b�gOG~(@
P��(@
P��(@
P���	����/~�*��.z��H�
�+��	�Z�� X1�m7����4����"��Tbf�ce��N,���%w�����r�cMrBi���
V���De���T<��A�����3O��*[��(�
��I��B�����7tthY������)@
P��(@
P��(@
P��jzb��0�]���A���*L����}�l�n��&���.�4���P�g��KV'�r+����=F�B���oT5`BC?i'7�����J�w����������&^A�_~���5��ms�c)w�|4XZ���z��<�����mX�, O���i:�� ��eVllJc���j��'����0��N�i����A?_qb�!�N�+�`OF>��uiW{�q��(@
P��(@
P��(@
D'�����%� ����Qt�V��u�tAo@������<������A���/� g�0�F�=�iX��mu��>v�
��;pWA:~�"t�L��\(@
P��(@
P��(@
P��h���,�_^
��' � ��*8��w|t{�W���f���D0������kzQ�ty�p90G�l�b=�}���P��CDl���G
��M�+;�b��(@
P��(@
P��(@�N,�
��?�%� ��Na@�[G��i!Zk��:���:�P�	9�w�[������v���(@
P��(@
P��(@
P��	0 d@��n����/�
���I����qc�
�0��wa���(@
P��(@
P��(@
tA�������t���S�(@
P��(@
P��(@
P�hO�q�d����X�F�C����aY��!C�����y7��5�k�|
����Sa6�+7w�oj���(@j�R�H[�v�t����7C�)�"��.�x�*���`�(@
P��(@
P��(@
P��V@�ka��A�nAz�v(\��		�k�z��7����������.�yB��G������*�������p�Q��;�D/�O�� �&#9��!8�	O1���d
P��(@
P��(@
P��(@�$�����5��i�PZ�	��s��p���m�������
B��qb����ko(���v@�-	�-�HJa<��,��������2�nt��!��j>7|n�����{�����{�����{�����{�����{����|$�����ho���.�`>�*0v(p���	�����n�7-vu��m����^w����
P��(@
P��(@
P��(@��-�N/FJ�(�>�Q����7p� �\d�C����M����8(��w��Ew=`e��Pw��<w��t������!d��>��y��O���F@���e��� DAj3��.�t����k��&����[����� ��.�����vAh4C��/�U1u�G��B���Bs�o!��(�^����3[�^O�(@
P��(@
P��(@
P��|�n��`�Q���v�W&�>��zXN�Fn��P����ab�\� ��@!�FDR���^���=���/�m��\�������2��r~Muh��5_��f�+������[����5�.({�������U
�5�����b����P��.����CP^���w��5#^�U��������F����j0a���r0-;�x����v)�yM
P��(@
P��(@
P����IPA
m�����,�}@��������uK����w5�[�C{�`�x!=vw��G��8�5������tT�~]0�i�
��jV?���?	c�tR� �n����W* :*�9�����c_��76��� ;n�)�Q�wZW
}�n�B��zL�)
��� LC���u��|c��W�6�a����l���{<p������w�r�G{\���d(@
P��(@
P��(@
P�R�#�SeC{.X=�?�9n"�KW����A�g\�
���������~'�cL���Xkjo�Ju�
���'��G�G�����t/��EH1

8+�v��f��NYA��0�{1�A�W����K��ec�B��j�a��8M
P��(@
P��(@
P�����;l5b����_A(���" pHbab��=�d
VJ���'tB�����I[U����x�3����������'�%FH~@(��h�h����$(@
P��(@
P��(@
|��a��>l��`��P����0����������?G�)4 l�a��
i1�d�����l1��8-[���^*u��0b@Xg��
�X��
��S����b��L_�h�x���G����C��s)����7�a������V�[~����5�:!
��0

���vc��,�a�����D-��4`C��V^�{=��ta_��3���t����I2��y����r��	/ll���.`\v.L��Pm,���u�������g��t;���2�=#b�-���Z���)�9���������o�V'�����{mS����a�d�7��8��/�h���. S�����x�������S7����� ��I����0m�
:���+���Bk�6O��M.���o��=���)��`���p�Rd'^����$�K��������3(@
P��(@
P��(@
P��� l?���������+��_|�wA�Cj1~}�����/Z��K���:��zB��Y�X.�u  ��Yr+��|����}���Q�[_��^t.lZY�%{��_�Oh@h�b�O��.s�i�	o����|�������s|�O?����7���E[����!;���U�0�%�v�h��b��f��m��yxy�6��x��J,?�0�u_#�?f�@��(����p�:��`!6��W�.z���X>�mh?�!<������(@
P��(@
P��(@�DXA��D�;�3��7 ��*3_v����\,l}`4��;�0���G~o��^���nu��A+V����f)�	V�����Sa��������X�'�W1�s�o��*������H�k
�������@-�Y��i����wVc�6���Gg|�n"+���/j[V�M�-���Z����VecR�� �Y������Y���+�v,��
/B�����+�l.�{��lHv��V?_��k���|���@@�9B��W.`�f�����h���f;^}IjU
��v�o�y��/`��X:U��9�:b��R��7�Z;=�<2E�"�bP������hB��a�h%���O?��+Sr����Kc�ET_�c�&;rgf`R6�}{��FjM�`�1"�{����5�>g����&`�Xt:��N����><��(@
P��(@
P��(�!�W&>-�A����� ,�S�)+]���	�G���]@a��4c�!;NYD�v��3DV�������������'=m?��+���'������,�VJ�8�8�q3�n������i�
�o�}��Ot���[�P�y��b!_'�G]�v��eZT�E��?����\l�C��9o��j���y�9C�&Dj�����;��M����L>�Ta:�|V%^���~��^�[�������N@�w��3�o�U���1�+'�|�y:(@
P��(@
P��(@
P 1V�.Q������j��������-� �_tT�\�[�*-��A�~�@�1Wh1t����c`���l�����:���MQ���0N��{��0L��m�~g?�����n�V�@H��ad����:-�GU. Tc�X=�OJ���!m��+�����B,��u��*t�������"��+��-����+���
��1�W
��a�o%���}��B����r#�hoU��^����>���<��(@
P��(@
P��(�,��+�!�+�����pE�������_AT����s�"�����Y��&�� �=��<����]�p��
��p`��v<�D5���`.�����^hK���6X�R�t�\+��
]���y<~P�Y�20k��Z�
�@�`�00B�vQ�6{-�<��8 �fk7;��3~i����8t0
���%���
���|���lly��"�C��0{Ch��i1�Y�g�����K�9=�h7� S���79��(@
P��(@
P��(@
� �
� �%� �O"\%a���-F�=�� �{�SA(wcY�X�T��������e��jm��XB'^����0m�=l]A����<|~��gb�G.h��5~R�k������Z��JZ��D��>�&����b�j������hZ�^��w�jY���|g
����Yu�##��g����~���[���;�����Uby�>��=�;��W��`R� ��;�����������#�gd�N<��(@
P��(@
P��(�Q����W��������D������b��R�������tL�A�Tq'}lNlx�?��<�E{R���l�()��k��5�7.k~b@��=�S��#V����h���r0-[8��P��u�q���i?1����+�Ulz��7�Q����\��\#^z��1R�G���S��������n1��knw_#V.J�x���g=,������'�G�����#UB��W.`�f���C�|6f;^}�����>Z��K���]��m��;��t���od��R���E��jLy��Iwf������`��p��
k�tX:Y����7��zZ
�������x�s�l�0_@�}4~\�k���4�����1>�{o����Z
�����}���p��b��yxf��E@�P@a\���m+*���F�Y��$U��1�;��S��(@
P��(@
P���- L�j��'? �o�����k�AxhCf���Ua�����X��r����@o�N�n5�
C�*q��:��@�K7cS�}�(:�By  �n���w�Y���Z:��> ��C�0{}��T(*��������
#�z{^��
g���]���
K���}k����.���F���i1�}�s��F���C�!<o��j�;t��%�a�q
������0����<��(@
P��(@
P��(��� v����p�lg�h�l�R�htb�N6~l���.j���I��5�z?�]�?��x��+���y?���b�Z�������=6��Sc������������R%Y�+�4b�'NTh1c\:�|[���TbyH@(����z��i~*���YS�1Kk���b[�58����V���IMb@(]�������c�]�MR1��4���TD��cub�;MX��������<1
s��Qd���>n�+[�����w��7�`�H=zyQ-U��g���v�uM�:���9�NA/�XQ�W^���M����0o�����7��/��5l���-&���e7i��GU��{�kn����j��F����1R5&?�(@
P��(@
P��(@�N �
���p	+��{���������k���P�{&6u�M��W�i�������\l�#��/_���(@
P��(@
P��.�B����1��/G���,~:sz^�+��(@
P��(@
P��(@
P��
0 d@������H{��\��_cGO��w���9����-KRQ�����(@
P��(@
P��(@��^��p�������o���E�B��L��h��S�����1=7<���4��_�c����t��Z�(@
P��(@
P��(@�N%���*�s���s�D�������P���>�����K��p�E��vL��o��kM�
Q�����h�z���nBi?o�W��1dM�
e�!���Ga6�+���'�{��z���7�9�d
P��(@
P��(@
P��(@��)����M�m&h�n��_Cn��P�����@t�j|��[�~�c(��-�oN��R�#+�G� ���CT���4
{��A�?x�����?�k*������j�8���ea��[V��ZAX`��5���\��x"(@
P��(@
P��(@
P��@'Pj���J����@���${���Mg��N@�~~��bO&������bT@N�/`��O��w��3�!*52a�k�E����z_���
�(�d���W���E���w�
����
E�W�`7�k[����(@
P��(@
P��(@
P��:��&��}o��x5D���t��v����zA1|*��Oa9���+%#�+��N^L]E�a/�B����6@s�S8]��A�z���D*�Th�0�Z��� 
@��>k02��ujQ��%����d�_o�L:�>�s���|�=���|�=���|�=���|�=���|�=�����{ )�p���P
w~Q	W]9���P(5m�p�o��4jM�7JM��]��mp�U��q�����3
 ;i��iQ��(@
P��(@
P��(@
�P]�
�������P������p���"JU/8�z�����P��������3;GS��Q��n� �A�m3-�)�������H�<0��>��bU5�E~
����h>��rUpB�����}��Z!�����W����*��������=H���G>�s�(�������J(�Z�6+��-��dTn;����m�����7�v�����-79a}sW����(@
P��(@
P��(@
P��O@?q4T7t�2'��Sp�>LR����������n�U��
��3���Ba�^�:q�O�z�_���^�;;`��u���O����S"v����� IDATj ����8�A��/�>�R�+
���_� ?�`����IP�wG��Uj�|����
����H�yU�����_�-�	��E����M{�B���0�xl��������UW�����Fc����C
P��(@
P��(@
P������!:(��b����%�����&p�vN�����=������T��������;h^���L����QU�B���h�j;�W��B�9$����S�&CUxK�s�U�}��D��v���Cj�+�\wZfRV<��4��=�?j�6����l�i�#X`������>���x�oL2s>X~w�[�{�?����!�1?�;���m�sa��/����%H9I
P��(@
P��(@
P��:\@{�h����pw�k�>�l��������asQ�=h,Oh|������
B���h^�:�������c/T��Ri`.��!���g��0���]��hb���{c�S7��c]�x�o���7�W�)@
P��(@
P��(@
P������+��.3L@8��_�v���%1 |����s�����?h~S+[U�������j]���R]n�I��$? ��Pt-FYA�
���L
P��(@
P��(@
P��@�� ��j�o����QT�����b��
�-.��"�T��WzZe�&����k�����1z�q��XSZ���M8���e��n9�3[��U���mj���{�.������;����?n|QU�U���ht�!7�,�@���'�f������q�bXn:n�5�f�:�pN��U�g2�a�)3����= ��m���������&���{�ECaX�W�S��W��a����-5�q�<���eS�������(@
P��(@
P��(��XA�YA����P. �M��
���p�1e
T����z,���m��a�%x����V<�4�5�<k�@l<q��]mJ���A��	�D|Q�5f�Ze�+�����Ke���5�B@8�Q��?���6!������.�j��)@
P��(@
P��(@
P�'�
�K^A�]��������������-@{�#8�� *�\z)=��B��h����rhKf@��C��PjC��x�Z4�ZG����F�|��7������;��z#������Z��ez>n������bv�B�<G��;,X}ZjU
����?�b�7w��Sa��>���
uXfM��n�U����6�1����T'��EM%��0��?,x�������0�(�%���Y��\��`�(���N�]��S���8ZW����k���8�Z��_-�l{\��6�>|�h�a������<��_���
��|KR��(@
P��(@
P��(�
���Axu��U;�����I��pl���y���uJ��M����V��7��E��pT�����)�f) L��vC{�c8�� *�\z0$ lF���@[2*�
���R������+�L�E���������(��	�����+�nqj�RP:�V�y+p���I�J��^n1>�ml�z�3��=�G�r�8����X����J�`������9��T������-�XV]�I'��O,�
sP�	�P]������1L�!���(@
P��(@
P��(@��#�
�NSA��id*=	k��=���0\�? WA�����������4������� -d��i�G=�����+L���VUy�c�b�)��1t��{��
i�ZV]�;N��j�6�0<��������T2z>�mM��M�cXf*����AZ�A]��P���[-�3���

�����"�L)@
P��(@
P��(@
P��^ �
�1�A������.N������SA~|o�P���f������NJS�;l�]o�'
������6�d,��r|{z[��ll��z+�6Z�i�����~%�]�l�*U���_F�ug����#��</�*�
�,����&�o�����a�����m�V���4x$(@
P��(@
P��(@
P�][��������"������=�-F��=�-F�{�[��\��b4�=�<���>�=q����-F]M���3XM���+p�G���Q>�eU��t����cY��2���~��Q�
W�`�����c�w!M�M�`��GNcY�x����px1��Z^'�=��a%,5�q�<r{��������_G�H<��(@
P��(@
P��(@��B �
�����*���R��'��p+������C7S����������j�o��^����W����K{z?v��|{���K�>��
B)X����W������I�i�iwX��t����mRw�.7w��SaR�8�P�e���f��������$���jo�������&��4�w�z���g�\[h�X�:�a"��0�h�������������Xp����x������Z����}�\��v�
o�)��U���7���)����1�w_�)W�" M$ �v�K�Wm���u�m���U�-�k+�P��tap��x!7\������EP��(@
P��(@
P��( #�
�NQA�O�����Gp�WBJ�+�P�+�C[2*���P
���V}��k/^�'A����������V\�W���,��(.��"���������%������i�M���<l��i1z���(�;H��}{�O9����nZ�
�����D[-������}��? �/$mj1�EC#t���3�tR:&��jtr�- ���(@
P��(@
P��(@
P�����p���� �3�
T �<?�����f}HaJ�<,$�oB1������+/��}C+}����5�Ux����N�{Zn���;�u0�~�\vl�P���j���0%/w���=r�q�l�������/�;��|#n�JCo�q"���RU=v�Y<�5R1!'w�}�p����o�����@�a9i�9?���S5��q�)�Sa�)�kh������4������aE�.�~tD1�����

7V\���^��iF��U������q6`��R�-�������+r��(@
P��(@
P��(@��\�����0��,��H����=5m�h>����E�@���0���=�
{�!���p�;���xzg�7Tb���-���3��s�(@
P��(@
P��(@
P�#2 ��_;�9�$c����1�g
���I&�iP��(@
P��(@
P��(p2 d@x�Kz)�����/����^�	���(@
P��(@
P��(@
\
��< �&�oR���0�?�|��=���GAh.=���WA��Z�Z�|l9�%3�r����(C�!���Z4�Z����)�H����$f�M��Q�0��6�i�(@
P��(@
P��(@
P��^ ����V9U�Z�\��o/��P��9�����m���5���������S"^_������B���p��<�"\��C����[~u�D�
o�8~��Oa6o�d�$i���o�n�N�_u-�+?KlN<��(@
P��(@
P��(@
P�S
h��#����O�m��7C��"����+�Y�O&4�b�7 �]�������?����Zql���[���7r@�w��������*�%��+�f����=��9xd�
��P�m*�`��	�;w �G����O��4��0�H
P��(@
P��(@
P��(�	t��"�>�(�LR�
�����[�q�W�
��y���?�OW�
$����2��R�}�6X������{P���WA���
B) ����BY���\M>�E�B,�SA~|o������������
Y?�Z�0X^~��'1�"��]�H
P��(@
P��(@
P��(�u��H�3
W9T9��������8��h4��s�q��o��
BiA����7����'*�P����{�~l��<��W��}�/�A��B�; �
�}p/X��5P�|��
��o����XP�_�+��5����h�]�z���}���mOo>|.�\���{�����{�����{�����{�����{�3��RA(:�P����[�����p�����P�a��@��*h��2!��Ia��Z�wCi�B=�}o��	���-
 ��oy (@
P��(@
P��(@
P��@��S��!�����A��8���mn����A}�o>Qa��` T�>6EM-���M�����@�f$�������pwB��@	��qP�����B��J����#�i��:bf�xT�st/o�P����c/���=�OnE�����>c��Z�<����Q?z1t?�u���@?���&;B�!h��ef[w@��C�<=��n����q/�'R��(@
P��(@
P��(@
t>��������������-������`g��t,W�	�v�o���s,��=����}`)�c8���������e���1P�q������q���2h>y�����K��8v~�\-m�b8~2
�[nj��E[���
B���>c��'���G!��?$ 4B�5�
�TA�~g+�A?w<�>���3�b�s_�S�P����'��ss�����%�<7yx%M
P��(@
P��(@
P��@�0�<�VT������,B�����a�/ l[A��UiX
��J������3�����(�TA\��s�P��O$t��_���H�����0��}����?Kj1�2 �
[�@4�p���@v����p4��B��� 9��p�����l-o��g`fz.�����Rd�NV�'3����U����R�1�X�{Rt�%}=at�qw�il
`��=K7l����1����
=^& �_7544L�����u(@
P��(@
P��(@
P��E ��^hlp�������w"��XG�
U�������s��6G}|����E�' L�)����+�x�C��P���P)�	]G>����V���VAK�]t:�
Bo@�Y�ap�yi%�l�F��9�NLVXm@�����gf��		&���`�(@
P��(@
P��(@
��. ������	�&) �~S+[T�vW#V�O�)�s���i�*��\���r���T�o��ux��4^�gbU����N�r�
���P�B��M��Ep�%(@
P��(@
P��(@
P��Y ����,+�--[����Y���g�'�Ef��
O5Tb���J�0*-?5�a�64@�V�s8�pO5���[�������������wn�o(�S�&|i��@��$Cd�b\��2|a���{��M'1�����>����J�I�jy��o���=�g=�UMu��p�������1W2�XB��am��@���ZS�q��V�9@O��7�����A�J+��6�1��x(@
P��(@
P��(@
P�I �������
YA���B�L�(��]�g-�1���
�Z|R�pa?<�S������'6b�3x��y%���}�d�U.^���\l�����X��sE�0�_\a����=4�Yg��<�mU��%��g�BO�Lh�������>�e(@
P��(@
P��(@
P��B ����eUn�4<��Wt
P�~�3
��[���
d}�a:�^C�\O(3Cx���� l
�w6`e�I<g7x�#u=~U~/�s�*�3=�*m����Q�7�w�8i���|�3v���R���cowY��������^�U���Q������x���h[W��SAX�<}>~�]��Ztp���V�1�4��|������;�M��������'�-�d��g��W�
�sIm1�[��P��(@
P��(@
P��(�@t�TAx]��U��K;��� \��
hv$k����-��'�������=��,��}D���}�@am���&����6�j!���#��1�U'd&���LwT��33h.C�3(����a����FU[�p�U���� K,����6m+�T������M;��a��[�w����;����r4&����i-��' �Z����Pl�g����v��*!���3�3���'�y*4}k2�����<��(@
P��(@
P��(@���. d��tp��G�WF�o�? ����������BF�q[z!����8�{��
��/���w��}��*�Bo�Ur�+�O�	k�����C�XBC����~�
���y��2k�2!_�`�i^�* }U��e
����{[qd
P��(@
P��(@
P���"]@�a�������iy~�� ���"��P~}�����w�
B�=C~��z��1���g�*�z���S.T������u_aj�3M�����3��$U��O����#n���c��;����+=�]������t{���������������(@
P��(@
P��(@
P��YA(�w`�����E���-F�����b4x=oa<{��+���078�N���(J�yn6��>�_wS�
���n���xBo��F��X�����U��==�[�\�Q<e���9��f���be�Q<���1��
���k_�����A��z<�������7��2[�t���CR��(@
P��(@
P��(��������4��q*����[������c��,+!��W�'�����r�c�V��N@��UU`;BC��Z��B9U!����q�=������ab���z�v������:����h���dH��������r:���3������]����D77W���
lr�������|�w��(&�Z�#�'Vge�J���vW=��>��lN����~��6�������i\������yn`��~�	=m<��(@
P��(@
P��(@��- L�4���BQmNx`��;DQ�i9EP��y�o�s+ �}����,���w���>�V�
"�����W(3'��Pb8k9��UfT�1Im��Q��� ���s���g0����L��V�a�k�R1�n���B)�������+�
K0S��H��,���j�.
zh��NgBa��v�)/��M���:��:��0������
m{�C�3(@
P��(@
P��(@
P�����������Z�K����TV@�ci+��Z���!{������<+}�p��
O5Tb���J�0*-?5�a�����i��P
�lx���5��+���&<��
��@��
O�/`���<m�����t%��9��	����Xp�4�@hH(�+K�����N
�21�X�I��UU�X@(]StbO�9�li�&��J���Lc>��m[x��I$ �l����p��
�0
�km�k��/2^��(@
P��(@
P��(@�����'�>���p������:���q��'^�g�N`T5�\Q��\���(@
P��(@
P��(@
P�����X��[���^���G���7��2�V6^���)@
P��(@
P��(@
P�]W�a�����x�e.`�U`��j�.�����|�\(@
P��(@
P��(@
P��`@���A�V�nj��_���mn�^t
P���3
���`k��.�z��.����p~<4�F����;�Pf������f$*! IDAT��W��^�G~���T}w;���n����z.��(@
P��(@
P��(��R�+Ac��s��f)=�p'R��u���uq�W��/���PT�f�[�CU�,�����r �����8W���P)���7 ���h�a|�^��A��9��C;c$2�`~�����^���o�����N
�c�&���)@
P��(@
P��(@
P��:��aE_4�:Q��m@���������������hn_eiE�c���(aY "�[o(+Nz���s�P�~j�>�����@�sS`��a|C+�f�����*��1�����+k!l�a�>��~?�����V��(@
P��(@
P��(@
P��:���� u�4�G���p�
u��G��zC]��U@{~�� ���T�����GB>��1pl>�C� L�5�S����3y*���������#��o�#����P~}�����wx���`��+�
�������<=
���������(���P��(@
P��(@
P��(@
ti���P�l
t�����	�������CG���w}��J�J7uh�d�^�w(��ECSL��Vq��p�?�q�y��*S����]�wB,�� T��>�o(N�����{�n���u_��1M=ppHa���w��t������/x_��h��-�>|.�\�=���|�=���|�=���|�=���|�=����U:�!
.(���8E��p�	������}z(NE��3z��	W]
pE 
5����]Y���Be�@�3�v�g�;N�(�/�Y�(@
P��(@
P��(@
P��Z@�.FJ�Hh>x�3����n:���6BJ	�6~4��6��E�I���L�RA����j/Dwl�}R��4��2c T�����a�h����Q���=s@��g ZNK�m-�B(3�@a�n7DC*\�}�~4�����x!G'VA��At5Ca��������ZO@�8��Bt�?����q��7<K����p�6B����R�Tob��4�����<��~�G����d���x"(@
P��(@
P��(@
P��@�0�\���F(��G����(0Ig��P�n3�o��������{/y��S���WCtY�R�����p�P\5�/m
4?>��#!Xk���+���P�=��[�p�!�e��u'��
��G���n g��B5�
B�
C���������_C�$�5�*���nH@�ql������TA��{#\���a�DT��"��<�%���g
0y��[�/{Q�s���UX~_.>����W��(@
P��(@
P��(@
P��@�y�#�j�CUx��)��u��a�{���o�CF}��T\g_�_E}N���(���F�)��0�uW���|�g!�����hU����_!���]h�_�7���0��!�m��Tf��B}�<�=�����+�����U�	�WF��Od&��"Lw��l�����5G�1�s|���5T�u��oO���������d������X����q�Z���&-���c�t=��(}�J�\�e�q(@
P��(@
P��(@
P������#�V
e��6K��W��?�i�(o�:�K�B��OC������m94��vBSm��'�/F�/���S�l]���[Pf�	��7�=�� F� ��:a@�5}7k�2�(nr�����"��1c��6xrA�O
����i�6���KR�8(@
P��(@
P��(@
P���@t��>����M���y�_���I��
���SB��F�4�XA���r;^}��w�1�o�xd���.��G:.Y��L@x������W�����0�D	�o*�F7N�c�[v�Z��I��cr
P��(@
P��(@
P����. d���b�/k7 �6�;.�5�Zq�wj�/t��Z���k��<�;��0sn:fMR�hEZ���	�l������W6w��(w.��r�����p�P�|K���G?p��s�
CoM��?N����A�L@x�C�l����p�U��[�����|Tk�n��G�4b�Z+NU�p������7����,��EZ�
�&�`�b#��$����^
P��(@
P��(@
P��:�@t!+=������/U@x�	�����a��4@�K���&���C�������j=o@������H�����/����z��W�=�Z,~#s���	�4b���;�������ba�k��gtc�c�������Z�~�*��T�����{3$��onN��(@
P��(@
P��(��@t!+�a��A�U�6?��DW3�l8_�[����N8��G�=����(8mf���J3��������X�eSUa�����P��PD�iV?b���4X�F����J,\���?f��)���;^]*�"�]������\���������tLl����6�XX�������h|����nlz�
K�r��W���6L�^�4�U��N�j����4�5����������
	���O'����0���]�1+�/���^���w)��	�yx*(@
P��(@
P��(@
P��L ��P� {~�N��>U������	M������p	�U��d�A8
BZ[w�!��0�������A��,�q~�]6(Yp��>��^��]Q�%��\�]Y�����*��+�a�'2�
_�&���0I�2�n��FTc�OL��H����������Z���-F�1������A(��6���3-.�;��B�#>��ir�����o]#^]o���!��RK��ixdI���s�0�������-1����_���������0)��G�[fLyX��{31>=�{��Q��(@
P��(@
P��(�i�YA(��\A��G���Q���+�a�����{�`@x����b�m���0t���H���}%w�XtS-�Z=�mb  ���i����H���4c���C��"L@z�U@c�_ub�������r���/�<��0���9��6�?�<#k�i�e�(@
P��(@
P��(@
P�Q
D��AC~��C������p�.4��� �����5+/F@��5b
�B*c�5`����#���i��5H�A��(�Vf��ZLx��N��������0�w �(@
P��(@
P��(@
|��YA(�XA����{F��������A�o1����
�(� l/����KdB����n���98�k}�-F#�2�8��&MN@(�C��;�Ho��\k���9 �����O20F��{�q��(@
P��(@
P��(@�o�@la��U`B�����A�)P�Nj���Ge�e�����R���}�����
B���p�=��3V�O��0���u�����<�i#��	�A�B��d%�f�.}W�t`�`��Kk�|'p��|���Wo������c�[)X��,���S�~vL{*K�����Z�mn��/Z�odv>k�w������\���tS�Dz�ol����vl�S��Tb�y�3@��psALkng_E�>�'�d`�/V����v�����V����b`B�O�(@
P��(@
P��(@
P�3
�&w��OC������m94��vBSm��'�/F�/��*Y����! �}�B���_����.SA�<��(�^'����I�K�������C����������!P��Ly��b��_+��=l��-5X��&-��8����p�j�pg�0fQ6V�$8��s���	��6�b�CV��0b��tH����(@
P��(@
P��(@��K ��0��*l�w��[A����*����+<�%�	���R�=����k#�n��T5P4���s�1k��!�j{�T�w��z�a���J��`�o�1����5����ytU�>���z���@ �EPPAD7Pp�Q_\F���qt�Wf�U~��+�3(: (* ��,"�l�b�d	�����ZU�S���t'��x����n����J�����|����:P���[G����b���)�4��.+8���
+-����v����5����ug��$�z��3�ePsn8 ��*?`�'�����V��6�[���u�FaP���:�~�
(@
P��(@
P��(@
P�B- ��+��������(@
P��(@
P��(@
P��0 �\Jk�%F������a�������e�v�����(@
P��(@
P��(@
P�����/	������(@
P��(@
P��(@
P�`@��i��p�
�f��6y�{�%�
�,����<E������x��;@U��!�����|��o�qb;t�t�������%(@
P��(@
P��(@
P��@�> ����<yR$~��O���*���F{��N�j�����B��?�K*f?
�`'��_������>7��"����^�,1���0�*@��b������+ L���=T����O���a)��}C��(��>�1�(
P��(@
P��(@
P��(@�sZ 5�;M��2��8w�A�����O�t��<�����1���}�|��J�����U�#��h����h7�c6> �1�R(q	��ZS�,$�a�sS����=��>\}��z�]O@��g�~�q8F��������� ���
�X6}
��
H�r
���,�=1H
P��(@
P��(@
P��(p.
���@�m�.�C�U�����(�.U�����_9������Bu|�(1�=b����@���{������?��RU���D��Q=�=�Z�����uh>MVA��b��_���g�s���
���`?�	���q}{��/������a��@
P��(@
P��(@
P��(@�s@@�o�����y7��R8�}���A���=eL+�r��&� �A(�8�pT�&#��.���������=� .�'�Bi�������P�s5�E=c� e�����`�5U�'Y��H�e��"�60=��5�y�>�}���|�9���|�9���|�9���|�9���|�9p�>WAh�@�l5��UwY7(���{�kW�����!�.���%��Qqr%�9F���}!�U�+(%�Ul��^���;�]��;a�-�ek
P��(@
P��(@
P��(@�sV@�u�s|Jq)�����@l���n0�_�f���a�2�Dt���f������ �n�>�������
`�"�P�+ d�J�!�JHsQ[Ch���\(:��������i��^R��}� �
��P�t���L�2d�3 ��^���0�����|��F ��0������
7B���]VP�a.]���/��G�tGQev��aO�R��(@
P��(@
P��(@
�kz}g$���������
��������0m�
}���d��h)�X^�+!�=q5#��~9=���K �,�<�����������[a����F����|�Z-�k|a��P��]i�w��>���`���*3�7��SAXY�����i��W���t��t2�����a��40���(@
P��(@
P��(@
�q����U������������a���K� ���
���F�_��c.��w�@��R�g�����~;��3t� ��w�!
���Qaw�� �xm��E����.��!���d�3?�.;��V����H�UAXs����y��6C�f���E�![�^E\�_�Q��0a���0���H{~��iF�;��N-���z�'��0F|>���(@
P��(@
P��(@
�}���� �}{��$t������������@������6��h�����e���p|���5�c�:_�
������_��[��C�+?Y�@@�����?a��sO�O���h�t@X=�^�'����}{�@
P��(@
P��(@
P���U���0����O�j��p����
���k 
���p��(ix��UAx}6�a@�{��v|0���U0��d<��|�����Ixc��>���}v�|��4`��8�j���+
P��(@
P��(@
P��(p��0���`�;��:��Zq�u�����_�O��m���2
!��mF�{o�u�
,}�O!��!a��f�>��C"����*�����w*v~P���:pp�v�
3��G��?����K��]��.����lx��<���O���pi�~�f{���i�� m�,�a�~mzq�1I$���nb��(@
P��(@
P��(@
P��+��\!+����p���\<��������w=as�v5^:<�i<��	���=�W���hu���������~���G��e1��y/��p��e��5��&�e�������m����J�A�:1��[Q��(@
P��(@
P��(�� N��*�%F��L[i4��f+�E+���e�C2�� �����
7B���=����`^p{�U��c�_*��N	O��{Z�������u�M�����yn���'���h|�.����=!������������[Y����,x��*�H���=K��*r7Y��_���-��7���N�
i����g�1��#T�.��]O��{Z��_p7![Q��(@
P��(@
P��(@�HWAxM��U=y�h)�����������Axt�B�j1��k!
��M����A�P\�� p��z<�~X|W�^����X���h�..��gd<�i�{�Nw@(�a���0�s��B�;�X������@����j���jFWO�B���6����bXk�w�	QwC�S{����X#�Af�(@
P��(@
P��(@
P XV'u�+=�
TY�liD�(�q�� ��%����4>
������{�m�����pw���3������p������U2>x�S��^���I�.����,}+
I����_X�=�V�(@
P��(@
P��(@
P "�UzN]O~�l��}�b��������3����SA�O�� �����x�BW5��&���	ztO`H����������o����
FMK��:�2 ����P��(@
P��(@
P��(p�XA�%h�
B�<{�o��*t�-�! (%a�AX��\b����'���A.1�8�[A�p�=��q��o������@4z�x'��P�D���Lxj	0jF����#P�;����xl}I�x�s�BQ�4��tj}��
-��
�V�(@
P��(@
P��(@
P \��+�i��=����{�m�� l�{fA�J� ��Ax�{�!�{�v�*a���#��q���Wn���)�b�sO����`]4������x*`@��������?��6��N)n�.�����{�C�	V/����6���h�r���=!���>����<!���O���%<�,�;�U����=���G��0����(@
P��(@
P��(@
P��
��08�3XA�I%������g��
���
�uluuB����*�L/�=���3h�4m����
;k����v:�3��@��� IDAT�����O0��8�j�:I��*������D��r�*��,�(@
P��(@
P��(@
P�gY �
� ����DKY3� l����T�s^�A�����`�l+V��Q�n3��{�0L�����ja������;_�q�@�E��x��hJ����),�� ����E��	]l���	������;*��!��CK��:��
GE\t�������*��g�Q��S��(@
P��(@
P��(�+��:��
���l��h�s�N�����d�3?�.;��V����H�aoU�o�b\G����A�����(@
P��(@
P��(@
P��`@�ea@�[Q��(@
P��(@
P��(@
����@�sb+
P��(@
P��(@
P��(@�s\�ap���*��5�������P�t�^��e��>Y��%A,1z��N��I��J1*
>������0��V�(@
P��(@
P��(@
P��N ���g�����S������!m��d^�eY0?{?�n��b
�q�r������W�A^���k!�tk�.Bq�J-���o��W@?{9�	b���WM����������X��;���T��x(@
P��(@
P��(@
P����*���)?�^��a�h%L^��s��_��`|}>��;"��og�����=�7l�dZ���0~��t�`{�O��B:��7�=���SA���U�W�������SA�������5����������XG^���	=�%�*Zs�
����Y.{b<��(@
P��(@
P��(@
P��H<r'�W�B9|�;H���oh��/k�!�W�����B������&��=6�A�C�v�eS��B=�* �� )�YA����A9
�u#}����*�����)����{X~7�c��[Ah)[���O���=�(BE�G��u_�P����(@
P��(@
P��(@
P�\�I���(���m���E���g7����
9�f|�d�be	���C�`��#��$'�1�Z8F_E�m�!��q�g������v��v�%7��DK�
B�e��#�ni�����i�SA�I�3�k�6v-\����c�������'~��y�s��>��s��>��s��>��s��>��s��>4���R������&Du�]�~D�����; ���f���Z�8�r({B�m�/�}_>U�f��mkX��r�$���R��(@
P��(@
P��(@
4#I�������F��E�hE��k!��"?��]��"��(������o��6Y�w�
y�^@����_b�l�������q-!etw\��!�n�����Y���� ��Q�fS�Y�0��(����|������/C��$�;�B��!����+�:
���_����p����(@
P��(@
P��(@
P��S �GBisU?.E���@���HV�������c��(=&@mu9�o��#���}>���!�o1�'��P-��;	����?����bh���� ��)0��M{P���N4���!T��t��T�?�
�������F�@9�+�t}G��U��E
P��(@
P��(@
P���B@�S[�r`�; ���{ n'b��H5&@4�U���d�����U���� ,{�=T~���C��������0���%F���� ��ykP1uQ���]�j�G��[{�iK�V��
�/
s0��a�`��u�0K���	2 <k�<1(@
P��(@
P��(@
P����s��;��G��!u`��/Y1gI����#y����x��,l��tw����;������?_ ������{��;���������t1��ao���p_��9�d@x�]Q���(@
P��(@
P��(@��j�W6�<��]��A�oLCy�K�-^��p�; ����YA6��s +/�k��R��(@
P��(@
P����+k^���P�^%o�%X�Z!��VxFJ�E'����E���TT�'V5�'����Y��}D}z����||��b�jG���k�� %�[X���|����6�[L��bK�,�������xB�y����N���<�QJ��9�T�-��C��J�(��J1������D;�r��dt�ZI���)|����jt�>�K�1��������6��;�,�E�CBX&����J>��mLz\$&�nQ���<$�,�x\x��3��N��t��X�2��O�S��C*�FH���,�+H>���K]�_T���(e�E*f��x�Q��u=��(��e����U�#~�%7��Q�<��(@
P��(@
P��(�lXAX����������0Y������X���Z&�(�w�F�;H1���a��E5����*��}f�~>KZ�$���!S�W�����q�������@�����	��_�b���5<���G�jGA��j�������D�\#��k��~�����xG���q��;�{�yt�B���-���]���u��?!�U9�k����%(@
P��(@
P��(@
P�B`a���D�+T��F!��3*���b��1+��w�<�TL]����3t1�}� t�W>u�/�����{�*�q��
��xQJpW�i��x�qu}����S�C��8xB�	2V9����3��=���T�E�e8�������
��P�0+����Wm9�+u�O���LA�Z�U�	�R�7
�xF���#�*G0�Q�1\j�g<N������%��� �&������^>�Ir&���E�i� �cb�]6�c��rvl��d�����q���1��X��M��x����\��mT�	3�9x-0]���Y�iUM�+�����; �m���^y��z����;�s����xD�uz�lH
P��(@
P��(@
P���[�n@f�U+���;�{�/l�WEp�; e������ #���f���:~�s��l�V�3&�3~�%�,��^�-��d�LxF��U��|���;�#�ldf���m1_�ZT/�Z��_�����:�s�s��_{+']���8�5<i�������3��2�n@�:��;��:�Vm��MG��z�y����EJt-��y�Ex�~s�Q�r�8Lx�_p��@g{��{>K���L�A������'f{��mh�|��(@
P��(@
P��(@��� �)r�+��L��B��3PA������g�9x*�Tm����^#�	�Oo0��wa�?���aw����^4���/�c�]t��B=�k��}uB�����Yz�O���5��z/\�^�
��Tfb��5�pU�b���7��`�(@
P��(@
P��(@�V��
� �+g���uFap>���0�0���r���P�j��&lW��C5a�
��Y��s/z�BO��w�}�}����5��m+���.�`��P����}|s��(@
P��(@
P��(@��XAX��	+] ��������0�[���|��/����_k����������#��3B!&{�#�}��g^�����.1��3W9�k%�������]����|f�%F��zb^D��tY���m~�=�+f?���Ce��R��UxY��g��@�}C��q�el�vX Vb�������=�x(@
P��(@
P��(@
P��h8 l ���=X�P8�b��3�*j�=����P��/���H��=���O3� r����a�p��������
+)e��8�,����s0N1b����0P��}k�v�
�O��`��:�)��>dR
0C��,���w{�%xU�c��-+�wW�)0)'1�����>xQ��&��@j����S5a�|3#��w���=��
��?c��{�,�'��M��H3v�%��t<$�C����FT����#�(j6
Lj9�G��b�p�=5�+�'�.u�l)�9�j�"�$�RL(�����U�'��3RK�q/P+�E��Q��p���	=����.�Q ��Z]:��8c6�(@
P��(@
P��(@
P��@�axbN@��O���v�����T���oQA��/����/�>LV��g����ko?3n�w��������R�!N�}V�g�����k��~Bk,�g��v��������'�y���j�U�m�>��������5|z[X�<L��������]�W{�mT+rk�MY�������B�`���������L���(@
P��(@
P��(@�� ��1�rW��a�����u����|�U���bF�6B:�K��CL�EZ����b��K{�@9�1�"t���l���A���3�X%�c�R��+PH�p1�I�aT�)(P�0G)v�Ax��_�[�^)�5�|���~O���!w5���9�z�!��qrk,0d�+(��zh�g+�s�<��2p�]c=��vN;��y�H)�*�
@��D�z��&1�[U����`�|�+ ���%���|+����8�9�=�i+����3j�.S�����V����4��4�,��TWA���c'Q31O��C�(�S��(@
P��(@
P��(PG��5I���
���*T�Y	��cV2C������� �'#���������E~\���g�3z~�'s/[*��3���#�(@
P��(@
P��(@
4K�5/�fy���f@��bR��rG%��w����~�(@
P��(@
P��(@
P�B`@���Y���%<���e/�<4r��L@Q�s����O?�9��Z]z��F�j�'
P��(@
P��(@
P����$�����_�po��O�kV@1��Yb�TL]�>��3t1���>�F�N�����|a5l_�z>����\�L@���k�z
c���]�/�RD���(@
P��(@
P��(@
P��@��p�3G��I��)Y1g���������������QW���P\�B;2���=-�nB�K���������.������=�^Y��_���(@
P��(@
P��(@
P�h���Ra-����_�N�%b��q����W_G�5/��d,�df�D�?}��p������@Q?�v�?zw�]�i'� ��
�<p�5+��2�|���*��~P�|�VWZ�;����
{2<��(@
P��(@
P��(@
P��
���4Q�y�*w@��V�\�����|�j��a
���@jF/(='���Xg|e����e���
���
g@X�d-J����G�c��38��W
����P>s	��NX�5����_!�����P.����U3�C>X>��(@
P��(@
P��(@
P�h6�^�aXg���(�w|���?�x��!�@8�B������h�j�;�B����!d��m��������)^�	Q�O��bFOg@���.���@������o�z*�[�4�5N����������
���������~n���s��>��s��>��s��>��s��>��s���9��
B��|��T�kg�.D�@ZN����7��'d���R8�/�b����[�����P�Z�A��BAq��'�(@
P��(@
P��(@
P�h�j��P/��d�Z����ZV!���~�9?������C�q9��6���TY� ���h�����Xn��e}!/��m:��;u����
��@z]���h��
B��
a�?���
]j"�!w�����|l[�3/E<�`���vW�0T�<��aq	d�1�|a_#�b
��Q��(@
P��(@
P��(@
�+������
���A+Nk\���_����-���S���q(��B�uQ��A�am��t�
�U-!��{������!�
mi�p*�]AX; �������8�������' t5�*�u�#f����x (@
P��(@
P��(@
P����!`�p�!#SAx���Am�p$��T�^�������tQ����WB1�Z>���=]�a�.������M[bT�5�)��&A2U�6�|����%t�pd'��O�t�U�vZ@�����/u�-�G����J�
��08#��al���6�+?�7t��3�=�F7�9��W���$���01��Y,����������^x*%��l�u'�������#(@
P��(@
P��(@
P��l���0������
���� j��M�Ru"�/���������'3�����@����gB>���A�MlL@�"l	W@Xs�QW@�U���%t�pd����' \|@�=�c�v��6�F���y�U���Y��G�jL��y���a-d@�$�;�(@
P��(@
P��(@
P |W@xc�����p��"��}�g�+ �}����_72 <s�7����m��;IUp���:���4,�#�o���=�y����FL�a���-'�c���p���eaWF��}Q��(@
P��(@
P��(@�+]|a���}w=r=vT�\
r�
���"�+�9�"�*
�8
�k���
���i�w�������mS11;����u>�];��b���v�<T���ak%������uX�#����{�Tm)��r!�8���m@��8xY"2��8lX�[>�u���&;���3�J�X$���9��2S0��d��6����������)	�U�������`~�)���V@h�������kp��k��;�!C������m�q2�:R��&��������	���c��(@
P��(@
P��(@��C����hn�
��
B����@��
�|m<��`���0t_e�;?*sgcl��-s���7,������BG%�}wS���j����_z�T8��s�Zg�`�dd��WF����"�fL�������|���qs���`u��z�����+N��*����V��~������J����*d�G+]c�+����x~<F9
P��(@
P��(@
P��@�`��z5Q����V��4�O���g�
(��4������0^>��W{�+����a��M����![3�����T��krq�e+������Se�:qO�X��Y�=0�\����k:��dW���n���%x����\���U�~�>������7/�C�@�q��4��!b�r���?��(o%��/���/��F��/;�:������3,�����i��%x9Sr�+��#��o����K\��5���tU���=x#�6u�v�b�Q���1\�S����q��3p@X���x������S��c�1t[	�����m�=#������(@
P��(@
P��(@
P �@u@��8��X5r��?����gp�AZ��	� ���N�3A��{z-P
-������gB�7H,^|�J����7k��x�o���c���6�/��ya��P��s_�����'����
<�Yb��A��Bj����Z����|Ub�����.��1(:��
�����x�}T���X{�Z��U>�;w8���Y���	���[O������eN�*�����jM���]btd{������w�V>�(@
P��(@
P��(@
P���V�P.�
��Fl,nn���}�t-	�
kn�@�Z7�5� IDAT�+��	�|�������h����������_��u5\�p�=��' ���g������N����p��w���_qpB,.m��-08^K�]��������|�R� ���VIYvwo��J��5�#
) D%�}~S�AO
P��(@
P��(@
P��@� t����� ������&��P;^��Sh���J�/������Wt��������g- �F� ��+OW����X�G�����S�s�Q�vVnS���6�^��n@�	cq�V�?#-%�zp���r2 ����(@
P��(@
P��(@��`�' ����@����g�,1:�^}K�z�,��w��)��&A2U�6��! �����!y���#���mPw�Q�3�W`��������A����s���}��w�����+��B� t/���w7,m��|�
�~���O�a��6��o���C����.��������0��(@
P��(@
P��(@
P t��a��U��|>�����Aj@��O�����
B�
1tM.��g���20B[*T����I<�c!�fda��Td�z�7��n�����������y���>������7���9�gu�����K����q��(((�����S�z�W�������=���%��h�hd�_���
��UL�S�z����Km�.Q�q�2��?�Q��F  4c��9��
Kd`��W�
���a����r��%FC��
P��(@
P��(@
P��B`��	� �\���b���[Ax'�m8k����[=���*��bQ��38�[�Z�������~�1��k�QT��U�����{=Kq>n\[kw�G��B@�������L)��������?��u��e�/1��{�:xW�R��_J,�W2 ����(@
P��(@
P��(@
�.PwBO��Y����?5�
����S���{�_���BAN^�8P�y�6 *
#Z��O�apu�`3���EX�g��Z;��u�������p�}������]������Y����Y��&�m�G[+���Q �
B��T;���{��1���e"&f%c\�XdHd;��`�qV:�L��Kz�uH���QHtwp��Z�?! c��<<y�[+]c��c�����$���a<��(@
P��(@
P��(@�XA�;��]��a�t8:���U���ah�:���N"~C~� ���#�(@
P��(@
P��(@
P��G���Z1 l������?���4�~��`�"(@
P��(@
P��(@
P�8�2 <�nH�`�%���(��3:=����9m���9*
P��(@
P��(@
P��(p�0 l��p����*B�)TX#������r�Q{��8���Z�]$���j�cx��8<
P��(@
P��(@
P��(pN	���A+���{m��[�C�\�i_�N���7 -���M�}2��c ����gB>���A�M���B��4�����%��Ah>�L;?8���v������
���������y{���(@
P��(@
P��(@
P��@����������5�%��
����X@h��{�-_��o�5n���mz�>�&�}�������Z��w�.������V*6
VMd��%����|��
B%�0b��4��� 
P��(@
P��(@
P��(@��C@5�a~�n�/��
B5���
���&�/���~<����B(/���������A}���U�Zq��#!����� �(�Vp�{V�Qr�;a}R&b�����~Xw��[A(�-�:v��uN���)@
P��(@
P��(@
P������>�m�a����ufD*��P��-�
���PeG�qT���W�1�A��
��c���a\�_H�jh�z��/�������a@wg�ZY������b��� ���������J��?������G>��e��������������u������������������������~���~��g��A�� �lA��O�������!"���6�Ql�����A������l.��8����m�(��On�!(@
P��(@
P��(@
P����- �l
}��PL�cC� tX��Z�����I
��t�1���.�!���^k�V8��C���
c�T�}]L+�����68��e���tQ�}zh����8��g_IW=�x�uPJ�p��Sa?~2��������yZ@����/\����'�)@
P��(@
P��(@
P�������0\y#����� �*���������G���ND
���c��V�=������aH��0���W��8����T�X��FWV�O"^7%o}��%�48����DL���vZ@h���oV6x���b��-��0������[
>�:��)8X�~k��w�atl�:�t�co��Fx���;�6�+�qR��(@
P��(@
P�8�o����PUU��y�b	d��s1��|�)��>*���E�a��5�m�w@=��U�N�����|�gK�-7!�����]A����7���S�UA8�OM�y��
v���8$�>��N6���"8vlo������Y��cz��������0{�)����M�Tl�����\Ua���x���Dh�aV�	���b���x$���E��g�}�1��#��x��5��u`w�(@
P��(@
P��.d��#}���B������������i�CRao�)�1�k�{"6u,�}����g��~���a����h�><���y���]��OM
! ��[��U2 �:����R\vu+��M�� 	�I��3;�P�fx�����(@
P��(@
P��(@
P r����a����-��7��p�; |�9GW@�b=��'��6,.^����
� �"WAh�-���M���{6M6���j�A^���5�
��'������>UA
P��(@
P��(@
P�XA��i�
��6���5�z+}��qr9f�,����s��q-�iw`�^��o��bH4b\�$<{�����:���N�����P��/F����0�W4���w��T9�����G�(�}��aR�8�L�m(���'�8R�a�{v�0}O%VI�|}�|]�un���C>O�\��|������r��_��e@zJ4���G�E!�w�
x���*�+��
����{�b���]o���e�����vn��e9W�%a�	��VY��>*������������5�8b���R12&��+�g����{=�?�����
�~���-���cf4F����lD��������|F���n[9><b��*�3�)�����g)�:����a`�
P��(@
P��(@
P�8+�W�RC��������X����1hp,z��x�����0�K��ZAX�%k��@�Of��YP���'L�[1kY>�����n��9
�<!"�AP���*���m���p���:�#&�i�G2�����,
l.�F�<"{V����P�A*�E�gX
x���/�m���������W��������C^?Uw3�0����U�����zm}�����
������g��u����0~I)����������0����3rJ�,8p�k��O0��0����(@
P��(@
P���S�HU�~S���[�B�N�% l�==k���S���A��u�5���P�1xvP&������S�yk&��a��dLno@���$���s1�*c��-1)��`�������W$`b[w[�
�/�3�%<�ik3�����W'bt�vBy����R�C��O�@�\p��%���g{��{�'<�9o*�Uq���a�90�_*^��tm\��V��w[1��L���s���z��s������[H�F*
��Z�Q��*>��{�,�W����)x�[���Q�r���g����g(��-���i�B4&M��L�Y	j���9�[�aRF�������|�!��7b��4��wM&�{%���-(@
P��(@
P��(@
4������Z����l;������[n%����a��0��+`������|<�l*���
���a���_�� |dt[LnY������il��K/u�Q�mb��w��n@K,���W�^�e���b$����up��W�Gn���L��8�S�A��0yLk���t�j*6�����3�l����������
7�UW
:���Z�b��X�r��`��g�p�O~1&�6�c�������2��V��f��NkK_j=�C[��7I@�'����pjH�W����a��3����k�
o�W��ZO����*C��^b�{���SC�4�i�)(@
P��(@
P��(@
��� �����*x�����&���b��� �7y�����
B��{	={��w�{��zB���Y����Y�`g���x���\��a���5����'������S���_=�k�h<�xd�����������
i�A�
dj-2����XW{�Iw'5�{S.1�/�
��
�Zz����u��3��z��O�^���������H��4�i�)(@
P��(@
P��(@
��� �/l~�ZA����jI�����P��D@��uF�t`s�����I�5+)C}��X�<�7b�5��]�-t���������7���0,6�(@
P��(@
P��(@��*�
��+ �D���8��~@?������O�Zb�o@��������=�r�����p��R\6�
^��K��0��,�����Y��������e��.`��-1�d��1k�I������ t�����j��e��^��cR�{��8lg�z�����t��l���5s�I���g=Uo5������
Q%�R��%F���!��t���(����}��� l��1���)(@
P��(@
P��(@
43�H������0��T�jQ��U2 ���*6o���=&
H��l�����Uvl�3c��G�kK��������?YZ�SQ%�o,���x��#C���xqA������$�Kum�Y�W���)�:$`��$��mp�Qf}x
��0Tj���
�L��������`f�h�k������<���!�dbnw���3�N��X���8�Y����������m���#\6���U���2L?�@8����ou%F^�
�u�#*LEf��^������
����~���E����($�l	����3^�����CS0)Sr.k���9�[�aR��0����&���s�����.�F/o
�R��(@
P��(@
P����!������a����x��XtM`v`��J��n9~(����=`��`��c��������xz���m�@����O���
{�M����=�:��&��L�h�����l.���Ep��^��@v���-��q�^����o3	��f`f��*���p��J�io��1-�H�����=�A����x�n��{#�c��9���"�s�PB���J<��_�'a��	��aCz�e[����Q�[�t�'������Bx��Z��'��2
�����P4�\�]��;��*�P�g��
|_�k��>�{���- DY��o1������!���)@
P��(@
P��(@
P������3����l;�������K�^��W�f4Z3��q 7�|C�� ��/1������{M���+�$t�����1������V&�%����q����w�1��O	��_��rL�Y����Q�t����q������P���b[)fp�33��H���e����v�����c��
l/�S�q}�x<�-
���
' ���Lxhy)v'%���������`��R��[��z�L�D�����j�-����e�}�Qm�-�����"��P�
��
��j���h�W��$��~�H�z�~Z���0��r,������������s�;��0h�B����	�l�����h��KS�v�0��0|x(@
P��(@
P��(@
�}�HT:ga�c�exg���u�uc���
y����~��G@��Q�^s;;�(@
P��(@
P��(@
P�h�@���$�����!�P����[��@�UO ^7�OM�y��
v���8$�>����%F�-���b;
P��(@
P��(@
P��(p6�����!^f�!��l�}�1��c�]x���yS7k�co
����6<x�$,~ }=������y�^��>1;�(@
P��(@
P��(@
4�Bq��T��ya�?e��
��0��i��Hy`,o��
��������c{���������H
P��(@
P��(@
P��(p�\�h@�y�'W��S��B�����8��i*�C�`o���cr�w@�W���Mq����������B���=�m��9�������l�c?j������u��o���c���(@
P��(@
P��(@
P��0^s���ZV�Kq�����4����/}���������4�}�z+������k���� ���a�-7���v@X��fN�S���Y�yRjr�
B����|���'�)@
P��(@
P��(@
P����1��s��*�
��c�`����O^�
|v���(�)�1	FD��`L��A*�������]��8!��AO�a�+A�i�J�.Z	������]: ��[��{���
B�^��	X�}
��T�X<��(@
P��(@
P��(@
P�Y
Hm�#j���S!e�UA�MT��Pv�#X�w@U�3?wA�1��[�C�k�!j@/�����ypXs���r���t�N�K����0$��!�;*��
����y�	��]|g�Z�+=���Q����J�y?�~����>�<���=��~��~��~��~��~��~��~��~������@�*����i���]{�Uv�r�G(��:��.�%�=��^8]
�P��A^�#�_v��)@
P��(@
P��(@
P���������B�UA4D�����T�����������u��������U-�L"�#���X�����{��{��&;�>]�4nB�[>���&R��a�P�Q����\u:���Z]��w�T�	N�@�����gP~>��x (@
P��(@
P��(@
P��@��\a�
�b��A���?��mo��w���������h��D�Mt�a�=��i�=����W	����S	.h���<�5���F;���U�D��M
N4��}0�u���*��������=�
�l�a���s�x�8������x�f����Q���8
P��(@
P��(@
P��(����]�]�/�
BUE����U��.��U��{���W��2��~F��7���
���&��w�=C�'o��A���Z�v�[��;��<
1�������/��������h�����
\���{Z'��X-=n�W��*+���R,.���f�C��(\�����14�cv���0���P�K
P��(@
P��(@
P�8��	�A?���B����?pNT���>/"c�������_w���R'=v�>K��C��J�����v��� t��Pl��������pG�c���Qx�O[<��i�lp��p��/tS�K^&�D��^��V�L�j��y��1�� IDAT��0�+�c(@
P��(@
P��(@
P���0aX=a����N1E6 |��t��>^�|����U��w��t]�d��������^Do�3��3h?���~��/I)���Rp]�g���c�k����}nd@:��(@����}�W]����}2I���,* ���"��^u"P+��[-X�
��Jkg�G]-b��(2T�,f�������$9I������\��}]��;��o>}] � � � �������PA��=�_A�\,�QC�Pu����jF����^�������\�V�Y��!M��4�Z�����lO����ia�KJ�k\Z�n��N�l���zB�S����]�6M�U��������f�)
��?wfM�JQ?S��s�v�g��O�Tf~���-��2��R�ib���\k�T�8w���EZY!e&%i\�9~��y�{�L������;|��(m���))�*Ju��B�-+�����������U����vZ�����h��mq�5up������u��dT�n^�G�KZ��Y����x���KO��^uum��x@@@@@+��+k�
��.]�� t9�n�>Mw���)��TE��P�4�6<S�-��.��)G�r�u�:v����(3Z@�s���9�T'l����Z������!W���������I
��yd@X������NY��v���/��C@���#����������"Q���Z
�g�:����o���R�V�r�+F���:���$��4n|&�F@@@@@��-@a�x+T.���E��u+��3��F�����+���U�5����|���s��4==��.M�UmeEe��ku�l�����K�f����6�iD�.����!����Svi����4�Or��0��	��94�Wg����,������������\_=�!8�`@X���tM��A���K�����W�o*��z���`�u�����X��W��0"LTvH���V�K�����@Qd�W�P���t�.]���%'���@�~��5�[���H�K�������
�J���^S�����=�[p@7����.���oR#�h��)
@@@@@@�	+�J��*
�W���JX4��4�i[v�)��p@�����0�\/���*��#�v���$ET��[R�������%9�ZV��������\��n��rfh���P��,��]�l����������U��'�
�\zw�6M�VWU�d��U������x���;5��*��q����M
/[Z=+��e�kF����Vc]N-�/�������*��R��N����L����~|]��������[: ���m'��m���;����w������� � � � � � �@�TVK���� t�Z��O�v�u��^��n�<���K�GY6�oY����,��io�~_�5�V�w{5eHOMM�]�W�r�U�W-[��^���v��@�������U����e�ZVP�w��)����v��@0�p@����I��2�P�R���# ��sw��M���X?rh� � � � � ��Z�
���QZA- �T���o�ha(��^����vI��Qv���z��XB���)��(����60]C�K�V�����_p��}��E��l ���k�7;5[I����.mgQ;�Q�L���$ ��S'm1����h��P|q~@@@@@h���j�TA���<f����v��F�_���5����U'6�����������)�W�o�GZ��Ti��}Z
Wfo��;�-1Y; th��zP1.1Z�^�QC����o�+;��>��fh)�����;Q���F�w��:Afh�	]���)���Y��O�j7��^���
������#�CwWw�1��Yg�&}�[[���t�zFV*6T}CEc����<G"� � � � � �@�V������
��q�f�KS��,��6�'��n��!�5%��q�������|��Q���%F����5��J���E3�R4��������b��3ib�deFY���`��Y�/���0X������4�s����eHn�6�i�3A�w	F���>-��M�s�K�u��N6�3��������\�s�WN/�k\f�F��j�^����m�f��4mxOML������~����z|D]��tj(�[�C��(����jKQ������B{�>�����z�fZ���!5�o]�
����gh�������}���S�e�h�W�����R�����uk��}���LY}z���>��@@@@@@�-
��p��>M�����L�����OY/J�� ���EK�=K^�������B���F�?�^xQ�}��p���<G��U�V����5uB�
����M�8&)X]WV��W���3/��%�Z��S��k�5�$J�U�F�������%����w����A�����I���O@\Fu�w{����8,��p�=��ci\f���U���Y
/�y����5%���[���=��c���V�X��Agd_��_8��9p��\����3����`H{����k]4A@@@@@��F�: �W/5�_Kw*a��{���j���F��
w0��;8�FtL��:���tK�%=�J����+q)3)I�:��w]���,[3#B�>��������ZZ����:3]WwN���v�U���v�n]��C�kn^m9P�9���q�S���R�i\�d]�>I�B�s�Wg�(/��m�N�S��1����f-\^kN>�_�>�_���-+q;H�k\Z�.��N������/�V�����EZY�o��������>�n����2mqZ4�S;M���!�{u�F����z7;OO�2�_Z���Q����1���v��8O�[�Yb���}U�W�����^�%��`��� � � � � ���$@a��h�
��/�A	�G����G��� � � � � �G���
[��
-1��	�7���6��@@@@@8�����������A��D � � � � ����_-���1���o�:��<�f�y�>)�ya��VWN���U��Y9�j�%���J�yFhSI����8_���m�X @@�=� � � � � ���-�J2V�I�O�r-c�N%,���i���NUn8������}���p��F]��2��/�K6�Ug����)���/��Ke�>���!�P�W���7�K@@@@@@�#[�x�8�/
���b����)=�\y-�[dL&����v����L���OU���6�oC~~�V:��V��7�Ju�{m�jTz��*����<D@@@@@8���,s�1U�+����Y�=�G!�7���9I^�K���12Zo�c�z���X����)����
B������T��3yEu&mN���~cei���{�
B_i�����y�?���m6 � � � � � ��-C�,��'o��2%��TA����Y*���e����u�I�t&����M�)����?�|�q��`0I�ce�\*R����\?nR�57�v����3|@�����P���A����x����������<����C>�<�����y�y���x�y���x�y���xh����������v��^t�<��IF���=r�7H>W���h�*�%�h����T^)�������UJ�(��U�����69&�@@@@@@@��B���E�����r�� ��d�/C�>��)����U�N>���CZ�}���}ei7����%��7!�2e<D���r����([��A��e5y������[�.{X�^�����r;En����hx=U��D������Fy/!����y�|>����;wk�'�� � � � � � �m[��s�l}����Kc�{��n���U�;_?,&���|����������f��~�
���\E9����@_��_��^g6�X.\�Y�o�*}�}!��������{��*�xD^o^��'���N���O���w�Ir��N���m�X � � � � � ��	��J��'�TA���Y�����U���f^������?L������a��Qr-�(��ki0(����Y�^���	�J�3Z)C�nr��� �����r_8-8�%w�5t������[��{;������
B���k������5�z��"-��P �L���~I:ap�.9>Y}�c:�a�����������{�e���|�@@@@@h
�1g�2�����_�h�S���
c|�v@h]�M��6�8���%�|T��R~�d�����J;��&���h��p�a�?�?�+����S7 <I�>��*��s;��w���>��VMh����~87" <��cC@@@@@��#�����+��������"�|�P�� ��b�N{�R�����G��orh3J�O����D�o����R	��$gA@@@@@ZU�
�j�V� \����:X����%����d��8JC�w�oj��r�������o�Iw��Y�������g� ]����>:���9�Szh�i����]yw�3 ���V��{�G=��C�nHUjH���\uS���c�������Q�}�_v��NK�mg���n�:�4���
Fu�W�����ziM��[u�u�%�������@@@@@@��jV6|Xc�y�U����zk�WJ7���l�)���[o��]bt���%K�m�%Fc�5�=�/�����u���t����W�������_(
�UX���{��Kw�~����I��TV���d�Mf����zrH����cS�N���9'$�6x���tG��P3����p�Y���~}2���
�4���(@@@@@@ ��� ���T��tF����h+a��A^�4���6D�Ax�\C�5c������� <P��������U��{l�N�fS�6�H�]�')�@������'���S;��i�k;�R���/*3U�_�I7���Q����5��=�xW�M��>}��f]���?�A���n�s��ay��������vM�.��N}���_�<��O��7fd�T�O[��$]y^��8-I=l�\n}�A�~��\7L����-����6 � � � � � �@m�����4�_�[A����o*���z��]xL03r{���E���TF�{���I�����=��^�^�Z�2�I���1]���$e��������W��B��19_�������]t���P@x���tO�(|��O7��?O�����r����;gl�c�^��]�;�,�����tG)��
���U�����U��'o�s�z+�"���@@@@@ZO�%*s����zu�S�t�15��f^���La 1�'i<h���-G�G�*�.�L_�*
��qJ}xSj �
�Y��
������.N���?L��
v]���t��g�kvk��}�����r��- �[��^P�<"
=��2@��dV<m���s����(�a����3 � � � � ��-QA�P�����������/m��/�C����'
����ct[����������-w��; |c����C�Ba�Q�y��@@@@@@� �D�������v�m`���=MU�t���z����=�z�����:(�g���L����7~�[�Nt����jP��=[�����eC��������1����m����H~�S����g��|�q  � � � � � ��@K��-���������c�3��
�#: tk�+{��O�.���&��C?�[[������v��������}���Y�����&���wy���L�|�������JZ����$I���L��O�>W�^j�s���T�i�^����	�teS`y��M�u����DzcFf0 ��mx���z���:!5�YgA����S�����z�6�Z��@@@@@@�Vh����]���}�4:I��`�q��*�Z�y��[���� �X�c�TH?O�W��������
�����f�����O������mn�a-g>*wb�
�~2����x���jaxO��#���P����4$[��{;�������<Q��+���k��65I�L��;�TW������_(R^��2��s:��^��rh�C{�^��z*�Z����7��`��K�=S{��Q�m��?���zc\b0p�� � � � � ���@�a�����k�|J>c��x5V�~��[�
�zt�*g��6��I��%FK�rC��)���2��
^�}Ru��v������X��+�U��>��{k���XRf����sOK�Y��wq�
�=�.Y�>�SWfF�����B����k�G�Z��Cy�����t���5�[h~���m��t?�//x����+������[���n@@@@@@�Z�%*�Ux4��r=����F
��M�^jW�����6~�����y���B��k��v��-v:B@@@@@h����3�N�6��h� �e���pZ�Q�����K���E����7@@@@@@��V_���!������8�C@@@@@����.��7#�|��_��
�w�5t����������a��:_�X��#���S�v~��4j-����_@@@@@�] �#���X��_�h�����&��]b�/r'����'��u>^i'���K}�_�� |g����$��������~�R:?/��Yr�;\��'��i�\��b:�F � � � � � �G����/er�d��5���^����^�g����e
�����\����� z�T���c��}C~~3*����Y����E��{�)r8������Y��|�:N�w�`�o�� t�S��7�<D@@@@@8L%��E��S�r7����r��;,&o�wUR���xlV��c>�8%L���yT�����^���
�/6�}��d�s�'�$���%�Q�o��^�_<�����,����d�/{�����r��� ��L���������5�@@@@@@���1��lC����.�u���P�[�KIW��7�.X%��}h&m0��:H�Y����M���`��y�q��`6�4��o�U�������<#���j7�FY3���g��Pa���������������>�kU�y��!��|�y��!�|�<���<�<���<�<���<�<�������#�l�2�$+����&���`�a�n>]+9�V��w�o�0yv��l�-��H��#��_ho��UN�����59��@@@@@@@��)�_��j:I��r�b� M���{i��C7q�A��z�0l@�
�H�q������H��V���ANg�l�ce�������T��������_�Q��?����:<�|])�G�e������U�x8�C IDAT����2W�(O����
���\��^������@@@@@@@��-`5��m�2�j���UqW ����'�������W����3����m�S���������3}�u�RI)�B���������� t|�LE��_����'��u���M�v4��j��1J8q�LF�|�TU�_'G�����yH� � � � � � ��vO���i1U���A���c��zbE���Cy]�q{S��%������A0O���B�Ur��j���&�]��0�~���S���\|�|�~.�Iw��I����x�-�,)�l�*�_�������v������4j�/,���.<�����+��
�<Y��2Avi�s�l�>��P�)���g-�h���{��&M�W��>5����t�i��T�9O%k���_����������@@@@@ZW���WJ8����w>����q� ��l�ycc��������W��}����C�	M�;F7�M�c�9�8y��0�.���
�We�+��Ug1���5�bcL���a=b��0#����6���j�t�|f��y2�i5a�w�@@@@@8�l��G�
�W�w�{����L����G� ��{���Ta���-��pj�d�>���?�ufj���6N~����:���d�������A�N���Gv69 l�+J� � � � � � �� ��J�PA�A���	��]A�
-��r�q�Gw/H����Il]��O���j��i���Zu��,�Yk�����S�|���\)c�Y�����+��^Z=�D�gE��'Q?M1�*<\j��C����K��K=��h�T�.���\z���Xz�t��v=x�Q��n}Z��C�����_z���}c�����?���K�m
�;�������1F��=z�[��2�HG�;0����	���p��{��[N��
�����
��e�rd��5v��3�������Ju���U����\������8j�q�9T�9~��^��9������w��g���N5i�D8@@@@@�(P����N|�%F�����������b}S)e&$��L�*w�k��a�.1�Xh���*�Cn���y��S�����$�2���k�,O]�L�f�����:�{��r^���F�������&��������6��&��1�0.�g�Q����K�:�}���*u�W0D��
h����N]s�Cu�%�.|6I���+��p�F��hy��9�����pr{�`Da�z�M�v5����\F�����eT�vo-	��^�T���@k@@@@@8\Z����w~�����dG�������� �M���o�G�Ax�|�No����2��� �29|���SSoqj� ����A]?�R9cl�=���]$�}Z��C�&���,{�^y��C��E�������(�j�'n=���9M��5��h�=�2N�����:g�A6�O��^�k�!e�I�L��7c�J�I�+����V_��og�������]��/+e{69�y��Ue�*��`������s<���rOO�-�
�P�&��~�����b�G��R���[4{�-8^���x��}zl�5�8&6u��v��z�z��x�Ve������f�i|�M�>nVj�],{��s��D�[����,��;�(�Y*����.sh��D�tO���B@@@@@��)� �D2�Wl8��o����:a[�N��Y���!f��OE�%����6TA���A{�'������Ta�����$*����A3�H���j�,y��)X��E�y�Bg���-�L���������PU}V���kN�T��5sl���^���LSLu����.}����{�\��o��:hz�����C�f[��Wm:��r�U'�? t|���W���-k�v�C}.�|/���^�3P5�P�Gm����B�� 1Z@���k,;�L(DM�����>F� � � � � ��Q+��
B�^��]S\���t
�!Y�'V��*�Ova�����sN������K����.�B�V���
��<z��r=�_k��I#5�,��=��A�����I��&����J���#�(�]�W/L.�#K��
2���8����n�q�M�����,�2�65�? 4�IR�E�A8%J5^��U�s|�	��A���U�1����6P=y�~|2q@@@@@��@�+
�b@x�V6�Le\��o����n-���/Wx��K��d��/$VU��v@��f������7�u�
f
��l���?�	���Z��G_~����`�a/���m�9�`���
T�t��A^Q���%��NF@�6?�5 � � � � �u�_Ax������l��8D:Mh�m���m, ��W����oM��K�F+�����zJ�=
#� �*A��yT��a��P���w�jU=6R�J��]s�S�Z�4�y���E9���G3�H��k]��+��F	��3a���9@@@@@��,�������ek�����MQ����b!a��y�K�GW*�T���7���W��}Z��C�&���,���J04[=�B��Xu�Y&���)�����*��m��:y�K�<���WU��gdx���jPq���,�3���_	�|jp9RG�G���c����Zb��&x�9��3��c�KS��������;K5�ms��=vJ���k��[u��l��8�R�v�����
k��������T��z�� � � � � �@�@�B�>��M���~�������v�������������a��=�c�Tgo�P��d=3��~em�Z�f�$���������T�# ��j��r�����~�E������Ul��1��g�4������e3+�id�Ph��Z���_:��
��k�I�W{�:" |�[��6�L��,
/1*����l��/r��]�5�*�l��0�9���@@@@@@�H��������a��q�"�[�U�R6a�����u*�������o����e=���:*�|���#�� �%E���Se��r�^Q��(�T6t����z�i��Z��2�:�\�n��E##kG����M��_��G�v2j��&]t�M��*���(^��#���e�9���s�Q-J*���G�w��1��s���7�Ko�,�cU�T����^u����Z�]�,�[t}�����sz�UW�]��fM�����f���+O8��p?#�.���s���?��Z1 �c�Q����h`�T���G#@@@@@��@������D�d���B�r�v]�1]�:��pU�! ��N8ha�C�@@@@@@�6,�R��"`��(�qU�+�y@@@@@@�a5����!��8) � � � � � ���  l��p�O2�o��_�7����0%��7b�kU��Z�=���y@@@@@@�@0 �D2��|�|*����8�;i�.1:K��
��{6p:{�u�4�Q���?C~�2��W���U����f.�]���d=�������4%��)�_�&�2�
9{��4B@@@@@8r��������!�%5��-�R�'�n��A}���$��-s�c�T�+W������r��;^���
��M� t|����zT2����������/���W
_$�U�3.Wb�u2�s�M�'G��rnPy�`��@@@@@@�h�*y��d��+�
B(���Gdp���dz�ti���O� ��CrTl���O�KM?O)i��
B���*��~��;]�w
���,���2T:�L�;��|S.R��({f����rw#wi�|�e���J����v7�@@@@@@����p�������L���TA������s��)����h�ah'\$��,�/����\yq��`0��<B�|�dn8����������eK8.�>��*�~%�������=�y\���C>�<����C>�>�����x�y���x�y���x�y�����<��=}%��|���<%y��u�|�.�{���$C���\���3v��2"�����W^&�������m7J��}�u~)�w�SOD@@@@@h�S7-'H�2��*��m.��V��1'�����`�mD?�>��w��S�M8�Q�q���U�rm���Ws_�����g��z*��o}	�A���*s,������zn?S��=dp���`,��W~�\��U��\��>Of�b��N�1�#y]�� � � � � � �G���:R&��)C������S�Y�w�Q�;����Pi��H�e3d��h��	W�i=Wn�>?�+)i���B����������@� rM���� ,r��B�����4yz������^�"�d�6l�\=������"� � � � � ����d��<�
��O�h���V}V�R������*M}V�O*)�W�?��d%%����kU���? ,p��b��`@��y�l��O�@��
�7tH���R�z���b������M#����T�}���V;#� � � � � �D0Y��1�����g����+�c� [�O-2�������TR�F�?���R��lr����_j~K��: �����r�\J������@@@@@@�#R ^����J���(�*)}=p���U��y��}T�2L
@@@@@h�TV_��PAX�f����?��k����2�7��Fl.��y}�����v��@���2*��J�y�B,u+7���/I�������N��M����\������5��D��Y5�����qW���a	�P����x5��r��/���J��{m�2��?4F� � � � � �.5+k��f~Uo����]N}��j�����h��f�n5���'��K�>Zb��
��}j��6*Ca�kd���-[�������k������|2W����E��k�q��{����
��rW��a��o$�F@@@@@��� ��uWE��FA�hN��V�RZ	�e�V��0������d�
�/����=����y�{���6M����N2�f�)�����R�2i�})�~�UY��vVj���z7#Y/L��.�c�S3n.�;��k��p��@@X�\N���P3�$i��$�h���n@@@@@@��I�: 4�����a��������n��pJ�'�5:r��n��0����z���T�}�fj���+�pj��B}|_{-���R��b����d����G������xTT���5�`���i�CV���������~�P'�g����ti��s�
@@@@@8JZ��0`��w�>����'��z�S`�Q���'�z��
��]*)}#0R���R��l��C�?�/a<�u��A^�"��hd@�����u�K��X��j�Sve��/ ��_�!w�����!"� � � � � �5Z����u�n�C������=Y�����
������:��i��e��$����Q���'d3y�����(B>x@@@@@@��E���^��(�t�IWZ���dT�J���y�:�����xa���Wr@��Tl[.�K���y���������TVU�}�N�v�!a,��6 � � � � � ���h~@������%A�YL5�N@8�����/q�k�d�.}<]�/1�&�Q����+4��
-;�
���G��@@@@@@���z�|E��n��y�U'�+%��������=�Z�>-r��>��V�E��Zj3�c��
�A�,j5ik�
����B�#��D2,����^���� � � � � ���@�Bi��BW8=ux������x/]�������ezg�[�=-��W��t�Yk�;�����G@@@@@ZQ�%B���n�q�����>&��0[u��eB*��5 lt44@@@@@@8�Z& <t",1���=P�w7�1tH���R�z������� � � � � � ���a�e(K��J�K%�o~i��WJ��M�N������MaS�8@@@@@@ �V
�����������*p��b�r�]R�m'��'X��
�X�h� � � � � �����bI�Fs��g?P�C�+��]b��R��j�����Y����Td�<�(O�b��n,����
v�lg��=L2s4D@@@@@82L��d��*pkx���|����qXA��b��iL�)3T�.PI�;�����))��6�e��oza��=�'�'n?S���%��FG��U�]W���<���q��d��V��I��=� � � � � � �G��?K�$�%��OL��}��=�.��yX���vU�%W*���f��c����r�Rq�k��NWB����������MzSi�1���K�!��P���^'�}��>R��$�qr��Ey�����2��]��w���\�B����`t� � � � � � ����2�/���A2e�TA���c�:�|S�o<t6d5D�����wY��\,�w�c2��6�#�:y
i��KJ����]�I�b���#B��������\�����#��}����#�|?��PW��G��~����G��~�����<�|���<�|����>h����~�/��uRy�K6�?�.����f���V>gA�SOD@@@@@h�{g����J*�d��K���.�ML�g0�7��<?P5��Ry�M����[�n ��5������G��t�qxo�`����$�Z������6~�Q��[f�G�
'���x�~���%��4B@@@@@h�����l9C��Kdy��67!�o��������!9���5����*i�9�1�����l�_&�������=��fUZ~����n��"�X|���oZ��X:��d��Xz�������m=�X��rN� � � � � � �4,`-����Y���U�}I����NR�����������J����� �o����w@#�+� w��["-�[��]0W���a�3��i�����6k��������6���
�h����|�Y#ZM��@@@@@@�����J�����84�|��+]�Kl����[<��X���v[�9�������Du%�i�%�u[sB�a���7����i�I�;! l.'a������8@@@@@��%`����I��y�#9 �����Ak�A���ZAM�mT��w�@@@@@W��� <t���0F����0ZX��y����=���r��uR������`�9K�K������EZ��)uK�y'���k:hT�1F�
��@ IDAT�fmY��g�8�y��d��s�t�/3�����
t��Uw%������|{���e�6[�\���=��n�>C��G�k�^u�����W9�4��\�]�����������Q���eM��f_}7�R���_�W*��]�^�]��.Ei������4wY���[�od����M�;G�aW����~��Z�&�W�qt����q�]�UM]z��u��^�nZ�2�����w�����������1)C���p�8@@@@@�%�2���������$uj���;��B*��"����K3��������������$M{��n���S�����K\u�uFd8���Z7o���P��P���P��(]����/v��v��B���)���$&�_g���}��|@XC�����MK������C�����I�`|B����Ve������I�+��J;@@@@@�O�e*����E� �M��G�����e5����Wv�Wz��b�n��W+/<F������?�Q'?Z�������5��y���T����4qRZ�z-�?zg�<w�����6MHS�5�_��R-�W�:]�M�����-������S�i��������[�4M���nb�-�@���[
�	h�y�4��e%H�]����z7_�:����1��q��G���Y���Fqt��t���vI����9&6���Q��i�5�4��Qr:��c�s%��y�uid�b���f � � � � � �t*��27����U�0
�����)�%������a�2P�e8���V[{����t������.!����^wu���"���~����gm�_������P�z��l, �*wC�~^�E[�R~��mV9�4PK��v�������!u��a��IC���i�?k���.w�
,z�qc+����Z�u���z���L���3����0kx���eG��:V�R�{r���Y���@@@@@@ *����T�rEmk�P(��d�N�C!�u�����&������l�(W�j������St��R���+*���?���Q�=��,2�����FT����W����]�����el��wj���2�vxF�����J�:ad�]U:B@@@@@��� �������NZ$ \�U'?f��o��y�[' �O��W���J����Z��D+wI�C������_v��%F��=���/J���eK6����:K4�����R5uJW]2��4�I�	�*A	[����@@@@@h5*�]A��kw��AXs)���G��^7o
/)Z�gn����K�F�>�n�������3=2�_FX8��/��_���X�f��W�c��y�����}����@JZ���6�/��@@@@@8�������h�.��zt��,�n�U6�ti�����B��&
���~����1O�i�%�4��t
n�����T����4qR�2�!�+��/�4��4��f	�[^���_��W'V����������5��F��z���Nc^�j�������5I{��pI��2�P��,1���:���w� =}�U�x�R�m����<~�����w�+��:=��m��yc��}���$a�/"� � � � � p�Z5 ��^Y �2����NUs��
G�~����{i������,Me��4���#	[������-�~���h����z��L�g�fn����^�nZz�a�JnQ���EK�Zb��e/l�e�"���W��n�Uw����4bC�V�j�]{]��F�'����Q��h��t���{+Z4�8�Vn��|=h� � � � � ���#@@X}-[���/ ������h�����K/��)���/���qi��}z��B-����%��_f��q��>�Ix��m����@KW�hK���;Eg��Q�_��~��UT���wi��bm��������.�,o���O����;U������z}�f��' �T�6W>�t����������qN�%Bu�S�^������qnM�,C��e���#����0�Ds@@@@@�!�!��81 � � � � � ���h�����;|F������rw���5�@@@@@@�T��+{�-1Z�2�;{�%�Z�^q���h�'�5"��-�l�+kt��{l���1v�����'1����}��<�EG@g � � � � � p	D�vm��'i��[�:���������$�$�������-jEa�r� � � � � � �f� ��tu+�+��	2YS����y�!?���d�I/k��RG���vP�;C�N�����#@@@@@@��)`�5@I�j�������J��k��w��9fm���<�gJ�x�L��&�o��oz�������M:q��:&���9w��2�� � � � � � �@�0XdK�R���((kss��������'�=v��Xu\�!�J���9��u���]8��T�`��}�\��JB�A��z�������[�������|����@@@@@@��%`H�&K��2�����/���2��&���(��0�5@�������KT\�������`0��`��7J���;��*_~K�^Y�\{�,}�7����1��&�����������&��|>�����|?�����G��~����G��~�-���<�|���<�|p��������<���<���O@@@@@@@�LV�;�7!#��7�A~������������f
��@@@@@@@ v��A��������{�A�� LZ=K������� � � � � � � �,orw�
���}�� �������w��h��H��������r@@@@@@�@����|�P@.=��7�������|vg�O�j�-#� � � � � �%��UR���|���
���2
@@@@@@bh�
���b�?G� ���Y9�^�r�I��,��_Whc�Yc�M���K���Zs+vh���P;���d�tI;M<���M���U��Ds7�%�u��D�tf;]5����s�����p�e(��$u�����������8���Y	���>���Ww��r[�x�f\�G��n�Os�,��Vh�����]��o���K�����<��=3M�^�����a4<�Mc��4D@@@@@8Z���W��0��[��0n0�s�G{k_�����vj
��[������^�S���%���|����:�UG�����_��/64����=��SO�{%Z@8��_�V��U����
�Uc�����G����@@@@@@�"�B���5X��S>�e������������O��-~+W����?f�o'x�����5�����V�����z��<��������C��R�^�WO��T�
�J�������{�+�������5��������
�2.CO^�����>������^A(�.����Gh����{m��v~�=�W�~Y�;+��S���)�����+�1 � � � � � �G�@�=c��Z��P�z�/K8����\W������w��k��J�P�=��p�~����r�~:����h�i�vR��^�4|�zB��N�������'R5�����h�%F�c��>�W�L���XkT�^��S��uR��	J�u�G����@@@@@@�"pxV����ohU��_�����*��R���:�{1p���dL&��Y(�R��
���U��0��l����t�1��2��B��+�e�"�0Y]�:cZ�zM�������{�q���O��=��'�s��y���MU ���Y�x��t!6}�T��Kc���I��x�.�?���S��g���X��#��c^��Tw�_�Q~���s��TE��%�)N�vt�����F����w @� @� @����9o���w7/���77z,��n��p�{�o�s>�x�o��Y�0�������9,�5�Y���K @� @� @�4#p
� l���� ��
����?�,��
���|������[�N=�����;w���5/>�cY<���9������w�[�F�o�:o���N��N�+�]�y#@� @� @� ��aaa��Gc����*�.���OEo����sq<r{��^�}��Wlx�-�����\gy����O<uk<��-������h��07�.n�����c1��P|���=?�-�]uC������D����_wm����;�^n���x���(F��p��?�^<G'l':���G_;w���~,��u��y��g�R9|!�����Y��}�	 @� @� @��)����S�~���c������'�y���}���';�����>�����M�-F����������j?��]kA{����
�Q�����������h���w�7�a @� @� @�R��ET�~��g,�����b�0?�V\�?8W��������7�#�7�&_��������?_�#gj�>��#n���c1p��<��Z,�s[w��w~|�Ksc��������C��$t���/�=�)�����3���w�������a�z����g��W����#1��o\7/����������� @� @� @����@x��^���_)��O�=��J'�,����"]{���.�C� @� @� @ �)� ���������'b������d�7bn��� @� @� @����c���G��_����t  @� @� @��lR'� ��'���y�m1z)v��w���#q�����T��8��a�7�� @� @� @�4"0�@X}�t�t=_�1z���:� @� @� @��4�]q��O7�	�
���������;� @� @� @���F���;���I��SK�S.���&a���� @� @� @��h���[V�*���68�|��0�����"��p<|j�|0���* ��<�����������������������o��<7�[��peM� @� @� @� ��@gg\Z��[�����'�*���vt��|�8� @� @� @����[��w�]�	��LU ���t�p���/�D @� @� @��|����m{c'%�N��p�w�����H� @� @� @�@s�g�8������ T l��9� @� @� @�@����>I��1/G @� @� @���
��� @� @� @���R��E�%F[�����&� @� @� @������ @� @� @�f�@�7_)�2���7�bs @� @� @��T
����L� @� @� @`f	�,�A8�n�� @� @� @��v=g�7��O� @� @� 0�R�A������e	 @� @� @�f������~ @� @� @���,�AX?�#	 @� @� @�\=��=0 @� @� @��	�|�P���� @� @� @��
���G @� @� @��))�z��m� @� @� @�J�����E\,� @� @� @�d%���������
6�����/E��CM_�� @� @� @�4&0�fM���NJ��@X��r��/��$l�8� @� @� @�@]�1��U������
6���2����$F��y�>�&��`>��CU@�y �������G�������������|�R�AXG-�! @� @� @��D@�0'7�0 @� @� @�d!�@���k @� @� @�����z(�!��f"���nt{��&��`>��CU@�y �AE@�Cy(��<�<�<���h���7sR�5 @� @� @�Y(f�� @� @� @�r"�@��a @� @� @����P=� Ld�=��!n���0���|�
�y ��<��Cy(��<�����������aeX� @� @� @� ����� @� @� @� ���a��A� @� @� @ 'z������8���=��!^����`>�Uy ��<�y(��<��������z�z�z�U�� �I��0 @� @� @�d!�@���k @� @� @����aNn�a @� @� @��B@B=� ��0�5����=�k�|0���* ��<���" ��<���PzxZXX��z��Y�a]� @� @� @�@Nsr#� @� @� @�@
�Y(� @� @� @����A����z&������xmB����`>T��<��@T��<���P�C��C����VYx�0'�Z� @� @� @� ���a��A� @� @� @ '
�9��A� @� @� @ =�P��P�D��C����M��|0����<��@����<���P�Cy�y�yh=`=`=�*�ofQ�u
 @� @� @�9P ���0 @� @� @�Y(f�� @� @� @�r"��
z�A��#{��C���	a>����P��@�yP���P�Cy(=<���Ze=�
��Tj
� @� @� @�@
�Y(� @� @� @���(��F @� @� @��,� �CAB=Ycq{��C�6!���|0��@�y *�P�CE:>�IDATy(�����������@���A�E�5 @� @� @��D@�0'7�0 @� @� @�d!�@���k @� @� @�����z(�A�a"��!nq{��&��`>��CU@�y �AE@�Cy(��<�<�<���h���7sR�5 @� @� @�Y(f�� @� @� @�r"�@��a @� @� @����P=� Ld�=��!n���0���|�
�y ��<��Cy(��<�����������aeX� @� @� @� ����� @� @� @� ���a��A� @� @� @ 'z������8���=��!^����`>�Uy ��<�y(��<��������z�z�z�U�� �I��0 @� @� @�d!�@���k @� @� @����aNn�a @� @� @��B@B=� ��0�5����=�k�|0���* ��<���" ��<���PzxZXX��z��Y�a]� @� @� @�@Nsr#� @� @� @�@�������IEND�B`�
#2David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#1)
Re: [PoC] Reducing planning time when tables have many partitions

On Fri, 18 Mar 2022 at 23:32, Yuya Watari <watari.yuya@gmail.com> wrote:

I found a problem that planning takes too much time when the tables
have many child partitions. According to my observation, the planning
time increases in the order of O(n^2). Here, n is the number of child
partitions. I attached the patch to solve this problem. Please be
noted that this patch is a PoC.

3. How to Solve?

I think a better way to solve this would be just to have a single hash
table over all EquivalenceClasses that allows fast lookups of
EquivalenceMember->em_expr. I think there's no reason that a given
Expr should appear in more than one non-merged EquivalenceClass. The
EquivalenceClass a given Expr belongs to would need to be updated
during the merge process.

For functions such as get_eclass_for_sort_expr() and
process_equivalence(), that would become a fairly fast hashtable
lookup instead of having nested loops to find if an EquivalenceMember
already exists for the given Expr. We might not want to build the hash
table for all queries. Maybe we could just do it if we get to
something like ~16 EquivalenceMember in total.

As of now, we don't have any means to hash Exprs, so all that
infrastructure would need to be built first. Peter Eisentraut is
working on a patch [1]https://commitfest.postgresql.org/37/3182/ which is a step towards having this.

Here's a simple setup to show the pain of this problem:

create table lp (a int, b int) partition by list(a);
select 'create table lp'||x::text|| ' partition of lp for values
in('||x::text||');' from generate_Series(0,4095)x;
\gexec
explain analyze select * from lp where a=b order by a;

Planning Time: 510.248 ms
Execution Time: 264.659 ms

David

[1]: https://commitfest.postgresql.org/37/3182/

#3Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#2)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear David,

Thank you for your comments on my patch. I really apologize for my
late response.

On Thu, Mar 24, 2022 at 11:03 AM David Rowley <dgrowleyml@gmail.com> wrote:

I think a better way to solve this would be just to have a single hash
table over all EquivalenceClasses that allows fast lookups of
EquivalenceMember->em_expr. I think there's no reason that a given
Expr should appear in more than one non-merged EquivalenceClass. The
EquivalenceClass a given Expr belongs to would need to be updated
during the merge process.

Thank you for your idea. However, I think building a hash table whose
key is EquivalenceMember->em_expr does not work for this case.

What I am trying to optimize in this patch is the following code.

=====
EquivalenceClass *ec = /* given */;

EquivalenceMember *em;
ListCell *lc;
foreach(lc, ec->ec_members)
{
em = (EquivalenceMember *) lfirst(lc);

/* predicate is bms_equal or bms_is_subset, etc */
if (!predicate(em))
continue;

/* The predicate satisfies */
do something...;
}
=====

From my observation, the predicates above will be false in most cases
and the subsequent processes are not executed. My optimization is
based on this notion and utilizes hash tables to eliminate calls of
predicates.

If the predicate were "em->em_expr == something", the hash table whose
key is em_expr would be effective. However, the actual predicates are
not of this type but the following.

// Find EquivalenceMembers whose relids is equal to the given relids
(1) bms_equal(em->em_relids, relids)

// Find EquivalenceMembers whose relids is a subset of the given relids
(2) bms_is_subset(em->em_relids, relids)

Since these predicates perform a match search for not em_expr but
em_relids, we need to build a hash table with em_relids as key. If so,
we can drastically reduce the planning time for the pattern (1).
Besides, by enumerating all subsets of relids, pattern (2) can be
optimized. The detailed algorithm is described in the first email.

I show an example of the pattern (1). The next code is in
src/backend/optimizer/path/equivclass.c. As can be seen from this
code, the foreach loop tries to find an EquivalenceMember whose
cur_em->em_relids is equal to rel->relids. If found, subsequent
processing will be performed.

== Before patched ==
List *
generate_implied_equalities_for_column(PlannerInfo *root,
RelOptInfo *rel,
ec_matches_callback_type callback,
void *callback_arg,
Relids prohibited_rels)
{
...

EquivalenceClass *cur_ec = (EquivalenceClass *)
list_nth(root->eq_classes, i);
EquivalenceMember *cur_em;
ListCell *lc2;

cur_em = NULL;
foreach(lc2, cur_ec->ec_members)
{
cur_em = (EquivalenceMember *) lfirst(lc2);
if (bms_equal(cur_em->em_relids, rel->relids) &&
callback(root, rel, cur_ec, cur_em, callback_arg))
break;
cur_em = NULL;
}

if (!cur_em)
continue;

...
}
===

My patch modifies this code as follows. The em_foreach_relids_equals
is a newly defined macro that finds EquivalenceMember satisfying the
bms_equal. The macro looks up a hash table using rel->relids as a key.
This type of optimization cannot be achieved without using hash tables
whose key is em->em_relids.

== After patched ==
List *
generate_implied_equalities_for_column(PlannerInfo *root,
RelOptInfo *rel,
ec_matches_callback_type callback,
void *callback_arg,
Relids prohibited_rels)
{
...

EquivalenceClass *cur_ec = (EquivalenceClass *)
list_nth(root->eq_classes, i);
EquivalenceMember *cur_em;
EquivalenceMember *other_em;

cur_em = NULL;
em_foreach_relids_equals(cur_em, cur_ec, rel->relids)
{
Assert(bms_equal(cur_em->em_relids, rel->relids));
if (callback(root, rel, cur_ec, cur_em, callback_arg))
break;
cur_em = NULL;
}

if (!cur_em)
continue;

...
}
===

We might not want to build the hash table for all queries.

I agree with you. Building a lot of hash tables will consume much
memory. My idea for this problem is to let the hash table's key be a
pair of EquivalenceClass and Relids. However, this approach may lead
to increasing looking up time of the hash table.

==========

I noticed that the previous patch does not work with the current HEAD.
I attached the modified one to this email.

Additionally, I added my patch to the current commit fest [1]https://commitfest.postgresql.org/38/3701/.
[1]: https://commitfest.postgresql.org/38/3701/

--
Best regards,
Yuya Watari

Attachments:

v2-reducing-planning-time-when-tables-have-many-partitions.patchapplication/x-patch; name=v2-reducing-planning-time-when-tables-have-many-partitions.patchDownload
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ce12915592..7faf634e9b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2636,6 +2636,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_members_htab);
+	WRITE_NODE_FIELD(ec_not_child_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 60c0e3f108..35271b75e9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,6 +31,144 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
+/*
+ * Constant, state structs and enum for looping macros below.
+ */
+#define LOG_OF_MAX_NUM_SUBSETS_OF_RELIDS 16
+
+typedef struct
+{
+	const List *list;
+	int			i;
+} EmForeachListState;
+
+typedef enum
+{
+	EM_FOREACH_NEXT_BIT,		/* Proceed to next subset */
+	EM_FOREACH_NEXT_LIST_ENTRY,	/* Proceed to next entry */
+	EM_FOREACH_LINEAR_SEARCH,	/* Standard linear search */
+	EM_FOREACH_FINISHED			/* Loop was done */
+} EmForeachBitStateStatus;
+
+typedef struct
+{
+	EmForeachBitStateStatus 	status;
+	EquivalenceClass		   *ec;
+	Relids						relids;
+	int32						bit;
+	int							num_members_in_relids;
+	int							all_members_in_relids[LOG_OF_MAX_NUM_SUBSETS_OF_RELIDS];
+	Relids						subset;
+	List					   *list;
+	int							i;
+} EmForeachBitState;
+
+/*
+ * The following three em_foreach_* macros help enumerate
+ * EquivalenceClass::ec_members.
+ *
+ * See the comments below for further information.
+ */
+
+/*
+ * em_foreach_not_children
+ *
+ * Optimized version of
+ * =====
+ * ListCell   *lc;
+ * foreach(lc, ec->ec_members)
+ * {
+ *     EquivalenceMember *em = lfirst(lc);
+ *
+ *     if (em->em_is_child)
+ *         continue;
+ *
+ *     content of loop...
+ * }
+ * =====
+ *
+ * Usage:
+ * =====
+ * EquivalenceClass *ec = ...;
+ *
+ * EquivalenceMember *em;
+ * em_foreach_not_children(em, ec)
+ * {
+ *     content of loop...
+ * }
+ * =====
+ */
+#define em_foreach_not_children(em, ec)	\
+	for (EmForeachListState em##__state = { (ec)->ec_not_child_members, -1 };	\
+		 em_foreach_list_move_next(&em##__state, &em); )
+
+/*
+ * em_foreach_relids_equals
+ *
+ * Optimized version of
+ * =====
+ * ListCell   *lc;
+ * foreach(lc, ec->ec_members)
+ * {
+ *     EquivalenceMember *em = lfirst(lc);
+ *
+ *     if (!bms_equal(em->em_relids, relids))
+ *         continue;
+ *
+ *     content of loop...
+ * }
+ * =====
+ *
+ * Usage:
+ * =====
+ * EquivalenceClass *ec = ...;
+ * Relids relids = ...;
+ *
+ * EquivalenceMember *em;
+ * em_foreach_relids_equals(em, ec)
+ * {
+ *     content of loop...
+ * }
+ * =====
+ */
+#define em_foreach_relids_equals(em, ec, relids)	\
+	for (EmForeachListState em##__state =	\
+		 { FindEcMembersMatchingRelids(ec, relids), -1 };	\
+		 em_foreach_list_move_next(&em##__state, &em); )
+
+/*
+ * em_foreach_relids_subset
+ *
+ * Optimized version of
+ * =====
+ * ListCell   *lc;
+ * foreach(lc, ec->ec_members)
+ * {
+ *     EquivalenceMember *em = lfirst(lc);
+ *
+ *     if (!bms_is_subset(em->em_relids, relids))
+ *         continue;
+ *
+ *     content of loop...
+ * }
+ * =====
+ *
+ * Usage:
+ * =====
+ * EquivalenceClass *ec = ...;
+ * Relids relids = ...;
+ *
+ * EmForeachBitState state;
+ * EquivalenceMember *em;
+ * initialize_EmForeachBitState(&state, ec, relids);
+ * em_foreach_relids_subset(state, em)
+ * {
+ *     content of loop...
+ * }
+ * =====
+ */
+#define em_foreach_relids_subset(state, em)	\
+	while (em_foreach_bit_move_next(&state, &em))
 
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids, Relids nullable_relids,
@@ -69,6 +207,21 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
 											Relids relids2);
+static inline bool em_foreach_list_move_next(
+	EmForeachListState *state, EquivalenceMember **em);
+static inline void initialize_EmForeachBitState(
+	EmForeachBitState *state, EquivalenceClass *ec, Relids relids);
+static inline bool em_foreach_bit_move_next(
+	EmForeachBitState *state, EquivalenceMember **em);
+static void InitializeEcMembersHtab(EquivalenceClass *ec);
+static void AppendEcMember(EquivalenceClass *ec,
+						   EquivalenceMember *emem);
+static void ConcatEcMember(EquivalenceClass *ec1,
+						   EquivalenceClass *ec2);
+static void DeleteNthEcMember(EquivalenceClass *ec,
+							  int index);
+static List *FindEcMembersMatchingRelids(EquivalenceClass *ec,
+										 Relids relids);
 
 
 /*
@@ -364,7 +517,7 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ConcatEcMember(ec1, ec2);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -379,6 +532,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_members_htab = NULL;
+		ec2->ec_not_child_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -439,6 +594,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_members_htab = NULL;
+		ec->ec_not_child_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -573,7 +730,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
+	AppendEcMember(ec, em);
 
 	return em;
 }
@@ -645,7 +802,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMember *cur_em = NULL;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,17 +817,42 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * Ignore child members unless they match the request.
+		 */
+
+		em_foreach_not_children(cur_em, cur_ec)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
-			 * Ignore child members unless they match the request.
+			 * If below an outer join, don't match constants: they're not as
+			 * constant as they look.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
+			if (cur_ec->ec_below_outer_join &&
+				cur_em->em_is_const)
 				continue;
 
+			if (opcintype == cur_em->em_datatype &&
+				equal(expr, cur_em->em_expr))
+			{
+				/*
+				 * Match!
+				 *
+				 * Copy the sortref if it wasn't set yet. That may happen if the
+				 * ec was constructed from WHERE clause, i.e. it doesn't have a
+				 * target reference at all.
+				 */
+				if (cur_ec->ec_sortref == 0 && sortref > 0)
+					cur_ec->ec_sortref = sortref;
+				return cur_ec;
+			}
+		}
+
+		em_foreach_relids_equals(cur_em, cur_ec, rel)
+		{
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
+
 			/*
 			 * If below an outer join, don't match constants: they're not as
 			 * constant as they look.
@@ -711,6 +893,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_members_htab = NULL;
+	newec->ec_not_child_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -798,17 +982,23 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMember *em;
+	EmForeachBitState state;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * Ignore child members unless they belong to the requested rel.
+	 */
+
+	em_foreach_not_children(em, ec)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
+		Assert(!em->em_is_child || bms_is_subset(em->em_relids, relids));
+
 		/*
 		 * We shouldn't be trying to sort by an equivalence class that
 		 * contains a constant, so no need to consider such cases any further.
@@ -817,10 +1007,28 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			continue;
 
 		/*
-		 * Ignore child members unless they belong to the requested rel.
+		 * Match if same expression (after stripping relabel).
 		 */
-		if (em->em_is_child &&
-			!bms_is_subset(em->em_relids, relids))
+		emexpr = em->em_expr;
+		while (emexpr && IsA(emexpr, RelabelType))
+			emexpr = ((RelabelType *) emexpr)->arg;
+
+		if (equal(emexpr, expr))
+			return em;
+	}
+
+	initialize_EmForeachBitState(&state, ec, relids);
+	em_foreach_relids_subset(state, em)
+	{
+		Expr	   *emexpr;
+
+		Assert(!em->em_is_child || bms_is_subset(em->em_relids, relids));
+
+		/*
+		 * We shouldn't be trying to sort by an equivalence class that
+		 * contains a constant, so no need to consider such cases any further.
+		 */
+		if (em->em_is_const)
 			continue;
 
 		/*
@@ -1560,6 +1768,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
 	ListCell   *lc1;
+	EquivalenceMember *cur_em;
+	EmForeachBitState state;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,17 +1780,16 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
 
-		/*
-		 * We don't need to check explicitly for child EC members.  This test
-		 * against join_relids will cause them to be ignored except when
-		 * considering a child inner rel, which is what we want.
-		 */
-		if (!bms_is_subset(cur_em->em_relids, join_relids))
-			continue;			/* not computable yet, or wrong child */
+	/*
+	 * We don't need to check explicitly for child EC members.  This test
+	 * against join_relids will cause them to be ignored except when
+	 * considering a child inner rel, which is what we want.
+	 */
+	initialize_EmForeachBitState(&state, ec, join_relids);
+	em_foreach_relids_subset(state, cur_em)
+	{
+		Assert(bms_is_subset(cur_em->em_relids, join_relids));
 
 		if (bms_is_subset(cur_em->em_relids, outer_relids))
 			outer_members = lappend(outer_members, cur_em);
@@ -2336,7 +2545,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			DeleteNthEcMember(cur_ec, coal_idx);
 			return true;
 		}
 
@@ -2857,7 +3066,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMember *other_em;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2880,11 +3089,10 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		em_foreach_relids_equals(cur_em, cur_ec, rel->relids)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
-			if (bms_equal(cur_em->em_relids, rel->relids) &&
-				callback(root, rel, cur_ec, cur_em, callback_arg))
+			Assert(bms_equal(cur_em->em_relids, rel->relids));
+			if (callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
@@ -2896,14 +3104,12 @@ 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)
+		em_foreach_not_children(other_em, cur_ec)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3235,3 +3441,407 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * em_foreach_list_move_next
+ *		Update EmForeachListState so that it points to the next
+ *		EquivalenceMember.
+ *		Return true if the next element was found with assigning it to *em.
+ */
+static inline bool em_foreach_list_move_next(
+	EmForeachListState *state, EquivalenceMember **em)
+{
+	int length = list_length(state->list);
+
+	while (++(state->i) < length)
+	{
+		EquivalenceMemberListEntry *listEntry
+			= (EquivalenceMemberListEntry *) list_nth(state->list, state->i);
+
+		if (!listEntry->deleted)
+		{
+			*em = listEntry->emem;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * Helper functions for the em_foreach_relids_subset macro.
+ *
+ * em_foreach_relids_subset macro is based on enumeration of all possible
+ * subsets of the given relids. For each subset, we look up the hash table in
+ * EquivalenceClass and iterate over the corresponding EquivalenceMembers.
+ *
+ * This technique can be written like the next pasudo code.
+ * =====
+ * int num_members_in_relids = bms_num_members(relids);
+ * if (num_members_in_relids < LOG_OF_MAX_NUM_SUBSETS_OF_RELIDS)
+ * {
+ *     // We will use the special optimization technique
+ *     for (int bit = 0; bit < (1 << num_members_in_relids); bit++)
+ *     {
+ *         EquivalenceMember *em;
+ *         ListCell          *lc;
+ *         Relids             subset = construct subset from 'bit';
+ *
+ *         foreach(lc, FindEcMembersMatchingRelids(ec, subset))
+ *         {
+ *             em = (EquivalenceMember *) lfirst(lc);
+ *             content of loop...
+ *         }
+ *     }
+ * }
+ * else
+ * {
+ *     // There are too many subsets, so we will use simple linear search
+ *     ListCell *lc;
+ *     foreach(lc, ec->ec_members)
+ *     {
+ *         EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+ *         if (bms_is_subset(em->em_relids, relids))
+ *         {
+ *             em = (EquivalenceMember *) lfirst(lc);
+ *             content of loop...
+ *         }
+ *     }
+ * }
+ * =====
+ *
+ * EmForeachBitState and related functions provide the state machine for this
+ * algorithm.
+ */
+
+/*
+ * initialize_EmForeachBitState
+ *		Initialize the given EmForeachBitState.
+ *		This function must be called before using the
+ *		em_foreach_relids_subset macro.
+ */
+static inline void initialize_EmForeachBitState(
+	EmForeachBitState *state, EquivalenceClass *ec, Relids relids)
+{
+	const int num_ec_members = list_length(ec->ec_members);
+	int num_members_in_relids = 0;
+	int i;
+
+	state->ec = ec;
+	state->relids = relids;
+
+	/*
+	 * Enumerate all members in 'relids'
+	 */
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		++num_members_in_relids;
+
+		if ((num_members_in_relids >= LOG_OF_MAX_NUM_SUBSETS_OF_RELIDS)
+			|| ((1 << num_members_in_relids) >= num_ec_members))
+		{
+			/*
+			 * There are too many subsets, so we will use simple linear search
+			 * instead of the special optimization technique.
+			 */
+			state->status = EM_FOREACH_LINEAR_SEARCH;
+			state->i = -1;
+			return;
+		}
+
+		state->all_members_in_relids[num_members_in_relids - 1] = i;
+	}
+
+	/*
+	 * Prepare for the special optimization
+	 */
+	state->status = EM_FOREACH_NEXT_BIT;
+	state->bit = -1;
+	state->num_members_in_relids = num_members_in_relids;
+	state->subset = NULL;
+	return;
+}
+
+/*
+ * em_foreach_bit_move_next
+ *		Update EmForeachBitState so that it points to the next
+ *		EquivalenceMember.
+ *		Return true if the next element was found with assigning it to *em.
+ */
+static inline bool em_foreach_bit_move_next(
+	EmForeachBitState *state, EquivalenceMember **em)
+{
+	switch (state->status)
+	{
+		case EM_FOREACH_NEXT_BIT:
+		CASE_EM_FOREACH_NEXT_BIT:
+		{
+			/*
+			 * We need to proceed the next subset.
+			 */
+			int i;
+			const int num_members_in_relids = state->num_members_in_relids;
+
+			if (++(state->bit) >= (1 << num_members_in_relids))
+			{
+				/* Reached end */
+				state->status = EM_FOREACH_FINISHED;
+				return false;
+			}
+
+			/* Clear Bitmapset */
+			if (state->subset != NULL)
+			{
+				for (i = 0; i < state->subset->nwords; i++)
+				{
+					state->subset->words[i] = 0;
+				}
+			}
+
+			/* Set flags */
+			for (i = 0; i < num_members_in_relids; i++)
+			{
+				if (state->bit & (1 << i))
+				{
+					state->subset =
+						bms_add_member(state->subset,
+									   state->all_members_in_relids[i]);
+				}
+			}
+
+			/* Update state and begin iteration on this subset.*/
+			state->status = EM_FOREACH_NEXT_LIST_ENTRY;
+			state->list = FindEcMembersMatchingRelids(
+				state->ec, state->subset);
+			state->i = -1;
+			goto CASE_EM_FOREACH_NEXT_LIST_ENTRY;
+		}
+		case EM_FOREACH_NEXT_LIST_ENTRY:
+		CASE_EM_FOREACH_NEXT_LIST_ENTRY:
+		{
+			/*
+			 * Iterate on the current subset.
+			 */
+			const List *list = state->list;
+			const int length = list_length(list);
+			int i = state->i;
+
+			while (++i < length)
+			{
+				EquivalenceMemberListEntry *listEntry
+					= (EquivalenceMemberListEntry *) list_nth(list, i);
+
+				if (!listEntry->deleted)
+				{
+					/* Found */
+					*em = listEntry->emem;
+					state->i = i;
+					return true;
+				}
+			}
+
+			/*
+			 * There are no more members in the current subset.
+			 * We need to proceed next one.
+			 */
+			state->status = EM_FOREACH_NEXT_BIT;
+			goto CASE_EM_FOREACH_NEXT_BIT;
+		}
+		case EM_FOREACH_LINEAR_SEARCH:
+		{
+			/*
+			 * Simple linear search.
+			 */
+			const List *ec_members = state->ec->ec_members;
+			const Relids relids = state->relids;
+			const int length = list_length(ec_members);
+			int i = state->i;
+
+			while (++i < length)
+			{
+				EquivalenceMember *member =
+					(EquivalenceMember *) list_nth(ec_members, i);
+
+				if (bms_is_subset(member->em_relids, relids))
+				{
+					/* Found */
+					*em = member;
+					state->i = i;
+					return true;
+				}
+			}
+
+			/*
+			 * Reached end
+			 */
+			state->status = EM_FOREACH_FINISHED;
+			return false;
+		}
+		case EM_FOREACH_FINISHED:
+		default:
+			break;
+	}
+
+	return false;
+}
+
+/*
+ * InitializeEcMembersHtab
+ *		Initialize a hash table in the given EquivalenceClass.
+ */
+static void InitializeEcMembersHtab(EquivalenceClass *ec)
+{
+	HASHCTL hash_ctl;
+
+	if (ec->ec_members_htab != NULL)
+	{
+		return;
+	}
+
+	hash_ctl.keysize = sizeof(Relids);
+	hash_ctl.entrysize = sizeof(EquivalenceMemberHashEntry);
+	hash_ctl.hash = bitmap_hash;
+	hash_ctl.match = bitmap_match;
+	hash_ctl.hcxt = CurrentMemoryContext;
+	ec->ec_members_htab =
+		hash_create("EquivalenceMemberHashTable",
+					 256L,
+					 &hash_ctl,
+					 HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
+}
+
+/*
+ * AppendEcMember
+ *		Append EquivalenceMember emem to the EquivalenceClass ec.
+ *		This function modifies ec->ec_members, ec->ec_not_child_members,
+ *		and ec->ec_members_htab.
+ */
+static void AppendEcMember(EquivalenceClass *ec,
+						   EquivalenceMember *emem)
+{
+	EquivalenceMemberHashEntry *hashEntry;
+	EquivalenceMemberListEntry *listEntry;
+	bool						found;
+
+	if (ec->ec_members_htab == NULL)
+	{
+		InitializeEcMembersHtab(ec);
+	}
+
+	listEntry = palloc(sizeof(EquivalenceMemberListEntry));
+	listEntry->emem = emem;
+	listEntry->deleted = false;
+
+	ec->ec_members = lappend(ec->ec_members, emem);
+	if (!emem->em_is_child)
+	{
+		ec->ec_not_child_members =
+			lappend(ec->ec_not_child_members, listEntry);
+	}
+
+	hashEntry = hash_search(ec->ec_members_htab, &(emem->em_relids),
+							HASH_ENTER, &found);
+	if (!found)
+	{
+		hashEntry->list = NIL;
+	}
+
+	hashEntry->list = lappend(hashEntry->list, listEntry);
+}
+
+/*
+ * ConcatEcMember
+ *		Append all EquivalenceMembers in ec2 to ec1 while updating as in
+ *		AppendEcMember.
+ */
+static void ConcatEcMember(EquivalenceClass *ec1,
+						   EquivalenceClass *ec2)
+{
+	ListCell *lc;
+
+	if (ec1->ec_members_htab == NULL)
+	{
+		InitializeEcMembersHtab(ec1);
+	}
+
+	foreach(lc, ec2->ec_members)
+	{
+		AppendEcMember(ec1, lfirst_node(EquivalenceMember, lc));
+	}
+}
+
+/*
+ * DeleteNthEcMember
+ *		Delete the EquivalenceMember whose index in ec->ec_members is the
+ *		'index' argument.
+ */
+static void DeleteNthEcMember(EquivalenceClass *ec,
+							  int index)
+{
+	EquivalenceMemberHashEntry *hashEntry;
+	EquivalenceMemberListEntry *listEntry;
+	bool						found;
+	EquivalenceMember		   *emem;
+	ListCell				   *lc;
+#ifdef USE_ASSERT_CHECKING
+	bool memberToBeDeletedWasFound = false;
+#endif
+
+	emem = list_nth(ec->ec_members, index);
+	ec->ec_members = list_delete_nth_cell(ec->ec_members, index);
+
+	Assert(ec->ec_members_htab != NULL);
+	hashEntry = hash_search(ec->ec_members_htab, &(emem->em_relids),
+							HASH_FIND, &found);
+	Assert(found);
+
+	/*
+	 * We mark the corresponding EquivalenceMemberListEntry deleted
+	 * instead of removing it from the list.
+	 */
+	foreach(lc, hashEntry->list)
+	{
+		listEntry = (EquivalenceMemberListEntry *) lfirst(lc);
+
+		if (listEntry->emem == emem)
+		{
+			listEntry->deleted = true;
+#ifdef USE_ASSERT_CHECKING
+			memberToBeDeletedWasFound = true;
+#endif
+			break;
+		}
+	}
+#ifdef USE_ASSERT_CHECKING
+	Assert(memberToBeDeletedWasFound);
+#endif
+}
+
+/*
+ * FindEcMembersMatchingRelids
+ *		Looks up the hash table in the EquivalenceClass and returns a list
+ *		containing EquivalenceMemberHashEntry whose em_relids is equal to the
+ *		'relids' argument. If there are no members satisfying the condition,
+ *		NIL will be returned.
+ *
+ *		Note: Please confirm the 'deleted' flag in EquivalenceMemberHashEntry.
+ *		Further information is available in EquivalenceMemberHashEntry's
+ *		comment.
+ */
+static List *FindEcMembersMatchingRelids(EquivalenceClass *ec,
+										 Relids relids)
+{
+	EquivalenceMemberHashEntry *entry;
+	bool						found;
+
+	if (ec->ec_members_htab == NULL)
+	{
+		return NIL;
+	}
+
+	entry = hash_search(ec->ec_members_htab, &relids,
+						HASH_FIND, &found);
+
+	return found ? entry->list : NIL;
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a6e5db4eec..bdf85b042d 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -19,6 +19,7 @@
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "storage/block.h"
+#include "utils/hsearch.h"
 
 
 /*
@@ -989,6 +990,9 @@ typedef struct EquivalenceClass
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	HTAB	   *ec_members_htab;	/* Hash table for ec_members */
+	List	   *ec_not_child_members;	/* list of EquivalenceMembers
+										   which are not children */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1044,6 +1048,34 @@ typedef struct EquivalenceMember
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberHashEntry
+ *
+ * Key and value of EquivalenceMember::ec_members_htab.
+ * The 'list' field contains EquivalenceMemberListEntries whose em_relids is
+ * equal to the key.
+ */
+typedef struct EquivalenceMemberHashEntry
+{
+	Relids	em_relids;	/* hash key --- MUST BE FIRST */
+	List   *list;		/* List of EquivalenceMemberListEntry */
+} EquivalenceMemberHashEntry;
+
+/*
+ * EquivalenceMemberListEntry
+ *
+ * Entry of EquivalenceMemberHashEntry::list.
+ *
+ * Note: Before accessing emem field, confirm that the 'deleted' flag is false.
+ * If the value is true, the corresponding EquivalenceMember no longer exists
+ * in EquivalenceClass::ec_members.
+ */
+typedef struct EquivalenceMemberListEntry
+{
+	EquivalenceMember  *emem;
+	bool				deleted;
+} EquivalenceMemberListEntry;
+
 /*
  * PathKeys
  *
#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Yuya Watari (#3)
Re: [PoC] Reducing planning time when tables have many partitions

Yuya Watari <watari.yuya@gmail.com> writes:

On Thu, Mar 24, 2022 at 11:03 AM David Rowley <dgrowleyml@gmail.com> wrote:

I think a better way to solve this would be just to have a single hash
table over all EquivalenceClasses that allows fast lookups of
EquivalenceMember->em_expr.

If the predicate were "em->em_expr == something", the hash table whose
key is em_expr would be effective. However, the actual predicates are
not of this type but the following.

// Find EquivalenceMembers whose relids is equal to the given relids
(1) bms_equal(em->em_relids, relids)

// Find EquivalenceMembers whose relids is a subset of the given relids
(2) bms_is_subset(em->em_relids, relids)

Yeah, that's a really interesting observation, and I agree that
David's suggestion doesn't address it. Maybe after we fix this
problem, matching of em_expr would be the next thing to look at,
but your results say it isn't the first thing.

I'm not real thrilled with trying to throw hashtables at the problem,
though. As David noted, they'd be counterproductive for simple
queries. Sure, we could address that with duplicate code paths,
but that's a messy and hard-to-tune approach. Also, I find the
idea of hashing on all subsets of relids to be outright scary.
"m is not so large in most cases" does not help when m *is* large.

For the bms_equal class of lookups, I wonder if we could get anywhere
by adding an additional List field to every RelOptInfo that chains
all EquivalenceMembers that match that RelOptInfo's relids.
The trick here would be to figure out when to build those lists.
The simple answer would be to do it lazily on-demand, but that
would mean a separate scan of all the EquivalenceMembers for each
RelOptInfo; I wonder if there's a way to do better?

Perhaps the bms_is_subset class could be handled in a similar
way, ie do a one-time pass to make a List of all EquivalenceMembers
that use a RelOptInfo.

regards, tom lane

#5Yuya Watari
watari.yuya@gmail.com
In reply to: Tom Lane (#4)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear Tom,

Thank you for replying to my email.

On Mon, Jul 4, 2022 at 6:28 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'm not real thrilled with trying to throw hashtables at the problem,
though. As David noted, they'd be counterproductive for simple
queries.

As you said, my approach that utilizes hash tables has some overheads,
leading to degradation in query planning time.

I tested the degradation by a brief experiment. In this experiment, I
used a simple query shown below.

===
SELECT students.name, gpas.gpa AS gpa, sum(scores.score) AS total_score
FROM students, scores, gpas
WHERE students.id = scores.student_id AND students.id = gpas.student_id
GROUP BY students.id, gpas.student_id;
===

Here, students, scores, and gpas tables have no partitions, i.e., they
are regular tables. Therefore, my techniques do not work for this
query and instead may lead to some regression. I repeatedly issued
this query 1 million times and measured their planning times.

The attached figure describes the distribution of the planning times.
The figure indicates that my patch has no severe negative impacts on
the planning performance. However, there seems to be a slight
degradation.

I show the mean and median of planning times below. With my patch, the
planning time became 0.002-0.004 milliseconds slower. We have to deal
with this problem, but reducing time complexity while keeping
degradation to zero is significantly challenging.

Planning time (ms)
| Mean | Median
------------------------------
Master | 0.682 | 0.674
Patched | 0.686 | 0.676
------------------------------
Degradation | 0.004 | 0.002

Of course, the attached result is just an example. Significant
regression might occur in other types of queries.

For the bms_equal class of lookups, I wonder if we could get anywhere
by adding an additional List field to every RelOptInfo that chains
all EquivalenceMembers that match that RelOptInfo's relids.
The trick here would be to figure out when to build those lists.
The simple answer would be to do it lazily on-demand, but that
would mean a separate scan of all the EquivalenceMembers for each
RelOptInfo; I wonder if there's a way to do better?

Perhaps the bms_is_subset class could be handled in a similar
way, ie do a one-time pass to make a List of all EquivalenceMembers
that use a RelOptInfo.

Thank you for giving your idea. I will try to polish up my algorithm
based on your suggestion.

--
Best regards,
Yuya Watari

Attachments:

figure.pngimage/png; name=figure.pngDownload
�PNG


IHDR�����sRGB���gAMA���a	pHYs���o�d��IDATx^��XG�SM1���k4��4Kbl1���j�FE��-X���k,��.T��
"�B�(
�  �0���{����;�������������yoon��AA
4� � ��AAAD�� � � "@�FAA�@#� � �P�AAD(�� � "hAA
4� � ��AAAD�/]UU5r�����A���pj7	HKK{����@���$�}����=��5����������e�������������������+�T���@#AA�:���	����a������ �pT�@�JZ�u����AA�����GSA��������c1��~A�N�WIL�U���M4"H���Q]�u�� � H]�]��@��C���,//o��M}/(�� R��@�5(��UqBT�.�E�QA�hA�cP�Bl�{��rv�v�����[�����	�4���qqqT�Z������H�����!8����7l�0 ?���������������*c��3��{�5����{��TA�=��p��dd�����fee��;�D���-���t �n7A�BP��qc�����EVA��r���<<�X7�"�
���)Sd]��}������-1B�EWv)�hE5�(Kl�V���K�B���L�
z��qT$�{���
P^^.�(���{�AA�h���e���3���c�i��������d�g{�\S!�UO������="�O@Vai[�-7zw���0**
!���I��{��� \x�	h��O

�?>��������4���^��]K"��O�M�I����K�`����
�"!~~~r��AA�h.�.0n@����BJ6�5*�Z���nD�(�� ��� %&7{4��%����yc�?�Y����]��#��r#3Z �*�[�B**e@n|�i�!u�HAD��;��EVP�����!�����(Bnd�n��\��)�n7u��h�%���rD&1�V7��8`W%IY�A9�/�AA�h&����C#p����3g�L�<�6I�F���> ��x%�ao*fX��x��B�+��!9��1s��}�e�RVT�b��#e��5A�rp
�}��hY��J	�x,@�	1HXU���KT�)'��*7��~��!%�]��T%�Au�� � �����i�P�D�h@��������CV9�J�j? ����`$���=�����!YR�@�J��u#AA-����U
XZZ��
��]�-�TC��!nT�7�K
�fd�W
eQ�@�J����AA�h~h-��w��*��J�j�= ��)�
��c���E�@�r����WTV��0�R�AE�L��F� � Z
4?d��c;��A���F��DJ����F��h�AUt#Q�UqBZn���r���a��0�PTVb������F#AAm�Z�C��2�����)����b�t#:�m]$�S��F��%��x�.R���Yb��������������Qh���F#AAmZ������/�c���m!�b�N����~o��j�: WV�� �%sJ���c��8�������[�n%���=:K������I-0v!�!����eEQ`U��+:������ � ���w-�
62�rG}��&CCC�t�r�"##��B��
O�8��T���p�In��EC�Au7~,���47PP����%8���mNm������9�JlU
?�n4A�ZP�� �E_����-�s��+W����qi�S$��vvvd����!�X:2�677g<�P�	9�����@C)YZZzyyQQ���q��]j����[���������������QV����6�����HAD��FuA4]�G)�@#� �{�@#�8�?�#p�6FAD�@�Fq��d�S�8r�AAADCnB�{���� � ���� � ��O�s���>�AAAAAD�� � � "@�FAA�@#� � �P�AAD(�� � "hAA
�ptt$V��=$���j����=���GPP������7�>|��(���6���A4^�:\�uy/}��
�����5�B4�K��F�;���h�"�E�J@k���_�@�V�{�xM���t�
�(���-��*B���-�]��5Bh�ZS�@����=�Z���*���L�tp�T�u�Ce�6�
����X����Q;kV#�74o���a�����@�V�M(//o��
o�z
��}�,�f�>���;���-h*�E�2����(�j@�@�����{ii)T?A��B�������YXX@�}����(�A���^/��;M��D	��N611q��a��`nn���Em��6tFUUUvvv$2\6S�LQ$�BR&dggo����H���Gr�2`t���gG����ihh��h��4;��D���Cg��s��+W��[�Bbr=���M.Bj��J@�^222d[/�;#���E��*^�lH�����Pn�X�%/] )C���E�*���EA��@����O�)�U�bv�)B	)l%���/�� �����1���Fx3r�*o��([���([U�P@�/R@l�/����FU�@�V�Dc�����TFL���{7�G�������
}Is��G���ow�2}�t����)*I��P��/��$l��S��Y�rc#��9P����t�Rv���w�E��E�EU ����-y8���������H�P�����b�[�J_R���	����U�.7�B�EE����
"���}A�����3�z�7c���(j�4�e+7�\���_��;��AKU�Z
h�reD������I3�v��~vd��%���_(2��)��B����)���GH��5O?�>+��$����������"���9�MG���f�����Zf�J	��^r���#G�$��l89M@n
w��[����U���M�9Z,��
�N��1/]����(h���K�'l8g�z��M��N�n�pDZ)d/**
!���I o�r�Dv�T/"@���{�*G�#0�6889�V�+�KRW/RG��8:���=��~����>���4/F��"}=���4���q�����
�JYn�O ���_�I��Dd#���q��]��	����(��-{��c��B�.
�����Z#�M�T)�j5���U�\��c�!B�������ewg��},Q�#�kA��B.��r[�"T�bv���(
�������%��B�{q�3��"���`��}D��V����D�'��r��`NT�����H�}8�yu	hC��_�@���7�����-^N_cr����&Q)s��~����#2r+�$����8��@r�q
��~��)7���A�r��Zf�J	�������7��pQ�_QE,g6�J^�@������(���^�W1����h�tf�J�.R�� �����^D�ZG9��~CT��:5��m���^_���������V5��
� -��S���Q�e0�5���"�����vOLL<s�����!��"����#�GY������22������"�H�~��@xL�]����
o-�Q��^E�����a��Q�L��]�d_�KP��*G����G��B.$���I���}vJ�pW@
DH�
�����NY�"�Z�_���[>��V�������~�r����������~�������2��5����4��;e:���R��2����������%���#*-�����/{��!�M�T
����L�d 0���Y�r���Z���%��v�{��_��32 ��k�{�%/Q��)�]r!�TT rQt,E���b��)"V8�T�5��D����EDG�F6�;7%�-�e���1PT�vV��G��r���u	(Q��6���^4��e���T��'��=���,��r_f���Z.��c�zyy����U���T�Ie�QQ�I	�������4��@��|����@U����Q���J��<�L�CE)����r�M����D
�s�LP����;*� E���b��)�]Y���(RFeq��v�*�F6�wnb�-�e�����]�$'�Y�.o)��HE�wd@��*W��5��
�P$��l��i*��/�������L.'��9Rn�-D�;������h%E��j�����J��8���Y�rc ���[t�e����v�(�U�l���D
�H������B.$���:E���U�>;%B�d)C_�	��.�]"�NY�"�Zd��`���|���*�F�N��D6����#e#3B.RQg'���FU��)j�� W#��^�#/�
]��2wd��l��G5��)+���t�@x�,��h|�&1��V��Ga����&��rkY8BJ�n��2Q�����a��Q��Fl�K��L'(����'���F�*fgX��"%�u}h��E�;7nx�Gx��~j�)��*]�8R&�	
�HE�o�e/�g�u5��
���I"���p�����=#�SV�w����"������ �J#����t��rtFR�}��i
�	�f��t�&���@�PQQ���Q��FT�K�8R���*����E��z��N�Fk����"e[�"�����E$����`���|D���W(�������_vc~vu���(�j��?�M�@7Y@����4�����Hg��O�����eQ�u"�\�������2��d>���Iq\��Z�KxL����[����,J�2�%�.g���H������#p�v��rf#����0�Qr8���B.�HE����)]���S1�qD��a����6@
����pw�t�j,"!�E����#�lE��\�t�"uv�"������Q�/P��i7��=�3Z����$/��'x�/xN��W��������o!b��S�/P�o
?"�>���*q�Gt:G�E`'E�*�2@����	�=;�-���B�����#w-�^��8��tLN����m3���
9!%/Q��h���� �x=h�.�o"dd�D�1�)�^D���B'+��R��[��OM�}���0��	y�����Q�/P��i|���n����l�c�V�-�
�o���5C����D��
��� ���3�A�M��@d26��������HG��&)���]��1322�E
9<q��#{v�D��@l-�E`	���>:�t�p^u���V��r����!?��Q�5�k�F�E!�B�h�"�}��Q��8�):9Sv�� ����6�.E�����uj�����n����v�f\�D�J�A�V��%
����?e���34D�#�e;Y����y������FH��~���<#���X�����F�/��H�~�|�ew������G.���������������(��>�c�Q{��[�rRR������fD�3���G%�������h�fQ�\H>��@Ejnn��T9vrM���Dv����f,��"'��A���\��x��}�����\4joT:
t�A��M�����@k���@�(D�H)@��7�@������.�'(����Ad����z
41��C�5(���A�E�H
t�����4S����	
47xQ �"�"E����MC�J:��k�R��N��
^��H)@�FAA�@#� � �P�AAD(�� � "hAA
4� � ��AAAD���TUU-]����G�:������'�>|�
�ExLmF�c_U��D=��>#� �h������
�@#J#���9�1�!����� ���@k5��7��(��*oE:�����'�� ���@k5(����@�AQh�Qh��@#� J��*��bFF��a�����e\\�E���dc���{yyQ�j!��{������uk��}������=z�1W�\YZZJm{�1�����s�������^���DoUTM�������j�Q��)S��=#Q�.���5w�\���p��5�YH�>���8����] ��$�>��!Mn�����l{�S��P�b���AA$ZU�qq����Y�C8����0�t�
4�g���#�a����6�w������;2����};PxL�DT
:::B���'U��1��v��M����T��z��q�����8q�Z�����9��&��6c@�oHP��f�X�Wn������|��]�c���=5��Hq� � R��*��FEEA ����I����IL10��YaD�-3=���K�$�d�s�)�y�?>��$!�{��c��;2��~����KL�����T��@T���A�^r�����\d���+�O�$1/]�D��mE�.�>'<��f,�x�
	 ����$�n$\�@����b��P�<��e%���5^5� H��*����2������s�P�����l8���U�v"J����m�dwY� ���G�In����k�v&F���������#���;vYr��wQB����Q�x��B}#YQa�Ff#�d���&�B�v1���AA�hUa��4d��8F;�f����h$f������ko�$�&���R����DT
�t'+7&�����d�"�����D@`L�za7cQ�KR��8J�l�-"6b[��EU��bx�bs� �H

���c���w>���[�B���	JLL<s�����aw@v�g��I��\{�@� ?��{�.��FxLn�Dx
H|F����������>@n�rwSl3^��w6�V�'p��
UT�j�jA�:ZU$���1���HY�y���1i��F�,�c�@�,--��X�c�R ��$���.v
�pG�{,�O�����"$��f,�xE5$ny�[�8..��.�B� �dk�S�Z@A�
��(���!;dr�������c���|j�G���~h��TB���1�'{>����,@x
�0�D��&Q�����H�.Bb�m�J\ �����A��8Y]\��]T�**Fn�S�Z@A�
����������D�w��A�;������s�N��>�:"<& �@�5H��3�\�Jn��R�)r[��]����������QAr�{���]T�2��n�AD"P�UE��*)�0���,IAv�Wd{�t��=/���{��\�c�-�5HC��cw��$[�j<@n+��������h���WTC�����da���b^��*�"��AA�
���@�����i8Fqz��v:�cg��'A���t�����El���A������s�3&��d?����e%w�1E������
��H��[q4��[��+`��W
� �H
����:2{�&� c�24r�$��<���DLv���8)��	�)��"�i�<�&2'U��0�}�� �1�5� ���"0&"��*^:����(t8��ea���b^��D��r�� "(���i�~���&B�)����}�����#�!Y��G������_����H��,{
�v���d3LgUxL�
Dl
��G�D�t�E*�@�:@�Y��ExL:{��D������v)�/��p]H���P�bT�U� ��(��"���1��Kz	t��
?��@.��$�}!���M,Q1���p�dd�K(*R-�c�"�@�� 
�
��/���U��}�'�(Ru� ���ExL���#ob/vC��>!�����?r+� �dE;y����I`�[��]5$�]�� �Z@�V�~@`<h�������n@��l�HL�$o[��
��51�L?�s<	KxL�yAd����!##�����0����1e^ �� @�G��"N�.(R��$�ZN��nE�v���r�p4$@�}�����qW
�d��NW(�i��{���B��#��
�P�A�A�D����nAA�hQ2��=�E(�� ��@#�2���K�
4� ��(�"zv)���-nP�AD7@�F�?k�����k�Pa�@#� �n�� � � "@�FAA�@#� � �P�AAD(�� � "hAA
4� � ��Z%���B��K?H����$-A����Z�t��G��u`���IQ���E���ijI�%/��}��U���I�xo������YXX@n	y��3��2�����������3(�*��:>>>p��

4n�g!$D:�{vl�H_n�Z�n�.�b�}8IZ��B�7wr������.�Pk�K�����sP��{�g��)����*�`��>��<S��N�����%������(�@K�~���b�����6�:��������=Z��@�E�>�}^uy�R���k���@�/�@�=�ZW�6�:��������=Z��@�E�>�}^uy�R���k���@�#���F�	�G������z��Z�����;;;���biiG�{����a�����������M>>>=z� ���V�\YZZJm�E��eggo���o��$& 72Iz�����s������k��!��";�0��s���l3`�U���(�P�����}!��>��f��g�Z7��
�n�P>pvu(�;�*�}8Y�f���)S8�2*�_�&�.jw�???���2,��*p�Vg�iu�2$!W�,t���p��U�����B��/�#�C�
�u
�
�H:tcf#�y��6��5#�]�=o� (�j�1
��{�����d��Y���t��]���h0c�.6cp��h4t���!�q��Q�k<q��"�#�9�Fn��;_�Bh�V������!�
,X��	J4�<�j��[2e�@G����!���� ����}�����a���%�������l"p�+*p�L����J��� �@���Yiu�2$!r+�D������w�dR�p��3���)�����(���*z����;G[����&�������G�����g��H��>����f� �K���088���q#	d|�I_����������D�|7J?*�N������~�,*ct�]}["�_UC�lG#�2���t��JN��1�����~~~�gM��D���+TT���`�������lx�\��4Z(v����%L�����M)*�}8����U&;<�w���upgO6�2��X�d�Z���*��� ��*?��k
��V�.C�c1*��l����;@v�����r�����A_�k��e�	W46o���\��\���l8�De/%�����7P���p�f����v���L�{ �c�&���1��l���2��> 7)���q\},�� *��]���&7��@8N�lb�8w��5�4�yS{�%qFu���m]td��*�}8��]Q�����;�������@4$�%&�*�:�8B���L�d��,�����e��t6*V���%�X_�Ef�r��pFcf�]�t�ti+��8�]B������OP����W2{"��^���d���'E�<���18�*v���*�Uq���E�������] ����8JO�f#6�jo�$����&����;��W���e��92Lob�N#�~E�2�L�J��F�V{�c�!�����������BP.��BU���T.��N��.mFCR�nx����\!h5����{��t/��C������cYMLL<s�����!&#2G����� ?��{�.������a����	!�]�;}����<������m���`gL�
bob��,���#AH��K�sr�@#�@��e��V�N��Xl�U���r�����FHd��sW.wC�X��J[Q8#ob��< v�A�
h5��q�����F�#w�@T�K�{�����F�H���d0��---��<��R.�F�������������l��Y��V��H`W���IWA�Mt���&#���� ${b�@lh�HT�S����2������U�?���bd��s_�$�c�E`�+*mEy`4<�mPn�A�
�h������Vv'�@��F�#��'����]���c���|j�(�#e@nJ����#�@��yl4�c)B��B�M��`��z��c�a�hP$�l����ITA�Mt���&�p�#AH��K`$"64b��Q�j�?���bd��s7�����"�����<�.����o�@�v'�@]=�D
`�3��`.�M�"���b����[5r���R.���By�&VD,�N_�f#6��m����lo]j� �&:�r3�(��H�=�2WT�$=�-���S�8+��,�b�����w{��E`�+*mEy`4<�mC��gp
��awR��P��E������+c�O�-���}�"|�=L������P�y�b`��z��g@���{wvupd����7q����#AH�x�tjh������S�8U*�;�d_�-\�� $�������!��tZl�P����(�j����������Bn����<��J�,8R�92�(*����!U@����	!b�����FT���[����:F�$� ����5�G$�7q��>5@`��F#�r��u ��` �"Tlu��9��F�J{��*#�������T.+�.1F��$��	ob;D�A�V3�#�r�b88�)0�vG <c�&1e#)r�$�~������h8��eOGT��FVT b�N_�f#*����-�;������K�
���6]k����W�����!l'#)p�
�*�:��>� �"Tlu��9��FT�3R{�+W�EV���pv�0X�ti3D��
4����!7W���7P����a ���XZZ���JLL$/:�������@%�����+��:1^�J���2 �����~ {�I�c		�g���G�MZ6{��&��8
DT����*�Fl���W%��� dwggL�
�8���2�p+����G'+�M���S��d~ .�u���)s���Jg��n���BP1���r"�%��:���J�K��+:�W��mCT��3(�jFn� ������@L!W2��0`���2&;~�=���I���8R��z/8�l�,0r�8l��ddd���a��'N���"<or�Nn�
��>�J��g)���F;N�>��CKn��� �&���.]�>(T�>���&<{N�.����V%��+Q�@?S�����2�������o_�lx�Vu���BP1������\��U:]\�\��tY�6<�m�����,���.1D�A�V3r{Y��P�C;w�������<�vg?�G���#��!���a��yq�L�o��]!�����	2`��0N����d�"6��R���}v�"�`	�JP�����t�V���[�����%�6�.j� �&����&��#��#�d��De�Q�����SV�*9�����lu��9��FV��6�����keT�����
�!�E�N�o����N�%����Y�T�Z� �G���3�%��<(�� "E��W�Ir'h���@#� �P���F	b�y@�z

4� ����Z��U$����k�1P�AD(�r�
�3�g6�c�@#� �P�i?������s�D�A�FAA�@#� � �P�AAD(�� � "hAA
4� � ��@����<??�1� �^ ���@uuuqq1	AD�(((�����D�@�:'''))�� �^���|��}�C����*---55��� �O��{����-����AD/y��azz�����>�_�>}

�AD�����~��1�'j�%�����
� ��������!W�a�(**��A�	���������,jAD�x���@?y��ZG�'�={��
4� �
4� hP��gP�A��@��� �>�� ��hA�hA6(�<�@#���@#��A��A}A�
4(���3(�� lP�y@�FD�A�Fa���v
tII	i�������C��
�TWW�����svLMM����6 �(@'���*'''%%:C��O���J#�������rj� z
4Z(���o�������O>i����������bj�`��:uj�n�`G''�����oA��h�^oo������g����������6+���;w8�j���W_}�w�^m>�P�y�6�������Y�f�}�5����Q�F:t��YYI^^^��W_��v433����{������A:&���������;}������:v���L�2���srr~���V�Z5o�|���C�m��i�6m@�a	A�������m��A_��k�;v$&&��ys��0���^�v���"P�m��Ao����?�x��Y�1,,l���080&&A���@���k�
&''<x�G��5��g��)����O��������[�a��-Z�l����T<A�h�J���>|x�&M�n�ZUUE

�N���A0���R(�p+W�l�����YBB��?����7�4�� �,�$�������+����/�@�N��������_�5��T��dgg��1������������������/O�4In�� ����V	����[�habb"��Czzz�j�
z���D*T��'N�����/[�����
�e���������9�jA�]����q��A/z��Q�9����144|��w�;F�Hjj��A�������j��o1o��� �
4�#�0�8p�A�c���a�
�%,,�k��=z��z�*$D1b�K/��k�.*�_v����[o���2D������
�+�
��J*+�>��95�����K*���<�,��~Z%BmC4�.	4t��������BCC��Z����������y3�"0
���o�������+++I`YY��%K�|���c�
��
��s��=�%�IeN~iy�>|������Ue��Or��?�,������9(�<h�@���,\���w�qss����Bk�~����:u�p�$��������_~y���T���RC��;wf�H�QR�4$:s����s���
�Lz>Mm�Jr��=����1a�_�l�� ����z6�V��%�>|�p��M���>|H����?g����z��?���^�����-[�l�����;�����!��]�6i�����"5����=eU��U��.�|*�F�Mwy��YqF��3�n,�iF�
��"��7���h����7n�h�"F�AWnnn��{�����
�v\�bl���/e�x�`oo�����@_�rZ��VT�����~�����V^��|�L���Q������No�8�e�m������P�M�K�g���[aa!TKQQ��U��w�5k��{�p�����u�:t��!C:t�.�n�:�
��p�z�I�&S.��`2��������6���'wc����<�}��yf���'y�&��A��A{�u77�F�������p�TWW4���^���
��������"�7���s7o����qqq�������9���
���Bi�_D����<z�s=���=���\0�qz������M����('�����<��"��{~�}����,4�r5���'��3'�	l��!�W4t&�=A�%�������kbb���w]�vm�&M�L��h6sqq���+�5k���ov�������n����%K���H
����#R�������&���������'��=��Gp|�]�;*�.���8+-������?%8%�����S���p�(BEB������G,�@��Uw����8�@��0QA/���-[LMMa�h��=Dz��=j�(:w��-�%%%iii0��G�MVffvv�����1��������Gx�<|���IE�U2�������1��C�|�����n�2��p0+7�S�i��	�'H�����g��
���-�>���b����?�o���#w/�����C:��+�r�n����9��O��h����I����=F��G	A����9t.p7��ke�9l����{0�P�����u�@��A{,���w�quue�������Ms���i��u����~8~����w�~��t�ME���0Rr4#DE��?�����f_�jn�����w�Mxx�;~��)���
\z���T|?���h]�#G�|����B�N����������o._��
��=  �I�&������Z��'��m=���� y����Dd=*:������.���L/-��*': ��8����E&��c����sZu����h��<��A������
�%,,$�g��AAAT��TVV�����/�`����>w��'�|�����4>+Z:.���u�3k;��.����VW7e$$��bgrmz����_��=��@���0���>		��j�~���_~y��-T��6mj���?�����/����G���K/-^���w�(�j�����>�6����������J�]��
\�����.����9���v�_44��n�j3R��@��=
\�|�U�V���11����*<z�h�-���#������1�=Cz����������w��&-�`���!VG��<0��s����yz�d���>��Nv0Hs���+e�kqf
�
�4�$�p"���o��������;w�B/z��*H���k������'O����0a���
t]����h������8�����3���`��4�U��E_I�>+��8b���s�d���5
4Z%�			�|�M�&M���o*�vv��I�4h6,�q�[�t��;����k�[�nQ���lbb��G������P��&*����+�~�N�=�lW�����6= ~�{���dt����Y�����m���%����X�d����>q�D�Z���~�W����,g��g��}�����kw��5*����o����Y�f{��e
���p0����l9�������o3�6��rl��u��E�^�~Z�/A�(�<h�@��������}{\\\XX��9s�g�����dee���W�^}����A8��#G��������r�Jtt������o��93;;���h��u������CC�����<~6 ���$�_5�}��'�3�:]w���:�l~�.�%������|��w��������={�7n�x��}��$���`��7o��u+(2���B��K/
>��������O?�D��1�v0@�V;��l�6`�I���~p=��dt�c��:LeqAv������u0�������)�p
��@��A����tttl��y��������}����{]�t�����EUPP0f���
DO����w�}���p����:uj����q�d�I+Zj�1������!N�l�|n�q�'�����\M��*��g��a���w�-/}x���h����c������C���s��������g��Aw�pj ���G��������@���I�O?��2d�g�}�������lP��NyeUlj�v��K������ST]�����lt�c����9n]c�S<�=�?O�(�<h�@yyy[�n	�~<x��� ���*..�3gN��mmmm!�T�?����L�<�C��c���.\��"h�
����g��w�������Y��D'���4��iq����7�}0�8�o���[��r�A�����|���I��t�������]+�E����w�=������K������Ycnn{��K�,������@KA�������E��`�@N��[���p��>��e������~��H
4Z(�@yy9d,--
���\��1�7����1T*�
���a�133������@KM������S{��|`�j_��J����+�
���lL��q�.����pH�tO���3��:C���O�.++�p�j��
�bB8�Ev$s�xA�V; ����:��7���C�cSU>�o��U�2�6�Hu��3�[��A�������W���k'(�<h�@�1(�RS^Yu-2�f�w�	��o��LzP�����.������T�w]�X��Z�=��@�1(�j'��=��~}���1�����������0V����$7�r�V���#�L?�'>�H��@��
�@KM��G+����mfu���>��D��`@�wA�U��s
�,4���,����6D��@�
��Y��j�	����;����<p��p=������wS<W�<�c���7.<-���Nj-(�<�@(�R��+b�4�6��j^�=t��s�z�*��}��;<�}�]���
�(��
���@�����[�'�������@?-)||"��I���b��O;����(|
��@��@����B�[4;�����	G.����E\NX;.����c�x��S��+��yv�
�@�
��	�q�n��!v�m|F����~O��+����;���>t���#����2�tB�hP�h��O����|�����@���+}#x��K<��U��n�R�*o�+o}ZuP��N~Q�������&�[��jD�}�a�S8l_|
�T|
��@��@����Yk�����s����{n?�/�yv6~��TG�t�D��;fe$R�M��:(�j�������	���&�]��jLJ�>���������R��r��f9w���7��&|
��@��@���[	����XX��?������]KI��[�q��F[��w�^�ng�z���hh�A�V;�Q�����������s|6�y���7���������'^��>��W���E����)��hZj�y��;���Y�C�B���N�<��@m��<�����7���).�\����LmC4
���@�����zN�4��Z�#�KC��^���6u���������`���������A��@���Ma�&8;�F���M>�y)�����%l�;�[��.1�}�YV�s���hh�A�V;�o
0�����k��B���<`�a�P���q�������������E���3P�y@�P��&8*c�
_��g��ls������9�6=�Iz|��P;���n������I.�
�4(����v|���V�
��`nsi���e;C�2�m�������+����l
o��~v3�sJ��@��
�@K����5�kD�k��w���3�����]'�l�n)�&�������!�ZuP��N^a������N�M�����q�z������mv�.���;�;v�[8$�����z��Z��hZj��t�N���m
����>p�f�5+�4�
�4(����v�J+.���X��=������<�#': ����1vM
��b����sZu���hZj6y�}>���Yd��~����9�)�k�g��u�!!��s������P�M��:(�j'<6k��k�f�7�y��y���~TDm�]'��X��]���F�sb�
��/�~VEmF�hP�h��z������v�o�7`�Q/}z
G���;����Op0L\>������m��A�Vh��zO�������y
��a6��7�R�t��'����'��)��0aN��{\�B�U���hZj"������/�[_�|a����t��&%�Yyi��K����2N���IzB�s���-�@�
��Y�����cg]R��~S<.��R�t���RS��������������1P�y@�P�������������O�����z\�>� ��</;�w_�����%n�����*���Pj9(����v|������@[s��#.,�z�v���txVY�8�Z�.�P;�[�C���,�I��!u
4(�
���V^K�������.��B���W�����`������e�1L��+y����z4\�A�Vh��(���7q���fS����}fq����.)s�d�uKw5LX�M��A|
��@��@������.�u����7�~��tW��\��i���>Yh���9��>�C{@�Vh�SRV��6i�)�I���JL���������B�M������f�S84
4(�
��l>�s�3��!�?"�;������6= ����Y��s�.0��b1�<'����hh�A�V;�,�����fVg��_�u2�a^1�Mwy�t������
������vJ�3���)P�y@�P������{O>6p�U�;������#�~���yF�,�8�.�n��<W���mh(����v��/����O��kuq����[��m�KEan��]I+�$9&�}��s��� ����
(�<�@(�R�i�����L+�!6g�o����GS8���K��foZ�*���v�8AmC4
���@������8;x�Sh���}'��O���.���rd��M��E#���,)��!u
4(�
�����{_���-k^��]�^�}��`�;i�
r��:���P�AmC4
���@���)������{������6$�����.��V�'�%�q�3��`���{�r3�mH���
4�-5�7�M[v�|�y3�K�l�9���N~Hm�bv8GMo]�n����{f���S�6D��@�
��y�W|�b��ygM?<wc`P���r��J�K�??y~������%,�.+�(>�CS�@��
�@K���B{N82hv���9�}&>�Km�n{m�v����K���=�.Q����\��!�ZuP�����25=/&�A����Uz���G1������p���h��
���)P�y@�P��f��[����y
��������$Q���'wc�/��o�`����,��9�6D��@�
��yV]����'8��w������
j�N��vs��=�����5��������)P�y@�P��&*����+�~�N�=�lW�����6=�YE�����\�2N���IF[�M��:(�j'9����7�Z�0wh�"���IE��6��� '����?�Mq2Lv����:/�
����hZj��
V�����F�{n;��4�){t����������7�xt�bU������:(�jg�����O���5��j6�������m���������������-�&;���H�n�h(�<�@(�Rs1�����},��-l�g,�t=N�~[�p4����]�=�k���=q�o�w��m��A�Vh�3o������jcg�����>A)�6���ieA���}�����X�U��������AkA��@�����6��������{Yz�����5O����`�Q�S8\��S8�
h�A�V;{OG}=����������&-����*Y��q�������\�W����I|
��@��@����g�Y�`�?�!�����v'}�������bMs�t+X`��l�`hnt�
�4(����vn����X�w�g��y���;���#|{5lv��5O��o;�M�f|
��@��@����{y�;x�;o������~"*3G��$?�z���Y�������{��y�~�i(����v��V��7e�)���V��v;C/�����p��^������Z����X��f���
4�-5O��F��[��o�.�M��)�U3}E�)����9��8���������VQ����hh�A�V;i���G�q9c6���*��Wo?��;������_���$�6�v�O|t�"��D�hP�h��-(=��<y�w�)��V^��VTRIm��/���o��b�;����Q��_��oP�M��:(�j����o�������������6��������t�m���m����E|
�A����@��/�-"!���`*����!�h�h�A�V;�6���S8������>)}t�,����z����hZ��q� �qf��
��h�h�A�V;�ND�==��o�� ����?w3N�G�����9�Y�c��'\�8>�ii�
�sP�y�N��z�����7q�����'&&R8�����c���������m�&�OG���`*�o�������:(�j'�^���o�r:k6���?|O�%�?��7V<J?�%~��q3Z��uJ�<#/:�@k
h�M����80`��&M��j��i����O?��}����Z����>{���`�f���h�b��iQQ�o���@S�:Mlj���WZ��;����^+�����g��
�#����iot��s��{g6����t��
���@���J�S8�A#'���?��C�Bw������v��~VEmF�h�J�a��p�B���5j�{����;w:��W^;v�����x,��W�\��qc�fWWW�q���}��y����O����C�S
�����_�'d���/&a|�����*��3��|��� ����s��%���]������Z��@�
��I�.�u*��3f�����bHZQ�����Qt`���}��O��C�No�q>ZS�@��U
U���{���=�i��������������m��:��/C���Y�
�-[�����������>;u��RQ��-5����<����E��S��FgP�����N5o"t�}�k�P�N�:�&B�M���h�s�B�w.��l.C�9�������)�m�K����=�)��f�uKw5LX62���D�)P�y�*�

200h��=�C�c�7o�����'��9l��m��=CCC���������_^{�5b�T�<P��f��`�����;����<���mz@��}q���<�y��q����7�m���Q��h�S��q/>�C�����"?!$�o�;���_���w�#=���m�@��U}�����zk���iiiTP-W�\���C������� `�����;#���%���KQ�5��w���^l�`<`}i�����
'����f�������F�N�	;������[PmZ�@�V;[��4�L�������������/a��3SS��������ag+K��(�<h�@WTT�Y���7�����\Q������w������
z�?d�p���g��������j��]�v���2��;���������Js/�����.�~r��K���?�/�������F���{��8�����:{'��gh=Z�$��]{��H��f�O��������u���x��yyO��
�|q�o����jj3R��@��=
���S����)VTT����?�����T�����<y����S�Nvvv������nbb��k�u���������}�vnn.�D�NA~~Nn�_h�����Y����'4�N��������dANrD������f%���w07�n��"*�A��K/Q��z�X*+�~}��:(�j���*��]��U����?��|�|�����:t.p7��ck�5q�|
��@��A{,����Q�F����]d���A�^~��
6PA,rrr�����i��_,��w��Q|��1T�������A[A�K:,��7��x��z���IN�=��T$&=����?"�zf���7Js2w�*��dz�*�A���5
\����N]��@�Vh���S��\�Ong��vZw������:�d�r�B�L2�{
G��?��S84
4Z%���� �,`4�dl����-[��)))Y�f���~��m���'�X�b��y�z�z���,--9�G�1���rh.u����u������7����h{a����tj�����)��O��j�1���g����:��/�D�kl9�ZuP�����	?��`ns����������~Dm�]��n'���2���9�2\������K{�)�:Oh.��@C������O>9uJ�������9��S�=z>|8;;�<�����#F�x���f����.����
�^'P��?"\w �d����O�0��e�q�'��������7 �� o�a��a���
��S�t�M�h=Z���T����S8zO<xQ��QQ�8�j�N�0{��!���>�KmC�h�G��������
L�<933�
�%&&����������
�N����l777H�
����C����Att4�]("�0^R�u��Z�i��js����C�������.�R�������]N�����:��;����</�������z}ZuP���&��f�O��2��Z?+���N�F����2,���rl��u��E���ZZDmC�h�G��s��5j��_�~��/����O�v�G����faa&�}�v*�_"##��Moooh
�p'w���AV'�L9>�������t�9nr�O����6��(��s���^��gS�M��:(�j':����!f�O�;����=�c��R�t�����+�����s0�������I��)���������-[�c��jo�x���
�����1Pvv��	^y��y��1�@_�p�}��;v���B�� ���/�;d���/&z�;�����\=��p���1N=r�t+p7Nw��pXnL �
�4(����v*�V%���>�j�5�����C�����dd�C������t��m�|p�s|
��@��A�z��s�6j���_���(**��;s�L�N�>��cOOO����9>>�l0��������h����������XPP���H�z���`�d_��@K�)��1.g�m.�u����Ww���{�6= v�s��6jD�ff�%KG_�MM��)z
�TTV=�-��������~��.'�J��Q�S8�.0���*b�o�U\?aB���h�n��1p��w�yg��!+V�pvv{~��������CM?�={����?u�T��!�����C�:L�6m������}��}��w���w��e��"P�����{�C��-f�����������q��Lw6x8�[��I���oGR�t�M��V��:(�j��'�����~t9�v_xTrN��:���F(�����:��'t�����N�����)P�y�*�`�
�8qb�V�5j
6,�r�������a����!0����-[����?����s�?��3kk������
��x^��p���w�� s�K?���r�����6O��%�v
�3	����<vT<����T�F��3P��������N;�s�������+�nV�Mw�*/���K�fw��8r���3�J�x�B�H
4�&����o{zzn��q��->>>0�Qj��-((h��]���%%%Th�X_�zu���6l�}��;G��h�II�lg�`+����|mwb��Y��h�������bf��w�v�{����G���z�M�@�(�jg��@S�c�A�C���l��a��;�6��?%�i)�V�ZwY<��u��e\oAB$���:F#M��W�E�&�^����@O>
����s��K������-wn�'L����c��Km�-(}F��3P����W{L�4����V�N?�������A�g�z������-*L���ph
hP���^q�B��Y��m�9\3��<���^�M���]�Q�[��7|��4��s�M���3�6���gh=Z�\��1�C����/�q�����z�����QQ������|������)G�?��w�!4(�<�@(�R�v_��8������{���>9�����.FYn�)s�H�1��^<�M���Z�@�V;����R-��6����mW��>������&���������"�'M�f���lCmC�hP��45�C?�p�
L�i�ys�KgZ��LZ��o���x�0��@����6F�[��b�*J�m���(�z
���>�:AG�yuuuUe��
�Rp'*�R���1�t������?�U�2��R�ph��!1(�<�@hJ4��j(M��������0���e���S?)���U��97/�/����8q��'��a����T�F��3P��NPD������S8�w�Yw��������w�����[����gz	�}t�}���m��������:�
������X�}��_�}vVPO�E$�hZj�V=���_��o������n?���{�)�����0�o��a���?�UW_�M}��/R�3P������&7��J~D������Lj[��4+5���m��wN����u�-�6���q������NO�����-/D8����5i�6�F^�*��I���hZj��w���y���i�'-��q!!����yR����7�q�Z�n�����\�G���6D��@�
�r��?���}�B��3����.#l<:|p�C���a}�.u��i�#,{NG��w�����������P+��@��@���K�#f�6��y�����]��E*�;]�f|R�n\����k�0�����6D��@�
�r�?)?�;u���]O�:�Kv?��;�3C�.�������\O3"��G��6�/���RVQE%Z��*/��t��s��uDC�@��
�@K��}���y�]�*os�@}{
GV���%#�:d�uKv6M�<�IZ�M��&p������,eggC����WYYImP@UUU~~>Df)����0	1�


@�I���
�rT?^^Q���������,�9Eg��NY|�����w_��~\X��S���TTT>c��zLUYq�@o��
��
4�-5�����5��<p�Us������u���<I�O�3'��$xz��������x�Gm�-�6]�Zu=���r�3ft���K�.#F���w/�+�Y���NNN��A-]�v�O; �����B�2�\����h)(���s:�{�����A��P=ZK@��@�������lu��D��v�7����G�������������f�>��cI�C���@�ih	���:p�����|`jjjnn��e�6m����q�p��������Z�2x������w�����M�V\\�����DH
B����
� 2���_�-[����-�����,*���A�@��P�y@�P��&!-w���AV��L9�����������Z4�'�������d�I��A����0j�nA�ih	

{~������#""v����C��M�=zT�\��������Z�j���			���z��W�����[�V���j���[6��8$2�u��
�1Z"</'��z�Q~)���@k	(�<�@h2U��u���_��2���ln{�;�3�"t�lUIa~bhV�����5_\��rs���FE��g9w
�4z�,F��+�rn\(��W��@�3
�������@�����T�?��_���w�9r$�$��������i����#$���l��m����/'!�@��hD��@��
hD�u����2s�bSs�����0�Y�.t�a�������|�����8Q�������UF�){x�����������%l�W�s�����c������A���8!s�l����^���B�����B�!_�5��}���ZBBB@�?���+W��p233�M���aCWW���UP���|���M�`D������HKK+-�n(���hhP�h�y�W����]���?���1�2t���S���4xv�@���x|eu�g���K�_������������T<�.�
KIfjv�G����mM�v��F����g�)���,|���~����ghu��W/CC���*����������UU�2�����c�{����c���p*�6�����l��������6o����l��m�ds�-D�s�����3�1v(��h@#MM���)��UiY���Bc2�c������OE�:������/��q!>4�-4�~T����%:�{���������y�����}B��(�j�1OO��m�~���`�Th-������~��_�UV��Y111#F�x������[T��y#"" ��_�U�V�~���i��>��c777D�x
@������(���@����@S�Q�TC
����������k�%e���r���S�~SU��V��j�(�j���j��=��7���os�KKK����&M���7d�
U��-[�}�]cc�[�n�����������Q#���5A�7l���ukH|��U�����`�0� j$?������;�I���S3R�e�I	^02����\9�0G�Bff&�9�0hP���!,&������;��'+�h������u]��V_��V��Z�l9|�p�ZKyy����A|g���pk6�����W���O��]�v�����T��o�inn����:55FD�>~������%�}�<|��y�v��oc��~�������D�dgg�_�8>�@�%��]7�L���|O���:C�>R/z����tzz:ZKii������}�_�N����;v���3yt��^�jdd�x{{SA�x��aff&���2�#���Z��+Jo,�~_��aP�y@�P��hh-�^��>r�H�6m�
v��m*�����>����7r?1,|���������ceg?s������v���<y�
�����|
���P�y@�P��h����h�@C�"""z��ajjD��>=n�8�x;����d�� ���rC��������III ��~���S�� y�@K>�� (�<�@(�u
��4���U��~�


���Y3*��������~�aXX�d700>}�4�/P���9r����e���Y={���� y�@K
4�AP�y@�P��hh-�^tuu�������ncc#��:P�F�
2$;��]UVV�y�h�b��qqqT���9.\�j�{�����T�?����[YY�����'Of�5h�@�F4
4(�
t�������|||�u���,_�<22299������� ��w���:55u��{�����#!@qq��u��4i2m�4�p���0###���'^�v
N����s��}�����kw��Y*�P�%� (�<�@hj����HE(�(�ZH}����M�6��6k�l��!?��C��?������C7NE����;w�*�~�:T��9+++tvv����Be())�������a�^�zM�0�����}���7o�}�7
�D�@#�h@#���@�@k!�E������O?���M��-[���w���iii��Z<==?�����[���RA��;e���������
bQUUu���i�����j�
L}��������T��@K
4�AP�y@�P��hh-�	4�U������LMMe?����0>>>11������^0���qOe����h�xLLLvv6���6NP�%� (�<�@hjN��P����%��	
�D�@#�h@#M�F�R
A�F��BP�UZ"P�
��
4�]7�@��@S����T�h�@�F4
4(�
t����]A�Vh�@�F4
4(�
t������@�
�D�@#�h�n@��)Z
���@K
4�AP�y@�P��h����:(���hh�S����v���h��e��yzzr����"88x����������/_��Tzz:{��A��P�Q��h�A��hD��@��m
6|�������Y�f���������M�Y�f%%%Q1X��a��:4n�v��)���+0��h�"((Z��@�@k!(���-(��A��A�j+88�w��o�����#7o��������4h����^Q>��o������F�U��-[�o��d���;�����G��s�[h�>9�HEo@��NP�UZ"P�
���V	tvv����{������FGG������7m����CT�����������_Bii��+ )33���phTlyhD��hh-ZuP�%� (�<h�@�������u��>>>T�?�TVV�:ujNN�GBB�s�V�v���1y��]7�@�@k!(���-(��A��A����3����/c�����?���~����y�
�FDkkk�'O����K�*F#MM��)z
�v��:(���hh�G�a���qc�
~��7F����MLL������� �TTT�<y�e����7����B9��@S�Q�TCEP�Q��h�A��hD��@��=
���K�F�.\������~���n���
RLzz���c�w��4�<i�C��>	��[�A�S3
�u=#��1���u]��V_��g�@K
4�AP�y��.**ruu�^�`A^^ZKUU���_{����7SA����o����~x��y*�#���JKKa��3(}~�%j]�yVU�v�;�c��s���@�QU���x�x,l������.U���S�W������.uy�@�
�D�@#���"��
z��W6n�H)�����������O��QRR���r��/�J�_z�Z�s���e����u���d��p�[{�������[%Z�����&�"��y�'���}�n�C*���z.������b�R�
���@K
4�AP�y��)����7^�h��@������e����SA
�~�:�����=
���;�pP�C�A��K/Q�uKQ������U��u����2 <u�������<�`l�hy������:gr����o����.�L��j�R���I��_�Z,�!p��6I�,{e�w�P�\'��j]�@�����@�
�D�@#������u�����VVV��/��*))����[�n���T�8��a������}�
��0Rr��v(}�����g�}6z\��ys{�,[�����o��}�wm;��*�G�p�zPd&u�2�d$��z���	Vm�f��b��j=�u����~kU�:�U�M��Y]��>���:fz��SZ��B61�U}���Moy���S�����N��4��P�@��hD��@��=
�:u
������;??����0 ""�
�(�����/�<e����|*TD����V�hhH52s��q������2���T�/��]jVM�{��|�x\�_�$�������nr��
u�2�Kq����g���(\�]��hq�\w�'����2����E�R���g��Y��]�!&����������MnLi�||u�u��Q��h�@�F4
4Z%����FFF�����j��u��5j�h��i�Z=��?���l������
�
t��'��:j:��`��A��Zf
�0h�����2�1��S�uV`�V:����A!]>�t{u�2��7�3���L�X��>v7��jf����A������M��Mo;���ag�`�IML���/�G7��-h�A��hD��@��U
]���-����������*++CCC����������D�����d���Dv���7�����C�K�.�����&O����z��c=&��6�)X�%d��U���`<+`�s���A������[{q�V��M�Z ��n�x,����1�:hX��A������z��k�\X��lb�%�� {���Lh��@�
�D�@#��h0fcccp�������o�����������S�"I4���K������3G���.\������QTT$��f�W��u�QXI������3���i'��	�Y�
�u���c}&����b���ZH3D�
K�C/0��bn���M�x�P�.Q��@��qQ��h�A��hD��@��U
���yyy
6��>�����?���)S�DGG�#\AA���~����m�����!�"PA�@��n<;`����'�?��`��C�B�=��x������W���h�@��\��f�F��.W��L�{.\$�=��-h�A��hD��@��mM�q����kmllf��������Qj)//?q������C�d����@�]�vQ����@S84<�Cj���M�<`�������,l/�����<�1H�{�d�"��%��0��K��.7l��1��q\Vdu-(��A�Vh�@�F4
4�)�u�F�

�F����]��8�����g�G�����k����gX�c
�B�\w�T'���_
&������A�c��S8�h�A��hD��@��
�@K�u
h{��d�/~��o������:�q5,��/�^��f��y���i��7x4�&0��$��+�t�����W�R���`�q�?���VEC��(
���@K
4�AP�y@��X��
u�uMi�A����v�����~;��f.��������n	�����Y���!6�dY`�h��mg�`���l
���y,JbU3�#sk/u-5G7
�s����@�
�D�@#�h@z��/]��:�H����7��gnX_<;���\���O?9�!�S�K�P��n��+�E	�f}4�M�u���R-����<�S�������k���u���s�UW:���P�N����>��:gD(���-(��A���G��-�p��O{�M��7��d$��w��^�v~>��~S����x��G�j�)G�M=��EmK�)������� ��ex��<����m�
�I�$:&;&92�A ,�U6\�K���W'�H=�S8x@�Vh�@�F4
4(�
�D(r�	�zO<���v��p�����;�h�	�a��j�]��p4��q��C��=����m]Zj�����T�!?(���-(��A���G���)�!C�,l.��8��*��n�ha�ca{q�c0c���)u�;�,���@�
�D�@#�h@�.~DH?���>.C���G�(�j�j�(�z
�D�@#�h@#M�����Bt�H�M�/5���
���@K
4�AP�y@�4"���ZS
4
� P�UZ"P�
��
4���
4
� P�UZ"P�
��
4���&p��M-(�(��@�Vh�@�F4
4(��F�

�
4t��g%�{,��{K���L��U�T�F��3P�%� (�<�@z,�������R[��!N���q7��]������<t��%g�a�|��s��R,9p�.��&[C�s�@}��/R�3P�%� (�<�@�)���:j:��`��A��f�Psc�2��Y�9�t������9�P���G�e����n��1��33P�%xZ���|x����sF��:(���hhP�=���'Cfl=|��/�>X7��x�>�����#v��`������B��U�O���y�+3�lV]-q{�_up��~F�DKf���k'��'Q��(ZuP�%� (�<�@z(�E�����?����:[���4���K�0��Z���p3�:gM�0���E#�+��u]���pss���(���-(��A���C����Y?8����Z�3���/^�8�Z�E�6��K{�����h�A��hD��@��
�@�
A�2�8z�f�S�z�^	��o����H�j7(���-(��A��@��o��@'�������+�:w�\^^Nm�bP�UZ"P�
��
4��&�Ej]@��+�&M�Dm�bP�UZ"P�
��
4���CP��M�����:(���hhP���Zz��������:(���hhP��45��p�
z%�m��%�k�dhh�A��hD��@��
hD��[����?
��������dU�'C�@�
�D�@#�h�n@��y���U��{�={�P�\�'C�@�
�D�@#�u	tvv6$E��7P��h�h6�&M"�\k'CK-����0���P��
�D�@#�����g��Y����i����~���Q�f�������_]�zU�
���Z��s����k�dh�4�:QQQ���[�x������������c����-:|�plllUU['@��hD��@� V����CCC��[��g�}���o����a�6m�4o�����z�������={N�>}�����md(�u
��M� @'�����(���������;x���>���W_�S����{l��������o�n���/��3g���gsss�=�9(���hh�40�����o8p ���311��������Ys���}���^����q��Q���0N|���'N<x���;w�XOE�]7�@���e����j������`77���;�1w�����z?�w���#��K�,�>}:��$N��=������@�������'(���hh
tee��������-[���k���/_��-***++��P�UUU�Oyy9����:uj��Y}���7n\HH����]7�@��@Z;Zu�{��o�����u�1c�l�������lII	���1�����������0::����O`����K�.��? !�@K
4�AP�y(�111'N���6m��o���*��y�f����S���Z�Zk'C�.�{��>|���3���`�dj�b ������������k��
�h�@�F4
4
q������J{���������u�C#M����
z.��vN�V]�<<<���C��!==������i-(���hh
4�c}����FZA��s��p2���c��Ph�@�F4
4Z.0�@�I�����>����G�^�ZPP@�(�u
4
4�m��Uh��������cbb<==O�8�{��-(��A��Ai������3������
����{�F�`\400��e�*
����FFFFGG��VQQAm����,--
v����s������F����S8�h��M��B�a���a��e����a544��o������}�]@@���
����	��aJJ
oo]%t����s@����@��hD��@���@�`�����'��1�[�B��=��IX���w�-�t��o�>Y,��5k�����e��m��������B������;w220`���B����&P�z
4
4A�&C�]�����{��=z��yR���y�����o��gO�'���>[��O�:��?@
�����x����4j�<""":v��&��i���m���g�}�j�*�Y|(���hh������3g���k&L���/))qqqy������``��a���G�.**�v�X3f�h��)t�?����_~�����m�v��]����,Y[[7o����o����a<X�d	�}h��Z���VE��*�3Z�`dd��G���_0���0�v��9r$,,l�����m�����n����wo�.]��-,,����k�������0R�X���N�8q��1?���q��/���Q#���ED�-(��A��A9�������Ixx8��4����j�����
.���ABBBmt��X�y��&M���������?���oCCC8(�Euu��e�4h��[�C���ZI�@�o�������(�u
���(�d2�z�Y�z5�<S�N%��w����i��#G�y���U;;;���������o��3���[w�������z��;w���Q�^���"---99�����4`���_~������*h�@�F4
4�	�����h��GR���`�]�t��V����sg�7�������>���]�v����3@����'�&4�X0����l�r���Th��_&O��3�����Q��hhY�d2�z:.'''������*�B�}�	����&88���>�NI��x�/\���W^���_��0���~u���111T��l��mffM)Z"P�
���r�����G��#G�����-�V������w�����[���r������������SA�Ux���V�Z
6L�-m��4k�l��i����?r#

�6��S8h�dh�������~�r�JX-**0`|���i�p���-Z�U+1�������eKOOO�9���p##��
=z�
�J#<<������>:x� �h�@�F4
4�	tdd$�l��m��=���9z����~{��uP��������533�����o�>i~��Pp*�������w��]�,������������������o���fgg���B���\P��h�!�����c�>�c����B����@���


�M�%�����.Y��}D�			���O������Z�������7o�Lqbmm
���i��*T1(���hh�hT�/_�j```aa�l�������V/_���_�������5WYJJJ����y�777������f���k�q�3��W^��e���I��u���������,d�C��P�Q��";:44�
�+�+�@BB������������4h����|�_�r�|����C�V+�>Tv���;w.t��������7n��I�3g�}h��O�~V^��
�@���@w���3gN�v�@ya�8|�0�7UUU�6m��o��F�s�`�rvv�^{��E�
���077�����T�P�p8h�3b�CC����n���M�6m�����+�\C"��t_u�1��p�VtjF}z����z�i��t�;T���e'C�m���_��v��/_�����6j������!�}����e����,Y"���g��dSS���B*�����U�VA�joo�{F�7��_}��_�U��@gffR+��8���W��g��1�� ����Q���@��Ai�`�3g����?<<��}
2IV�]���9���yyyTh-����
���<s�Azz:3t�o����?�|�����x�����A����
6@
Tly����gTg8����<~t�7j����7���r�P=���GE�I���*�nJN~Q�t"��.����7�x����W_Q�*���
��=z��v���i>x���' ����gO�:#!jz�w�}����1�A��v��&M�L�2�qs�M@@|�i���������(d�BI�#������O���x4.���\}�3>���{;d���GV?|\�'�Q+�7�������@�%��@�uP^^���yE����
lR�hH���6�}9�
�n��}e���1l��%�����F��4�P�D��=��t=1�QM��)���+x��������/,=�{;��������]:�q����"F�1��8;'�J�~B����x��_zi���T�j��|���qR4���J�!����5A�����c��=o���h���Py@ON~>t����o�:%%�*tDM@���x���Gc��?S�:��GwR���9�*;7�
D�Mff&���������
�ADD��]���]�?���f���B�B��`t�Q�a�����0�Q���(��W���;��8��w�}���/�3~�~��%���RA��1FJ��a�Z��������������,+�J�O�������u%���Q���ng��aj�����q������|�D��6�K����H42Z
�k�p���
�n�_�o���g���K���G�~��GFFF��/L��!�����7�X�|9�~���F�������s�%�L��-��
E��9�Z
4�	4+����!�n��M?���f���������
���G�
F�uF��-���0V�������������/���}��W�^�Py�t���j���9����J+s�>|\����Ons8$*3G^��y%yee��cLY���G����6�2��Y���3�?�$g��m��kNq�]����-/��*�z�_�Z�F&C�]�����A�{�=�%�v���������8�������C���*���~���_���GJ��455500����@���#B}��UOo.�>��Nj�(�<('����S�Ly��W��a$��{7���l���������.���m��
�)��~����7o���~�q�
��L�{����N���u
���C�n��A�� y���"C
�%�z=����'"�������g����o�����g��'���
����lW\�z�iU���S-TW��=�[x;��vD�����1��w��E��t��"��jF���[�	O����'T�V�U�>z���T���]���?oll���w�m��i���T�X��-[������ ����kii	�~��A�;���j����sT�6l��?x�`�ioP�%BOcW�#�{�#BM���r
���{w##���O���WWWW�HEE?P�����s��Q����m�'��6~��
,_�\�Mb8JXXX�N���kw��)*�v�srr����_��-E�V��������$��M��Ob,��������
����v60����.��I�,�����`��vnk���C�.�F��D-�*?�&�M���UQ�{�:~2�z�����Aa��JJJ��z-�[�(�hH��?�x�����Gw��C����I�&?�������
������-�#���������`����K���Wl{�,��h����{���j�z
4�	��3g�������2�"%%%����G]�t��qcdd���Wg����o���'<<�D�������/>~�8�@YY��y��~��=z���?::"�@cF�6mN�<�}m�@���O���{��G%=�z�~u�f���N;�0~��x���e^�U����S����^�-vqq�����-[�����K:�3f���Ag�}���]������"��!����k��Y�n]rr2��5j�+���;U�
�D�J?�d�S�)�(:���7���[���/�����c��.��0���y.���
4�	4��w�}�����*HMdgg��;�u��-Z�8p ����kdd���I�~���`���0�ZXX���o��mcc��iS�q��!0�|����;w��~0�"P��&���'�F9�6wp�|��>	����Rg<�({t�r��_.['�_�$#��s���(//���7�@����;�����.����
�[��������/6l�����O>���������'q������&�@zz��������k�.*Hz%�q�]���v��e;��=t�7�.�r9��O���L9����k����t.��{�BS��E��%X�t����X�
4h�h��w����{����C�[XXXTT#

�����q�������7]�t144�4i���������-\���7Z�8���A�`0n����O�GF��/����g���dn8��g���Wo��;Q��</+�����G��%�e��[U�����BM�P�Oc��l��1�F$���9���B�5x�`__���H��=��I1��o@@��3���'6l��M��4����"t�����������Bk'������F�
����W=��g�n�b��/,�_&�=��/3\�e��^��������oQ�*�,��Ou��Tg8��i(��A��A9���������y�/��r��%�V�Z)�����T������������Q����
�@�0 ���1����[�$]�������" hIY����8�A��
u
5��1�����~'��d�_6�����9��n=����$-���[PmZM�����JN���jh ((h���-[��]�b�l��|��?����������t��s��C�H}G!���&8e,����v�z%�n^�0����6�R/�vW�
`��d��������=[�P�%e��P�NZ(�<('�0���������m�5z�E d��!�O_�P����R�H������f��^�����i�6= v�K����
�,4�v�f�%+�4�M�����Z����I��M�V�@�. ���y�
�>�_ pwwO�v�����M~���6�)D���������~�:u�2�@�!(�<('�0�L�4��W_K�������~++�U�VA4��@�1(�R���x����V�zO:������od=����("�sM����9�
g�D��(�
�M�����ZuL�V�@{{{w���Q�F&L�������k�Y������X��;������!��tjq
�d��h����r���kjjjll|��������B(bYH`ee%
�"�����"n������� +�>��}ewb����*��x=�qbX�_VQ�c�:G��O;��,7���[��uu�&��H1Z�
�,Y����?���LII����������{8��^��
4�"(�<('��������/���m%�h�������O���
r�fn{y����;�6=��nl��.����V�n��������7��Dsj]MH:Z�]]]=o��.�����h]XP��A��A9��������
$���
����l2�0��P�P�Y�==���G?"����>(����\����q��� E���uJr%��$�dh�����wo�=\]]� =Zh�EP�yPN�KKK�����]�����<(e6����1I;��@S8�c
�������7��ha`asa�"��(9oe�Ubv8EMoS����B��na3;g���L]�8.@���&C�}tZZ��?�hffXUUEu�/"�3�4
�.,(���@O���r����g����w���
777ww��2899m���%j�F#MT��u�;��+���:���C#��o9v�A������;�7��|��� ����s��%���]���T���U�E*�dh�tBB��9sz��amm
��l�8w�\�0��?/�c��h]X4*���,�����lb�kA�������k������`&<��ok9(�R��3����N��zr����=aw�$|���Qp;"i����F��;G�1�{zcy>�N8��VE��U����)�1ccc*�
�={v^^�O�Z
	4���s��������7g�3��f�3�D
�F����:&&������r��)�'O�0a�/�����0�P;h7(�R��t���',��v1������w��_����J��p�����Ns-���\Q��4Q�}2�����s��i'N���R}��@�>|�@�SP���B����Q��A��a�;����`�i��H�n�����*���hP�yPN�uh�Y�?�d�����!N����������*>��=�-��O�����Fw�
c��8>����D����>�CA���EsS8���7��7����h���F�A���1��5�����q��b���h
teee~~>]��HJJJ���(�R��6���m/Z����`���V�}l���5�U������\��Zu���Em�-�	�4�V5N�V]��s�W�w�����J�Z���^h�p�i��n���N5�5��V�=wMs5���F��hP�y(��������w�Vb����+v��A�k(�R�[Pz�/y�"��S���|14�II�6Q���;�,��k�<�;����~�O�3D�T���U�q2��}��!����r��
���j�~���Fp��L�f�C���v��k�u�=�If���@s���@��8K�.����7�|�e����o*�����[���_�����
�>P���������v���M����?&���j��Q)���~����C"���O||��X7CI��:i�����@YZZ~���3g�<����EEE>>>p
�7~��wqqq���	
�:����H��p�T_4.������M#������lV-�=���
4��@����Z�
l�{��C������}��������rrrrsscbbn��������B333�
6�E%�}�@KM���uo~��m6����>;NDg�Q���g��"��V��4�8���E�S������6]'�Z]��Uh�`ee��O�����~��W�\����s��-==���������-;vl��=MMM��f}�A!
�Z�����gZ�]4�JM�<���@{_X���6�i��;�kfn���z��t��a��g)�h���0""b��y0H4k����?���3X�w�}7c�?������w���q��}�'�-�a��Y[��@�������?5h��6~���[_n��Z*�M(�JM�Z_sz�Q�:����W�S�t���.1p��>Z]?",**���!���K��M[�nmbb���_���/vvv���C�����- ��������'''���>���e�S��Yf��������k�84�u2�:�o����wmd]�K&��3�Y3
:fV���]���vQ���
4b��&55u��-?�����A��ma� �i���O?544�1c��]III�>��FZ����`�_�]�*os��^�����)�.����?��k�<�;N�bW���X?�.���.=�O�V�@JJJ������gaa��C����u�O>�B������p��9|t=E�)��la��g��~S����u��{�y������4�����������CgvIs5������&9��7A��{P�yPB�	P�)))~~~�N�:]���g�]�%^PPP�~W�-5>����{�Y�X�]���������Lj���S8j^���O��L�n'&�8Z�
@R������� ��{��>>>���S\\#['@�V���CO�q�A��5���}&�7�SB{�Es
r�=�zb]��Q����{��s9R����C�wA����Z(�����hj�~L�H�*X�7t���/,����v<��>������s�e���7Js6�Y>�q�������:A���jhY�K$�#�eBg@�V�<������#f���������~57����A
	4����|�������k�:��0����\|�J]���Z����&�=@��4Q��6���7�������
�s_7'���M�;e�D�WE�.�|eC��:o�p�*=ZR��P�����~&�6���W{+�o�Sh�@3b�o�����&5oK�	$�s��f�e��[]
4(�<�@(�R���-���-��8����
�y�l@���
Ro%n��eoa�9r���S�?�����*��^E���(�������N�������������|��uW�A��R�c��
4(�<�@(�R��`��x��5��������Q�Xj�p����^�����9w��}x^l�
Q�M�F�Vhu.�?%���|���cfVgk�CO=F��P��phvA���hZjo��Z�;�����w������R�����Q��,��!ht�u���S�6D}(7ZuP���v�j>��w�37<�
����r�K������yhP�h�y���L`�����'���������:-m���w n��g���������Y��������4.d���Q�UZ=��l��=S���g��8tM c/u-(����@��
�@KMye�������'����?2�A�3=�����M��5��$tF����{��(����T��h��r�
�	a��Q�UZ-�P��A�/R�?���6�o�X���8��7�����@#/����
������7l�p��������������**F}Zj�rW�	�������Os}6�HPHm�'�&n�=�0��s�[�4�Ue��iT��t�^�h������	tii��[�����c`` (fRRRn�~vB�V�R3c�����537j��MC����h�.(��@���@���y�����]�v0P
8���{���'N<r�HEEO�A����^�y�����������&S����c�cf���V��8�� b���H?j�nA�i-h�$'����������`WWW�?�����{o����=Z[[;88DEEQ�th5.C��_Pgz�
��I
4�"(�<(-�0�899����Y3cc��?��S�N^^^.|��7 �������hD��TQ�Z�i��nyx�� �*�/&x��G�����5�u�|�'M�]:���0�4�M� �
P��z6��Hf�'C�]�!)??��C������}�����[o�5u�T����
���o�ef���8Q�ua �\����paw�Y�tb��@������r���������]�lll��m{���������������M�/94"�z�_x���>�3��m}���]���Mm�����_:2��s�S�dg��-3������a�u�"p2��R����A�---!���&&&�F���N�>��c���;v�}��
�.,|}���]�N9�M�3��N'rjh��@���@����1�U�V'O�,++���pttl�����7�
!!!��w���Wjj*��v�-5w�
���y���������~"���j�P���yiO��#Bm�������*����tUk&&-0Z�
�8p:�����V�?>|xnnnII	�|�&M�������}�?(���p
�����W'�
���C����fQN{��	
4�	���O�=����Va����%
�c�����KxxxmtmG#M�o��Ns+!�q��S���b{z��k)�y�6}���������r��3S�*J�p���*@�k����+��������~�����IHdd�����#rrj��~����o��&�1��h]X8:7�Z��n	����:���>�y$���
4�	������G�I~Q#��@Cg������q�6����&�:@��4�<o���ia8�)����t�S�	�6D����6�j���'C�]�[�j�f�r��-"��1�����'�|2}�t�)k3�&��c�
u	��S�K�`���9�@?t�R�{�'������s����r��_t��!(����0���5F������N�:}������2(�R��Phw�������o��{�a��z�*o���VE��W����)�����6m:v����"IJJ2119r$���j���
.X���;I�/�������f3��Y���e���3����v������O��CP�yPN���tqqy��w�����Pff����u��������L���;666Tl�Zj�#3lV�
�:��T�A6��6����:A�pzr��'C��G�������k���zjjjPP����W_}�����O�-Z�<y�����@��u���m�'���s�]�l�q\�{RQ��h�hla�h��a��---�����Q�_~�e��a����
�bk=(�RSP\~1$�~���m���p-�~i���~C��&C�]�+++��Y���7n����'Nl��y��]'M���[�>�`�����R�u}��a���`���g���8p����sL�@��+P�yPN����rp��c�������#�[o���A�o��&  ����(+���01�Q��������JD8d�@�k�����v����v����{w8�����W_}��7�5k����N���M���=:�9l�S�N-����
����W�@���@RRR<�����K-;w���Uiz+�d~~~��w�}��O?�\�RH��������@���;��?~��}��x�@�F�@�imm����W�XA�h������k6lpuu��z����9s\���C��@���TP��
hTh�������_4i��m����5k����Q�����9�wTT�����H�[�n��/��chQT���V
���,���4�H$�z
�.,(����@���@�x���t��//��'O���?q���Z����>�������s:ti���>s���}�����/����)))T<��g������z��y��!��p��ioo���8n�F�F�@�i�n��d��?�:@)���(,,z0��W��G�7111u��H

�.,(����@���@C������6h��a��M�4y_��F�
2$""��A0\M�2����sqq���^����I�Q���/E31��7m��ppp�������)Z�� �"����@�GO��V�@WTT��x�W^y:C��V	��������h]XP��A��A9����tss{���al?~�����_�����?����=,qp���.]�t��!88�
���n���i�_~��9*�E������Z�Z�nv�Z#� ��R�Z�Vd'C��u�
4�c�{666���s�����Zk9u�yJ�n��
4�"(�<('����={�l����={�����������������8q��[o�5t�P���Z������\i`(277o��=������A����kkk���_QQAER
4���I����E2���=�.����^�b��~��7�\�~='''77��k��,E���GP�uaA�F^������S�N�F����
R����U�V���� ��S����}�{����u�����^$99����oYXX�����O>i��A�^��l��k�D���I�2����C	i=��OO������(-�����k�RAz
�.,(����@���@���~����
Rb_E�@�����q���3*���x����5;r�$����/]���ys0����~���AAA�O����z�����k���IEU@QQ8zNNN^BFn�ZG������U���(��1�
������7w������
�P�uaA�F^����?���w��{���������e!!��������5j����Z )���_~J�G�q���)S���������)))�1c����-=%%��:���� ����B�k���������Y�N�f|��@III���5j���7�T
�{�#
�Zh�E��B��B9����&N���cG�c���O�#=z���777W� #������
#����x���[�RA/]YY���q���f��u���
���QQQ������K�:� ���A�C]��>���@^^^_��n�:�e�GOOO�0###������h]XP��A��Ai�������o^~���
6o����@l�8Tlp�����}��m�����T�0���{����}{�A�t�{����lQ�U=z��Or1�'C�p�����w�k�����>Q���w����fP�uaA�F^��:##������>�������G��,��a������uk�
&N���������������� a��q�����O?����h�
lI!6@�#H���.��� ��n�bO�VZ����Q#����P�b-#F��>s��������h]XP��A��A9��r�J��=��o�@DDD|||��DFF���h�����7�QG��������m;d��)�4����5k��K���������O>��3Pph��M��V
�������;Z9��~f���}��2�Gu��@_@�[����@�����
4�	���g�����G�~y��M������ISA���Z�`��o���� ��@�wO�:����G�>����|���������#���� j�j��P�A|W�XA��v2�r]]]�����}��7RAz
�.,(����@���@���~���_}�0T�:���Wn���O?��!���0�l�����$Tjzz��[����`��������O�y��U�Veff��p^{��i��]�-<<<`T#��E�-��7�C��8�
b�=q��z-��?|'�����!P9���y������� =Zh�EP�yPN��Pa������JKK����>@ ��	�@����a���
����!�����aF���\�������������$��<8;;�����[��8q����g����m[�������������@�-%��q� ��l��
4�l�o�S077WB�����!����WTT��ad�=j3(����@#/���r]^^~���Q�Fu�����4z����d��k��s��W;H�`s��������'�5z����u��t�R����SXX8n�8�F�A���l�����M�6������a�!g�������0���X�M��^ +�@hh��o�I�b��YJ4�9�W�������C���OF��w��={�����~��G�@�����
4�	��7����ysc^y���L4��5�����v	����g��a��r�
���ta�����v�ZYYZ{:,,�a���4%��h�@���'
��C��?�I���_��7n��?��������S��P�uaA�F^������VVV�F����F�M=�I�gGG����z�5%
4��-l������5�N�
��w���~�i��1�=~���T��/�7 �EEE�>�h]XP��A��A�)�c�����4''��@#�������O>��84�dh�R��<2k������M���
4�"(�<h]h� ���AP�Do�+�@PP�o�A���-��~��
���Z
�.,(����@� P�A1�_��	�~AA�B�es���[�nA�z1H�@#���H��CX�����X���1����cU@l������N���];��[��.Are��/�������,R����9�}���y��9���g�3g� ��&�H�>-���cmmm���|g��hGg���q��Z^h�%h	r(����C������?�A�����e����������9hB��l����z��HK���w��-�S
���-[����/Oxt������m�����(�tZ�_�l�@K�C�����2e�����s�0���8v��:t���m�N�:����W�	!����R����F�������R�~�:�z���H<���'{���U<��}�����Kw�@C�V�W���f1ZX��0/���t
t�P�%��@'$$���~�Z����d�5/^�pqqA7
4!$?��@#�>Z`���b���o�����/00������BL�/A��W����yGEI������N*z�������Zo6���(}{.��:/.f�u:#h	r(����������]V(��(-���l�����)��WO��"���	�,+hExe+�Qo=���t�9!�_���>��e��{��\z�m�w}�?�P��C�� �������?`���r[�WA�&Di�^�<x �����7`<x�U�Vk���O�CI������#r@tt44�������2�)0J�yZ��b�,P>���c�5�S�^���Zd%���INNJ��1��G���W����6	�s0]{�����8���#h	r(����;v���qrr�
4!JK�}����{��bmm-���y��]�c``��kbb2i����7l��g��FFF9	p����������o�	�7o>k�,H����P�#F`lF�l�"v������
��u�E�/s��m�����.L��+���������G�����G$Jr�<,��	q��h	r(����]�vE^����M
��E��~���t�g����wo�f��-[�����x��)�o���;t���W�:u����B�CCC�NY`ii��G�r��5m��_�~-[�,]������/��?�~�3���m����}����B�n��;&v��������}Pc�y�Q��j=�B�I�4��{a�Z�8W�����2��I��������G�a�GP�%�*���������@P�	QZr"�i5z���bk�@���O�W��s�g���h��X�b+W����vss;{�,��b��.\��#�X���������'OF����S�b��'99�����k��7?}�����<RpvvF�f�J%����8~���]���Z��I����\�K��K��w�O�rr�	W5��@�(��P���������������__�������C
4!JK�Z@�hMMMq8E��4i2}�t�1��?������a�ghh�;vlTT����������-��O����%���X�R�Ju�����Ul������\�L�+W�`0>>�����*U��}����P*�����3f���D��C��O�@K�C�vrrB���Q�_�~��M�(������_��X��!hB���h�K�.��}�|����u�6m�t���S�LC0�g��q.\�j����w�*U��?^lJ����u���+W677�4l���a�U�V�x�����z��eH��7ob044t��E��%���������+::Z��=�\������7B#��W�@��'P�%��@;;;#j�+V�\9�lE)*T���W/L%N���@��|�@����C�����G�U�TI���Y�f�W}����k��m544���|}}G��7=w�\�	�����G��
zzz����Cv��111���QQQ��>>>���X�_~�e�����o��=|:'��K\7�RB���|pT&yZ�
������^��MW�\y����R>|����9�@B�3�4,���gO����'���#G� -,,r~�����W�����W/���BXX��%K�T�r���Lgx�����k7k�������_4hP�����ic``������_�h�Z�j
2d�����2e�`�����/��
t.q���+���c�a��W��	h	r(����u�fgg����(E||<~b����o(��(-����{�6m�h�������$!�"!�A*N/�y���5j���?������n���b��+V�����i�r�J��5+U�4i��N�:A�{�����V�D�v��=x�@�fnn�>������
6���addT�~�r��m��9�x

����TD������5���W\_��,�*:A�	���\3��������$� ����9�)�|�@����*6)hB��t�f�V�\)�����UUU����E[S�����s'w��X�5
��_o�����
Tx������C������h����������'N�{�N�P`��M%K�������K������������n�3�����{�U�		�;80�j�@�+���x"� �@DDD6�f�@K�C����C����m�_��Ev��������/^�K�
w�����@/_�<�+���]�]�v���g��.�~����������k���M�4I�V�)��AAA���=�a�_�}�
@rB,���P�%��@;88���C__?���
4!J���o��-Z�X�"�2e���+u�����w�{��.]Z�J�dz���7UUUk��y������n��%J��93���������_�������@��{�s�M�
�9h���e�V�\)�i����Ev��u�����O�<�z#6��6m�d|
�����Q�*W�|���L���S��<x�v:T�b�q�����`J�J�_�)xyyikk7h�����bSfP�s	
4�C(��P����S�8�3]�*hB��:<<=1��3��s��V�jll,6�`kk�F���]�����1`�����_�r%�����m���D��/������W�^L{�x��a�F���m�7�2��KP�IB�� ���P�	QZd��o�����1cFLL������e�����b��DEEA��/>q���Q�����sg��:u*>>~��M***���i?���/��?a�bkfP�s	
4�C(�P����@4033���,_���u����^�~}���-ZT�P���s��1{yy���b��Z���;u�T�d���g[XX���=�|���0��}��?�888hii�e��QO�>E+++CC��QMM���G�����KP�IB���
(��(-E����7n\�J������_�z��s��
		;}�|��1x0�d-������[�.U�T�v�F�	W����S�866�����;wF

�#F`,��Y�f�a6�2�@�h��P�%�@
4!JKAh����q��5l��^�z]�v=t�^����
55������<}�t���M�4�v7o�|������i���lcc�X�A���������)����!h	(��M��R�@ya����^^^�������
�������(6��Z�5�����~~~(�W-aaa�>����K�@�h��P�%�@
4!JK���	:��@�<�-��	tdd����m�X[[;88����@���W�1�
�gcc#Faf ��������
8�\�M�
��&��
&L���w�>}�u���S�:t��Q[[[OO�o������;w��'N<s��U�2_B�&Di��@
0������E0
������G�~���'�!�d������J�X(����!h	�M�mmm����������/_�E�ZZZ�Z�j��Y��U��Q�7�_�~�
�a��
aaa���
4!J������-[�Ly�J���555�
6,Y�$�U���I�:u� '1���������P�s	
4�C(�|�@'&&���5o�%a�����������G��:C�O�>�>;v��W�����c���
4!J�������^�n]�
z��q��
������9s�T�Ti��	����{�.~�R�������L(����!h	�M�CCC��]3>~�8j���BLL���G544��������w�^������=�hB�����s�n�ttt�����������3[�ly�����$x���#+V�(�M%�
t.A�&yZ�o�/^�<�H���Mi����g�V�1������>j�(al>�M��"�[8v����a�U�V�M_r���5j,^�X�����#U�TA�0��B��%(�$�@K�m��o����A�2��(H?�������W�0��s�1c�c�!hB��
trr��TUU��7��K�>}Z�^����s�v�Z���)�$S(�$�@K�m
3n��-����bS<x��qc---:88`���@#��<ybll|��%KK�tw������������@����
��]�*W���[�#**j���%J�X�dIbb":����*T�@�L�@�<�-��	4�Z�lY�������SD'�(u�~��-:w�\�lz�,^�8���;����6lX.MM�������cbbv����4q�DTG�5k(��(-�h`cc���S�b���7{yyA.1�����TUU1���;�������l���'��
t.A�&yZ�oh{{�:�*U�^�z�^�n����K��5K�,��wo�����G���S�Y�f7n�'������
5�����+f��Q�n]h��M�r�!�M�6UQQ���_BBB����@���]�������[�JW����B��o_�x1�|�����+W�������}F��'.�P�s	
4�C(�|�@	x��y�4hP�h�2e��-[�Z�F�������%''c��Y��{���I


��a��~��iXXXpp������k��s���/[P������?ba����+���l��@D��3g���c�����d���
j����}����������/z�P�s	
4�C(�|�@`Z33�c���_�~���FFF&&&SalBB������uw������v����^����_�bE�%�������t���n�Z�n]��j�����/hBH6��@���$D��={�`�%ccc{{���D��������UJh)�P�s	
4�C(��(��DGGGFF
�'''������}max��A�
:t����+6�������z����������Yp���-Z>���3�e��qhBH6��@��9��?���KLLLN>�\��@�h��P�%��gg�s�����c���[R��y��6l�p�����T�����X�b���O7!��e�������=�2����G�������/_���Oc����@+�u B���fffv��a!�n���HK����Il��X:��@�<�-��	trr�������?�P�t�
*�O���f�P�l����;99������e��a���W����)���v���~��w���2����h�"���{7j������Z�:   &&&��@ ���+�P��4����C5k�D<"�lB6
 �$oH+@P�s	
4�C(�|�@c���?����:q�����~���s��7Xi���(TK�,AiY�vm�����kW����G��/A��~�z���G��F���E���������&�(8��������C=3�.�===�:,X��&�b
.D��[�����	
>�\�M�
��&�/_�����Z����'�}Q��i�<��� ��9@�-\�Iw{�[�n(H{�������MGG�M�6�?Z^�z%������F�����/�E��Pa�P��
4��c��2e� ��!!!b,����*�=cX/
tn@�&yZD�7�������z���i�e��E���_�~}�
?��_�tIlJC``����4hp��	�)E��6m:e����{+��F��IOB��!_�F� �6o�,6)�\�M�
��&���?������~Nn��!�����o/Q���y��bk
����[�����2~_����7��-[�reCC�C��Ka��U�Ti�����[��?��:
��e*B�B"�+��1555
4�
4�C(�|�@c�%K����?{�L�n�_�^�x�^�z�M)�x��c�����b�$&&7h��\�b����.]�p���g��ooaa��@�B�&Di��@kkk==��#G���1���KP�IB���������:t>|���3��k����I�������M)�eddT�r�	&d�^���$''�����Ha�����3f`�V�Z-[����0�lD�M��"w��$��/WWW_�z�����_�>�t�:��@�<�-��	��7oP!455K�*U�V�f�1j�(q����%�+Wn��MB������~��%<�����%&&FEEEFF�&��G�5l�p��~~~111�lq���ThB����q��}�_��5��-��Q#1�d���_;��:��@�<�-��	���5*���V��u�V�Z1���={:::�����;w ���U344|�����
T�P�!C�xyy	�bcc>����wg�kmll7n�O�MYC�&Di��@#��u�KVUU�\�����!�c�z���V�d��KP�IB����:88����.\�|����8����Y��������#G��j��B�
�5������������#""����������
'4�5�����*U0`�]l�
4!J������x�0��K�.9;;+��yS�s	
4�C(�|�@�*�'O�,\����C���i����8.�����G�>|��=��Z�0OO�������?'��M��"�{��
t.A�&yZ�
4j	"eF('			!!!AAAM��H`` :|���!hB��:::I�,.*GEE!T��F����H~��HB�&yZ�
t@@��������]��A77��s�����o���3�[�n����a�|��Ev������Cg�����-�t����#����4��>}��������9�sA��KP�IB�� ������m�"E��������-Z�P��v�������s(��(-�������f�����A�7l�P�lY1����0��%Y�@�h��P�%��@�����}����nnn�q���l�s�NA�
4!J��������O_�~���899�={V��,������$h��P�%��@+6hB�~�Pv(����!h	d���������`�'-�ghhhA��B�&Di�=����1
�~a�7�8��
>�\�M�
��oh������#G�,Y2���_2g��_�5   cY��P�	QZrC�CCC>|�e�����T� ����9����:��@�<�-��	4����O[�lY�b�r����@��e{�����$N���@���]�Qu6l�P�jU1
��)|axx�8A���KP�IB�����t��Y***-Z�X�x���;7���O�:�+���������\SS�8p��M�6m��M���@��'Obc��(����!h	�M��?��U�z��]�|944411� -111qqqIIIhBH~F���l���B�
pwwG������hLHH��I(�$�@K�m}���&M����/,,Ll*�P�	QZ�+��b�5j���SlR(����!h	��
t���{��$6d(��(-r����7j�h���b�@��%(�$�@K�m�j��f��=z�H��M��"�{�����u�6x�`h�"���
�\�M�
��&��
(�������c������`kk���k���e)B�&Di��@'&&�8qBWWw��i&&&���.�%t'(�P�s	
4�C(�|�@�$L�>�V�Z***5k�l�����hhh�?���C� C�&Di���/^���G��%K)R�i��������)NS��@�h��P�%�6����<xp�6m:t�����2��53f����@B�3r�}��u���}
pe1��E���7��kWDD�8M���KP�IB����:,,������'fff�2����Q!(��������������b ��O�>���Q�O��B��%(�$�@K�m�C
�=
4!J�����!%s:��@�<�-�,�������RtZLLLlmm���D��@����@���999=}����T��0��|��
�@I(�$�@K����3o��-Z4h��n����-�vuu{�o(��(-�!�<���O����@����-[x4��M�
��&�����*[�������f��=��D[[{����_��hBH~F����=f�dc���utt�w�.�b
�)<x)*NP��@�h��P�%�6�vtt���k�R�

�������G��bmm��@B�?r����Q��U�4ir��)[[['''1S���C#��M$�@�<�-��	��{�6l��C�� C�&Di��@'''/^��l��K�,��
t.A�&yZ�o����m�v��A�M�� C�&Di��@�Y�FMMm���b�@��%(�$�@K�m���5f�---OOO�� C�&Di��=�7n�����1cF�y**�\�M�
��&�(-W�\i����U��������E�1�|mmm��"=�.(����!h	�M���������M�5j���gZ�O��m�6lzq��
��E�"trr�5kV����6m:z�h1����k���cE�\�M�
��&�VVV��o��Y��%U��u�������
4!J������������E��0.�)�P�s	
4�C(�|�@{{{���{q
K2c���@Y'��P�	QZ�.��?^�f������K�@L��E����#NS��@�h��P�%�6�NNNFu��������7hB����`����WP�1'P�s	
4�C(�|�@����hI��P�	QZ�.�9!!!��@I(�$�@K �@����_�~��
��e��)S�,Z����=cY��P�	QZrC�����={�}�����_6m��f��1c�=z4��%Y�@�h��P�%��[8��V�ZUEE�P�Be��)]�����r��-ZT__���M� C�&Di��@GEE?~�S�N��C����D�w�,U��2�n�
t.A�&yZ�o�������4m�t����������Q���A�z��U�D�
�8q"""B� C�&Di��@����k��H�"zzzC�i��9"QSSs��a-[�,T�r����@���C(����!h	�M�z��Q�f�K�.a����w�����(6H�#F�m�����B�o����aeeekk���'��������@GGGL�����@���]��?�lDB$''�>}�I�&�7{��{�U�V&Lx����9�HLL��#?�nnn9d���HxL����!��
t.A�&yZ�o�G�ihh�B j1:q���-[�_1x��m�3f�6����b�u��v��U�^uj~��9����},��Q�5j�	4h0k������D�)��(-�h��U�j���v�Z����WM�6�={6~���\�re�����9#��
����]�6p�@UUU�!������q�����C�"?�U���s��}����C(����!h	�M�������5r�H�&ccc
������S����������+�$�&M�X�"J��	P�reT�#G��*��2��lll�E��zzz���}�B�1���)k(��(-r��K�6i�d���B������&tV�C�����.Z�(���;**���c�5*W�\����e��>|x@@��)3`���m�4W�R�g���G�n��e��������$v�
t.A�&yZ�o�g��u��Y___�j�����U�v��%���m����~add��}�*T���u��/�z�~�z��]h��������~�������?�0g�L���:���q��A�u�l�@���W�1�������m��Qh��?�	�����Akkk����SwB�bjj
}/Q����k���o�����C=z4�g�%%%!��6mZ�R����;;;������a�b���Z�*�x�@�h��P�%�6����6lX�z�n��%|��[�V���7�wooop������S��77��={V�\���3���Z^�lY��[OLL�z�*r%
�-�~������m[T�G�e��K
4!J�����v�Z������EB�����������|��3g��
���^��H�"�'O

[?�y�&�k��HE��K����O�^�t������_�����VWW���m
t.A�&yZ�oh���={J�(��S'�)ZP:v���}�c����;WEEa���jbbR�Z5�������ry�^�v�^�z���T������

/]����g�W���UUU!����G�&Di��@���8;o�<!�o���A��S��<y9�x��u+����r<j�(���7��^``���&[��wF ����W=BBB�,Y2�����
t.A�&yZ�oh�����w:�A��u��T�R�h�B�
��Ys��B���?}�t�b�F����d:�]KK���b��d,~������������g�M��4066�[�.\Yx���5��Z�paD���2�����������������
q?p��������I�J����U���@����{{{�����~�
t.A�&yZ�oh��s����+��qc�=tuu�_[x���W�ZU�L�������#�B�o�U����bS���EFF�t���t���f�����e� ����BH�&7:11���W�/_�9NNN�i�������!������6.\�P����VJlJ�r���%Kn��9��bi:T�b�3f c�GKK�v��?������C��5�\B���=�Ua�C��P�%�E�A�[#Pl|}}s����`����/_~��u�vXBB���^�R�N�8!6eA||��s���k��a��E��=:��>�A�&Di�
�@�Ka������s�3g���[�j����QQQ;w��[��7/2�����{�"{��i�����C��qcXu�n�LLL��Y@��%��y)�@>8*�<�-A:::���������W�)`iiigggmm-ZYY9::���Fggg$u���K"~��e���[�zu���C��***������(�n��m��-�/�B�o�>���A�Q�	!
DPP�8��y�	ckk�62���mll\\\�������������;w�L�20�t5��{�n���)SBBB����� 6����E����a9===<x��w�B�
M�81{�

��������9q�z��w������V�'$��_���/��_X��H�
R?���t�a�
�9hoo�3ft��n�>hiiM�<���#��B�h������_�>�C�����P���C�ccc��(]&&&����Y�&����_��Y@�&D!A�}��v�Z��}�����0k�
���W�������O�(Q�U�V�.3C��o��t544�xK[||���a�H�t�2|��i���4hp���)3(����{P��@���s���M�6E����������������K�*�FI[SD���K�'1-��ph���;b5����-�(-����q����������R�T�z��-[�

�R@	\�x��?��m�6�)
X���#*�����p�1d��������;��!��#��-$�@K�C����rrrz����W����m�����y�����cDDD��@�K�.!���%6��w�=�k���MiHNNF)B����U�O�>m��E��
�?���P�	QZdh������/Pb2�������tC�����-�_===$�ZlJ9�\-\�����K	�����N���q�
*�n��t�xI:���IB�� �
��. �iI�8:g�w����uvv�R�"����z��#F�HwYEr��--�}���M�����F�������M�!t6�� �(*�t�iSb�_�������r��	��U;w�\�@��������������p��������z�������T���S�����@�|��C(��\��G�]���;�]���7o�����*T8x�`j�c	G����?��2�H����)R�s��i������r�J�w��5�=!��@���.��<}�1�l\�v����������m�����#F��J	�X###���������{��q�����aC�����?_�r����;99�M�A��%�U�y:_@�� �
E�����b��*)-Z�]�v�L�N�{������7j�h��]���(H�f�B=���M��b||��7�-[v���7�cWW���[c�P6n���	?~l``P�Z�
`��	��M��"����]�j���RQ�Gx'��MLL���a���H����O�4���G�����7n��R�JW�\��099���m���[�n��vd���Z�5`�������'N�g���3B��%�S�������HA�� ����<m��	
�������G���G������Y��������L			Y�n]�z����:uB|�.]�M�6�b!�ADD����$���B��c�:v�X�J�
mm�-Z ��7o~��a�{
)��(-�4�r��H�2e�t��
��3y�t�b�0����a��������N�R��6m��s�Z�j���.^�8�vX�S�N���**��������o&�j�My�H�����������d:�PT��	|gs/���?�����L���G_��r�5!]��
49����pO�����z"��-A[p���(���3777m��E���V.DGG�?~���P�3f<}�4�����U�V��/O-	IIIP|�t��B��O������/�P�	QZdh''��={�+W!�z����/P�D	4�~!���nii�`�x0�p���FFFi� ����i������������}���q�`�����{��	Gg
:�PT�����8j`��������������0�~�0������������i;�e�Z���;��N��(��P�}||�
V�v�G���4�W==�5j\�pAl��������0�9p�������������t�v��	Q�0a/�P�	QZd�k��������7mg����[+VD{N�U�����A�!�*c���G�dFGG#x�	�^��
t.q����e7"���aE!9)1)&2�CXb��i_��CX|�_�!���#!"$]�_���S|�?��;�	Z�
���c�n�tuu3>�y���
4��s�8\A��@���.�h������8����zzz����>S��@��L=G.��L���===��g��������1G�p$7�@K�C����C%���wuu��c�����5��e�8\�@���.����2p����2���g��A��/_�M

����N�i�f����6?���q��`��m�����~�����MV07�	�OP��1����^�5��f�������)�N��M�}�@K�C�������k��=]\\���X�n]��M3������Ev���w/z��e�2����_�~�;w���>�!�o#*6��6`���F/���H��v�r�Y�c7�����h������_t^���w���-(��'q��0Ok��]�4�\�������y_����q�7P�%�*����W�-l���c���p�M��"/�^�bE��������_GG���JlRP(�����O��a�^�����q��^�,h��������[����r`VrKs�@�����G��w�N�?��]��I��[��tG%''���
4!J��z����G�R�����o����p�i�wb��-w��s��s��{z�\Z������	��!6��[�s��,k�vaC���=�
~q��+(��\�uuu555�=jmmm��~�;�F�S�Ly����/�Q���vvv�HP�	QZ�"�
46l��'O^�z%d r����m��i����={lll���O����y{{K>��A��;w�y�^�H{���3�t��`��go~�).�>&Dx����]��������������h	r(�������?��c�Z�P���qc�,[����J��������9>:W�@���.�����V�Z�2e�{����S�N�b��)R�f�����m[dd�8}��-wV4Ws��B����g�u�t���W�����SBd��@�����x��q �Z�
�����%Kz����s�N_��[7}}}��Sh���/������+������}���1c�t��U���������g�^�tuu;v�(4��:��#""��>h����u�_n��}���R{�I�7-��q
Mb��an/��4;���O�5
�9���www+++KKK������������hBH~Fv�����a6����E@@o� ���������,��;���5ON�r{���c�#�_\��6�qZm��-�����q�\,B!�@K�C�Vl(��(-�4�@�����p7y�m�I���x���������u���O�l��'w�V>����I��W�@K@�hB�
��P��N��1WM�F���;�x���V.A��U)����j�z�a��ZE��p�������$~a�@���
(��(-h��@����|'mx�u��.s��,|���+����1���@�����.i�~E���6�X����C�$�
t�@���
(��(-h��@�����i���u�E�E��f�iO���J����������Y4\V����k�o�q��C���
(��(-h��@��]g�:L��;�E7C�N3M��^���8N�����>�v����Z?�����|�]�A���
(��(-h��@�[����{�=������?x�1�}�8Nq�����m�}S\��������n����;A���
(��(-h��@�����v�<me�����{@P��O�����n�;G�-j����e?�/i�w}7���WP�%�@
4!JZv(��A��O��1�|����)O�h��S8��)yZ
4�@��P�e�-w�b-]��h�r���'o�����W��z��:�o�z���K�{,�pk/�@�h	(��M��B��
���r�������nkN�9����A!�ptT��}��������������yt^A���
(��(-h��@���'-�N��;�e������������)��S8�)yZ
4�@��P�e�-w�~�9����h��S.�Z����������;-�p]�����Q����WP�%�@
4!JZv(�r����f�n�~�i�G�,���).�o_��6�o�F�{�����

�h@�&Di�@�Z���������wt��_}����_	�I�8��O��WP�%�@
4!JZv(�r':.��������Nm=���?��<�.2��m�(���@/n�wm���WP�%�@
4!JZv(�r��u��#�����7f���u���F����1�Y���7�u���
����>��~�pl��Z
4�@��P�e�-wv���0������}
�I���_:��)4�S8���p<���p�h	(��M��B��
��Y{����+]
O�0�4��K_q����������4\Vu}{{O����8���@K@�hB�
��P��������?���S8��,|���K����1���?�<�����U��-m��R��{�����-P�	QZ(��C��;������s��O�0^������q	J��/������WP�%�@
4!JZv(�r'&��S���kn�N<����7��%+�S8"�\\���o���+[�,���P���V>�#��@K@�hB�
��P�����w�[�]x_w���k��������.1:"�����~v�V�e~���"�����-P�	QZ(��C��;{�m���;�y7K��&��t�)0�|N��������ONG����c�8�|w(�P����-;h�������+]Z�Xd�=����&�>�8����O	B?�������c�V�P�%�@
4!JZv(�r����1�w�c�e�y����y�*�S\�?&F�8�]�`�����a=���~��P�%�@
4!JZv(�r'84����#����zq��g/���W�
�.���j���.h��������/����,�&�
��P���LLL�����o�A�m�����Y�-o����o���c1a�~�V�^������xqt�P�	QZ(��C��;q	���>%�);NZ���;����������6�}X������:�f''�4�
h	��@GFF>|�U�V+VTSSSUU-[�l�>}^�x�})v��G�
6�F���Wo��i�:u*U����mdd+q���-;h������m���3nOZoj��uh�D!S�<�����5h�F�����g7�8�8�Ks�@�� _	tBB��;w4h{^�p����/]�4d����<���K��%�|�����/T�P��]�9bjjz���i���*U
~����l�D�&Di�@�Z��d�;����g]
,:�|�����������w�w�����oqK�e�}Nu2S�K��
��J����'N�X�\��+W��	�>>>���%K���o_RR&_��>�<�RWW������3���	TTT�����hB�
��P����O[���M|
���M,�)�$�z������sZ���lq#!*��RA�� _	��/�4i��qckkk���gH��c��T�2b��K�5
���FFFu��Y�dI:QF�?���}����AO�&Di�@�Z�\{�9t����a�]g?������xQI�IN����sn�������������w�8���@K�����������7�����V��];+++�)
�����W���s�a���)���L�@�Z������;nC��8���]OMm~��M�).�������{m��KZ�/�|�{�;�;|
G^A�� �t||��m�J�(1�|�������?$�y�����bS�X�d�����	x!$S(��C��;�s���wy�?����������G��+�v�����1�����|
G^A�� �4
��E���/�~��t;,&&FGG�j��/^�r�������_�F�;w���C���!�($h��@����yc���cO��1qJ!���-�w
4h�a�F������]��p�h	��@GEE-[��\�r�W�[S�^���������{��lA-|��y�J�(�x����HqD���������!Da����;�.Q�e�-w~�+��u�~n�g<s���W~b�q�K\X�����7�X��w�������e<6���-A���d����k�������]�/^����bS�$''�����K�,9m�4��8"kPb}||����0��

������@�Z�\x�>p�������}�e���M\���t���>���c����V�k����jB���y��-A���|���e�f���c������{Wl�TA�s����������8"[P#bcca��E���Yh��@����Scg >�����J��:���x]�@������H��H��-A�h�*##�b���?>]����l�RCCr,6e�p���f��)R$��P#Q)1�8LQ(��C��;F�����3����W:��Z~��=H�����pc���^KZ�-m�f������#��@K�<|��B�
:t����Sm<<�[�n�^���2B�jw���z�������/���G�A�1q��4P�e�-w�B5�hxOw��i���>��;2N���zX�.���C�nG�)yZ�|%���������WO{�F||��+J�(�h����X��K����9�����������.h(��(-h��@�����o����;���S����aC=,l
�����1v����@�h	��@���n���\�r�~��%�l�����:u��Z5v���������wRR���Z�hQ�P�9s��FDD��GHHHddd���@��P�e�-w�x���]��K���r���gf��E*q�AW�z�8�1L�IDAT�.~���c�Y��'Y	N�%h	��@77�����)S�c����/�>}:�J�y������6c��"E��=rEF7�s�b�`����_�j���000022�^�)��(-h��@���&��-{�e������}4o���o�8NqI�*og�sk^
_���R|���u��-A~h�0��s�6l��|��P�V�Z���3�BB������2p����hoo����c�p��e���X�"&L-������A�&Di�@�Z�����P|
G��J��>}�����������]�?��F��/�E�'h	��@���^�x���+VVV��������o��egg��
�����)���0aZ0+L"N�hB�
��P������]f�������E����.�c���[8����������~�[�,m�}`Z��i�w`���-A������-;h����~����ug�����������8�%����R�@���k4�^��}vC�c��U�yZ
4�@��P�e�-w�>>��=�)'w���	�[.��wi��m�S84#V�t�Q���>�#��@K@�hB�
��P���_���z�\vWw��%{�=w��W�
�g��ky��4�[��s���W�����DI�A���
(��(-h��@��[O��Z����']�����h����B�q�K���H_'�K��Vts�>2�������q��C���
(��(-h��@������\�*>�����&����O���aQA>1��&%(�m��
�h@�&Di�@�Z��3��<������,�g>dp��9P����OBd�{g3���|"*����xq��P�%�@
4!JZv(�r�����6}���yg��'g����#�S\���zf�z��i�]�7�=�$��V-�&�
�h@�&Di�@�Z��&|4������pl?i��{X�xd���������W�Y���������|��kH�A���
(��(-K�����^^^����m119�*���L�??���h�OrrrPP���f���k�G
�c�OVP�������&������ra�����%(�G��<���d����V�k4���:��%9)QM�/h	(��M��R��{���1c���_�N�:�����5����-Z��U��JK�n�LMM�>(��F���k��[W������>YA��;�^��_g�7������-�������u�o<��]���E������>8�O�'q4��P�%�@
4!JKA�����G����U�R�<p��
T�Vm��9!!!b�������gO�"E���j���C��+Lkkk+tswwG�B�
u��y��a?�����o���N��dZ��9h�>V|
G�Yf�&]xb�+�S\��cB�L����_����k����AJ����	Z
4�@���655m��i�%6n�������u��euu�����={z-��B~��ye������0���������[8P)���_�vm���/}}}�G�������
�����u��7t���fh�i�I��7^9��)0�|����>�v����Z?�����q����|Z
4�@�����-X�@EEe���i�{>|�p�r�z���E�2i���_*,6e 66���C�*U��s���5P����g����}<��yo�j��7]�&����|�����
���\����pi�>�#��@K@�hB��!��z��Z����������m�533��
/_���������������'O��UKN�
�0`�*U���������������Kdd��#[(�r'&.��&`�����Nm=��+ 49Y�=2���e�p���j�n�O��Z�^���'��7P�%�@
4!JK�hgg�6m�hjj�}�����F��>{�lV	v���R�J���N�6MOO�r����U9r���wS/f{{{C�k��9q��)S���W�|���Z���kW```Vj�
Z�����n�5f��)�Wxa��WB��{�{���s��Z����V�hxL����>�.��@K@�hB���/�(c��:u��������O���-]��J�*���[�����w�^��%Kv��=k���;/^���������v��E��U�����/^<h��
*T�Xq��9�kanY!4�!��,��n2�:�q�����=b���**�o_{����X����K�{,��} ��'q4�7��h	(��M����|���5j���?�C� �[�n��._�<���EGG�^��h��zzzW�^���	

�./Y�V������st{��Y�V�X�����i�^�C�2e��_�����xCCC����N�_A����_jM������"k��f��\�|�6�}��I!	
�������c���/�v>������?���Ol"p�fc>h	(��M��R �������}���"��)�������\�r3f��Z��iHHHx����C�,--�������]�����Y�>(�'N�066FA{��m��R�Jikk�~�Zl�lC???�$��wX(~n:j�q��+��Oz��~���C��B�$���rv=����V/W��{v�]�!�'r����|C���q(�P���U$��������b�
����0���k�/>q��l
������f�&M���/6e��w�����"l��2��B{�-��7�������@����P��>�9/Tw[�x}G���8���@K@�hB���/�(c��]�[�n�^�||��^����%K�T�R����qqqbk���722���F������������_���bSf�C�r':.��-h�	�y������G��O���E%�w��c��[����{,m��7~a^A���
(��(-�_����[��m����[�S���1bD�J����3M�������-j���BBB��*V�8n�8T����������t_������S��
��sGl�
tn���Shx�_!>D�+�3�@��3�9-�X�*r���tU�=S�������h	(��M��R ����_�r�s���M)X[[kiiU�V���!�*�W�^����g�����bS
����3.�b�
����uuu���t����{�������<h���
t�R\�vw�����?�{��o|t^A���
(��(-B��x���:uj�E���2e��Hr��K �;vTQQY�lY������5m��f��7o�LHH��u+�4o�<�(C�'N���3fDEE���A��;6nA��
�@�hx��%���g����Q!6w��O���������?+����Z
4�@�����?o��5t*laa���r���&M�T�T���K���B7������;�cbb���J�.]�Z�m��������_�p�s��0����������}{�2���Go��y�������-��Y3SS��yg	Z��<m�n�u�)�f���s��C����H���w�k��fkY������x���d��-P�	QZ
�@cy`���7�\�r�v�z��Y�NUUUCC����;z�(<���
-�������Y�&�Y__�q��*T��������'..������u+U��O?�4h��6m�������3g�4�@��u��i���5�9��f�iO�hj��w�($Iq���y���@�e�����������Z
4�@����\SS��S�6m�TMM�w��'N�G�p����
�l�������s���!C�4k�v��u���>�h��V�Z��1��s��|�R��Z�<��M�~�/��
>z�����XS�&����9��p�h	(��M��R�$''��b����wn��TOh			XS�CL��R�#�$22*�>�&7K�P������kf^cV���r~����A��Y~�����p�+(�P����`	t��-wb��l&���;������B��Iv������6[����f���|���S8�

�h@�&Di�@�Z�8z�o�Q�>���7v��7]��E�������/�xn�8�����~gWF�:}���A���@K@�hB�
��P���o��;M��;�y7K��&����pz+�Sh���<^��;����� �Oy�F�A���
(��(-h��@��5���^�����"k�Yf�&]xb��O��|J��x��u�_~Iq��=?C���
(��(-h��@��{�}��5��c�e�S����{��{�8Nq��1!�����5�K��7
������ q��P�%�@
4!JZv(�r�a��L<G-��;���}�_9���O��y�����7��.l�������z~��d����	Z
4�@��P�e�-wb>���>y�m��'�����=,9�����������4��V3b����:.f%'�4�
h	(��M��B��
��q�~���M?���3�L\���]��+��Z�y�8��{k�8r���+���Q{}bi�'����?�@K�?u������;y����yX���������+����)[(��(-h��@��}lu����)���r�C������q���-|��^�����0���&�>P�%�o���dkk;m���u��+W�L�2��7��aC``��C
���7o�k�n��	bk�P�	QZ(��C��;�<U��S8f�u�hlb�#�S\�IN�������gsZ���ly+1*\G�;h	��@��999����T�R]�t�7��?�Q�F�K�^�fMhh��/kbcco����E�1c�xyy�#��M��B��
���i�5j��.s�t�o�s�#�_�=�����������,���_�dn����q��C�� _	tHHD�l��x��UDDj���������7o��eF||<�{��%����=��'�y�F�-hB�
��P���_���?�������K�<{f��*6���������E�R
��#�gq�O��+(��+�������c�5n��-6}�����.Y�������:99���g��/V�X�*U�R�B�1c�P�	!�C��
���K�����)ko�N<����O���D����l��a���S8V�t�Q���>�#��@K������������������r_��G����������,�~	J��C��7o�i����<yR�^��C�R�	!�C��
����{���]���3nM�`z��ghD�8Nq	{me��s��O���{e��
���?�4�
h	��@'%%9r�X�b'N
����\]]���544�����/��u���/^`�
�2d�W	4�V�aB��@��
��9t�^o�-����Xt��x��;6����������{�7��_��wik���w|����'h	��@GEE-[��\�rk��Iw�;u�T�^�;w��M����U�n��4�:   :::��X$&&fnL��
��Y��i�1W��O�x�~��cex
��O�z�\��|N+����_]O���_`�o�@K��z��%�
4*Y��]�+v��a�)[<==�J�cbb|||�������5������h��@��+�_Y�@{�I�9�z���f��"�S\��>F����_�rn+��}�z~)>B��:�B�� ^�^�zu:��^���[�B����'6e��
4��622&MQ��qqqII�}�Zv(�r'��'n�2��~��y;�L��c�q�K\h���m����]�����������[8�

��G�Q�-ZT�|������a��:::��U�|����-_+�xkT�d>+���-;h����������s������� ]�����)O��_��}f=�������-A�����;v/^|���X*�5___--��������M��m�z)B�
��P��� �"b���4��{�{hl������������-�g�w;���WP�%�?
n��Q�D��={�M)<�\MM�S�NvvvbS�P�	!9�-;h��68����������0g��#��e�������<W�����=�����!6�yG^A�� _	���/�5k��Q#+++�)��>|�b���&M���y
4!$�P�e�-w.=������':s�����O�|�������~w���nM�_'������_`�o�@K���j��)���[�vm��_�~��{�R�J9r$�6eT;���]�hBH�@�Z��cg�\���$��������)1^l%yZ�|%����<h��q������s�����O����P�B#F����#66v������[�nM��t�:uL�&�dZv(�r��U���n��1����/R�����0��P�%�W
���N�:��}�
*��[�Z�j����
[YY�^o������������6����ww�2e�t������bS�P�	QZ(��C��;~������qC}���+?x'�#��@�� �	���g��/_>|��1c����+����A�'N�x�����p�5
X#CC�����M�B�&Di�@�Z�$|�����
���_����O��\�-A������-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P��N|b��o�������z���{����-P�	QZ(��C��;n>!�[u�~C}�������+�#��@���
(��(-h��@���������3������o[��!�#��@���
(��(-h��@��U�7.����De�&B��@K@�hB�
��P���U��Kh�4�������v�y�����.P�%�@
4!JZv(�r'��'n�2��~�1���U@tl�8���Z
4�@��P�e�-w����������������|(�P����-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P�	Q<(�P����-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P�	Q<(�P����-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P�	Q<(�P����-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P�	Q<(�P����-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P�	Q<(�P����-;hB
�h@�&Di�@����-P�	QZ(��C�&D��@K@�hB�
��P�	Q<(�P����-;hB
�
&�qqq!!!AAA����|�b�P�	QZR����###�C�`||�8"k�bbb�S�%,,�T��hB
��$���������^��M���f��eee%��
4!J��	4����g��Oo��%��W�^���		GgAll,��}���$s�����)��(h	F�}}}g��]�JUU�����i��|��:tx����#k(��(-
&����7o���*UJCC�g������V�$i/v������������_�O�>=R@�v��e���hB�
��!����+V����P0.\���������')Rd����
@�&DiQ0����j��]����M�fnn���v������W�P���� ���M�6jjjFFF�_�vMY������'��
4!�ZhT>�|�f��V��
!��\����{�5��=���,�f��E�:11q��-����c��FT���OC�
���%4f����~��C��
�M9�M��A��@5�������G��)�Z�JEEe��1�\whB�EhX�����W�~�����$���g�����+W����b���3RTUUu���o��quuupp�m����=��M��A��@:22r����
��-K'�'N�(^�x��cbb���:m�!�(	�$�vvv���
6����R@�4HEE���Cb���F�_��b��c��]�r���&,\]]������#���
4!�Z����#F)R$cmx������VSl�
4!J�"	��+W�V�
�
�R@	\�ti����m�&6}	���q��+V�\�=zL�>}���U�T)U�T�~�����~Y@�&D��@K��������r��)��?,--��2���g,��������<��� ���v�������$��O�.Q�D�V�"##�����v��Q�|��FGG��i������Rt��>�������v�Z�.]
*4y����@�kf��bq�B
8r��y�-�4����������M�acc�����A�l:)))""�[��0���f�	E�s���)SFSS3]��@����b��S�L��������v���...bS
����[�����������HHH7:!������h	��
���'���������C�&M`��4!DiQ�+�%K���
�����j�P_
T�H��72B	Q*(�(�@����C�-\���#G���011�=khh�u$���$��.]�T������_�|y�%�l�"6���������Z�J����E�-�4j��u���-�r����D�5���O�*U�C����G!�$�VVV�:uRSS����R@�6�P�B���2>>�2�?���^�z5���-�4����z�����Ow�e�����4iR���&�(-�$��v�G��Y����WQ������"��U�}�������{����������'�����W�
*9r��p�TP�%P�F����7j��a��>[So��O��3g���	!$SI������]��?L�6-,,LlMy�]�J���7b�!�***%J�H�M+�=w�����]l"�(h	@�Add��y��/��[�������888-�_���B�D����khh�/_~���������/_n��]�r�N�8!<�/99����������B<�{�n���ph==�{�������:T�~���Ko���� Q6(�(�@�=
�D������{��+���OW�\{BHL�cbb`���5+S�L�.]F���+T�0~���g9'%%�?�\�X�k��	�}��AO�=zt�~��U�V�fM�!Dy�@K�0
��y�v�Z--�Z�jA���cbb��F@BI��	4����{���#���_�v�:l��)�?� ��QVWW������[�nm���=z�8q���8��LP�%P$�>>>���X/�3!${O�����0���M�Xh�lTT���=���v777�~��ACQ`(�(�@B�W��M!2B���MQf(���
�hB�2C�&���P�%�@B�
4!�d�-���P�	!$#h	(��e�M!�@K@�&�(3hB�Z
4!D��@BHF(�P�	!��B2B���MQf(���
�hB�2C�&���P�%�@B�
4!�d�-���P�	!$#h	(��e�M!�@K@�&�(3hB�Z
4!D��@BHF(�P�	!��B2B���MQf(���
�hB�2C�&���P�%�@B�
4!�d�-���P�	!$#h	(�����8��������d��K����B7�{�U�HHH�	��!44���6���w�N����'����7C�.p`gEFF�X�/�f 111""�����A�@\` ��#~�$��"w�������
�/�2�P�&"3h	(����j�0u�Tx��K'N�(t{�����L�r�����sg

��_��M��#G?~<00P���XZZ�k�n��q>>>b��9v�v����iur�]��>�B���b��`��={�G�������8B��7�_�[@]]]������]�`��'O����������{��IJJ��
}����{�}���Dd�-:�b``��B�
^�x!�~	���~��78��������������|Ibb��#G����������+
C� ���W�U���i���9�������iXX��$��{��M�4��-�r�J���?����@C�.p`���]���SQ<xpTT�8"
0�!C�}�7o._M�������S�"##��|IPP���C�TUU����x������s�f�*V��E�gs?#0`����C���6f�,��9spX�Mr�i����(b�
���#$<��}7�k�'o:��������5��w]��I����G���q���s��C��e��G$�8B���o���^�zg���r�P'S����=�����!���>������Y*A�������������8���{w�F��7m��sB��v�Z�^��-[�����?�^h����k&�)���&L�����P�����G����2�r8|����rr�]��e��tTi��-�t�����M�.\Ge�N��+�p��}�v���*6��Qa<9�{k�o�`������iv�cL�8A���G���jbmm����x�O{{{��i��-Z&mbb"N�BBB�={��x�b�����d��\h�D�/�d��8����P�%�@��o��f����N��6Vu>��hc��'5G�u�$��/_^�H���+�.]z������������2dH�Z���e�������HTD����������i6�����x�|�B�����������;
h##�J�*M�4)�_rrr����*������+���_��D����9��o�@{xx�M�f����#h�B��v�����`�����b�k��j�CS������>]�24l	jX�n�}�����5>>���������p���W����_�\9�<����%*�����_�*o75}6���#�������8A �#F��V�������4������)�{��{��5j���K6�@/]�T�7��h�C����{�z�Ow�q�i��Zu]�*����?�4���{&������w�����~BR#������^�zA�:t��N+�('��^^^nnn���>>>!!!�n>����7�~�}<==��E����D�������@LL�����sZ���@8��aaa����.��"t9<�yr�����R���Z�����'�<�$S�K%�����G8	�Z�*�DlJ���a�����+��m����c��177G�I�tX���a��vio��@ca_������b��&l��r�r.�G���G����������!:7�@�	���~~^o�����Qx!���:s���I�/s"� �u������U���A���x�����C�J�*8*�
4�Z��
G1�
{9���!L�~���y;��o��V�|y����)20u��+�����L�@��g��,�p,�f��AVd�I������>�^��t������\� N�A��~8�@T��i@�A��=ZN���"#��
k��.�V�R�k�.�#�+���~������#��@/_��}���A��`k��L��Y���(F,!�]�vh�B����{�y����f>�����"��/�w�e�3���#s�.Z����#�O���g�qD
�9$c����9��P7A��e��+W�L�<�]�vm�-ZL�2���h(!'N����o�����A�x�K�.	�
d�F��'"�B�
���_&��q����lA�V���(BB��B\�~�.�/ZZZ�3c��	a��p=��uj��u�"��
���a]��U-m��	0��"J�@�?y*���Lh,����
p�!�{��
q�7���?������\�r��Cc;`G��(��m[�N��R�PCC����,�?l^�w��mO�>�l��F����6k����0���QAw���"��	ka�5k��H��S@��u���������
~���9s�*Th���h9B���_�n9����e��V��Qx!��^���A��P��5�|�M�6�y�8.;;;555��6mB��o�^p\�0��;w�������^�z8��
s��y�t�R�8����Akkk�H�;w.��|F>�������h������y��4i�������i����!�����Ca�555>�Q"��!����C�A���Z��/4����������c�>�b����Q�F	��T�;V���6l��k�������g[�n���?)R�b��(17n��(���W�Pe�������u����@�� �3g�466��
f^�f�.]�=z��z��-�?����'��~��a�t�@��}�vD(�!��}�����e���@�
���C�&=[�0���B{������8/^��"#4�8���
7|�p+++�P�b���E�4n����g�	& ��h��6m� ����D�#��g��1;v,U������HCH!
L����(���W�b��� �^�Z5D't��!(�%�h[[[t���/�5�K��|������
h��5���5������U��	4tGlM����!�cjj���^�:�����T�XM1���E@u��b�a�8�@}E�c�1+4b�cB�f���QY�9�(Q����M=t�Pl[(u�2e�.��)���b��I8#�����"�Da{
�-�(x(!�!�2��������.85�@�
t� 
������!�B<�����@7o�G
�k��8�/_��z�5..���'N@Uq��#����	q�����2rG(:���}�#�H��C���81$�p�"pT"��}��]�|�pr����Q����S!%��-[���l<I�p@a.]���}b�@�]���:�t���Kt�W�q�>n�8,����v�������c���P;p���s�G�a&&&���������lgd&�,ua���Xw�T1��
�s.X�9�-�V�w�
��s�����?���C�b+!��H����p�X�N��e@c���e�a��F��#h	(���\z���^^^zzz�f�:���6 �+~~~PU$��,,,dE���u���#"�~��(2��~�z�quu� l�%�����F�<"��)!!������h�Vb&8����v���!�N�>
���>}��w�={vtt4���Q#d�*��13E�FE����/o���"^�P���P����K1��}��!6,�[l��E(��j���}}}����R�m�6l
	�
}?~���5]�v-����g���9j���7o������F]�B�6�����ill���n�,f�76��1�y��a0���������@�
t� �@#��O<�~�:�i���h����5k"���8�p�	��m�6������#�}8~!�>KKK�9|�0����mff����#�*�f�<yC�$��� �(RX#� 4`��	�Y�~�9�������=�rqqAa����.w�(��P����������={F�B�F����k3644���� 6���w���hpe6d�-��������#�f�����M����B�Q��9Vm���JL�����'b�m���$�����������<665�o����$���Y���
����R,@�b�j��A��#h	(���\?a~����(bR�y	oF�@A�R��q�F�n���F���VVV�
6DH	cAdd$\f,��
?a�X�����*���K(Hg��-��DuA�@(�b!��9@���D�
�l��/���r�N��e��X6�?�����8�H��l(��$SSS��aSc�����c�iii�f��y3e
����;�������Z�j���cO��a��d;;;�x#��\�"t@�F��('''�8��e�B�Q����B���p�
t� �@����l������Qknn'���G�@��B�p����o��>}���d�Ty���i? k\�r%r���i����Eq���a����S�R�J	�[x������qh�P3��g���J���C�n��UL�;���)SP��X��5� r�N	���;�R�'�����1��[��0i��W�^%�l�7n�����s���LPS��82r\�R%a���a�����k[�l�������:r�Hl��{�
�#��V�-G(�P�s�������}��#�2��u+2�����@3>�C�`�p����5kb��!1n�8����KL��K�~#����
'�����\�������Q6R.���g�'��*G����v�2q���B(�Y�����Ez�M���Z���?�.��5��"����������Qqqq��o����'(lj��6k��)O�@�H�l����c3�Y����_/W��=*G�m�cp��!X�c���}��X����3��P��&"3����\���;�G�2���s8�P+�B,`��Q8r��D��8�p�#�:t�P�F
������y�*U0[���P���J�?4������BV@�q����	�p���4h��E��#m��X6��pE<'�(��'OF�/^����`}��/�mK�
�}��5N9�)��[�g���k�.�$�b��e�`��~�;HlJ�p�,�q�h�:u*�!�*�vB�k�|V���
�JSSSUUU�q.����3�����b�
���C.�\Fv#�`��[�vvv�_�x�"�������H+�(����}�;v`����#� �+��?�QPx ����s���w�%\:���^�t	JZ/�+V�@���K�B
544�s��9(f�T�LY�!�h�&����+�����o�����C�OM�FXH�u\�z�7n�8c�---T��p�LF��[`�����Y� �(�Hy�	����r;Ee'?���K�m�j���M��~��F��T������r����
N�8+%6���<��#�@�E�`�r�j��k
)x1����S��	4~Af��u���h��uH3������*lx���X�]�R�p$v��u��]������N��'����K,4Q8�q8��=3��0����b�����?�� �@�7k���?�@��x�O����k��<�-��{�nd��a��)M�f�4dh�R`�	��"� �o!6��p����@#������n���W#-��A����(7Hl���sI+�]�x<v,����o�-:���@#pQ�Q�g��� C�!�������?"_�~�V��V�n��"o����%4�|�`@"��s�"��2e� ��v�.�SL'��K4f���z�2 ������z��iq�a�"��G������=�!!�
4PUUE�E��H+U�D	�C�_%���B][eI�_HL+��3g���/�q����
6�M��;�!
(X~X��Dd��'�E��Cjjj����[[[���I��c�)�`�8��y>d)�!	{���O��u�Z�.�"+$���[�n�!�T�q�#���#GO'�X��
B
G��q |������
MuqId��#G"Lp6��O�
Y
�Fe���Zuuul��m�"��w���h�A�c�iI�GA�Q�pX	���W��
4��!*������6l��9����;�u�����`��U�6!r�-:���@Iw��)8������E��=U����?_�N�3f 
�O�<��>��Qo��i/^B�Qr<x�q�F��e�����P���q��5��+�M���[fff������X�.��@#|%���������b�i��K�.a�YYY��y�
�4�������k��t78b`;�[�e)��@��wP��Xfq���m����`����ME��;-����	&��D+�P��9
42m���U�V�x�"�	�z��1A����a�>B�p�C�6o��io��
Q��>��T�"##ai��+W��;G1�T���������O��!
��A-p��=��B�0�����="�/RI�����m�����&&&NNN(
E����kV=~�xl������ WG�}��\(d���@�����|C�b�[O;�������DV�T���
v"NN�8!6��@K@��=��8���R;��/�k�6�h��	��[�n��E��r���Wkt�b��2>��:���\R�(O�<z�l�&O�E��]���������>��;'t�g#4�w;�%�!h7T)��~�@��R3f�V�:-������Z��m�h�x67(�B����__��`*/_��2�Y�+�(�[�lA�����B�@���C�n��Lce��`����'0D�L444���������C�������K������G�iXf�{��:O�W�G]������t�(���n�D���;0�Q�F����x�1(t�N�pPco6k�����h��#W����{�����;��b�#�G�B�0�F> S��'|�������_��#G`�8fQmg���m��a��
��e#^h\�0{����(�z�pS�jN���m����F	��J��w�[�n�M�;v��F���7VPhX���yu��q\��3��)�@cP(y(a��Tp�r��A$$�!B~��a?����M���(��d'y�@rZ
t����5�d��4'��3������]j9���k&w��
tL�c��;+V�@p��S�Q�Ft�[:�600�TD����9�L�2h_�|9d���g(��������'xN�[�l�,>�,��%a��u�)_�0�~�'�x/( �������Q]���Y��oh���_�\�{V�73k{}�z3���i5���������J�������8����z����a��q�)"TM�s��}��C�E�#>p����-[UU���[���+;vDGgL����l�@�Z�
������Q�,<G����oW�V-�,df��nS�L��������a�c����I�(�r��'�<m�����8�����(�&�V�s|����
pH��C ����e�=U�����sW���q�	�u����EjA�����_��k��8B���I��>�:::81�{����W�v���RP$���-��Se����������xG��/�[�@��x>����r�����F���0�����}�ML9h��o���+�����������};��?z�{�N__��Q���������z�B��������]�f
�E�6m��d��13�J���lL� �g���8����=V������+WFZ����I���-��V����+h	(������36=����5��z�^v�����2��U�����g�����q��q��C
���B��rT��C|������n��=z4B%a����$�J7d�L��E�	"**
z��o��!: ����<y2�:���PO�3�l����r��A4%u6-�7�X��e�yX�/��Cl��}g����J�������
�<�s��A��zA�'N��GME�@�c�]�v
��H�j��aEP6N�>���R
G����53f&TSS���"
c�I�p���S�3��[	��/Z0�,v
Bc��8/Z�vm�����[�j�M
G��@XH�2L�{��:O�j�9|��	��Gb���EWw��J������������3^���;D��@�r��a�=z����Pj��p���Fp��Ch�����R�J�2�'���zzz�8���i��u��G"���s��C
����E��Dw�����F<x��uk:~����@�1[�_g/�~1�3����o�m��K���/�5}]�M��������Z������}�
�I�&�8X~h1���X}���8R

���(O�lh��7�W�G�7��f�vn��6����m(ji����8	AQ�\$''_�~;���C��o���1s��SAZ`�����;pk��~G�bni�>�
����S�?���Q1	���������[���S���)8)��
4.K///�
��/��B�A�����#F����{����B������C���Y?������� �~���C1e�D �;v����>hR�~�A�]�p��H��W�B"|�a���)1�c����QY��b?$'e��'<�&�"+y�����l�2T�1��}��6l�w������0a;@y�Y�
�B�qm���m�����F��nN�pa�=My�i��=�>�B���~��e���r7&���{,v(�4�[��B�y�����S���~D���{7�p����@C��>~J��������
���N��{K;p�N2�|i��566�Q�F!E��7n��#]8�;t�������{��E��X����
����n�?a�l������:u
�&�����,277�1c������+aB���E�p�C����������m<~H�������g���_����|�2�)=~?~������R��(:(
pk��*6���7�������M�I�D��h#��$��8�x������QD�m����(���I;add���'��������/����S�3g�����P�%�@�[���]\\����;�R�����������������LZ�Y�����B�������n������v������Eg�M��-����A |VFJ����Fa����X���p,!�45:s�#����wD�E�`��&��AF�w��p���[��.�����6����!�lO777l����0g6�#4�)�oooa9;;g��X5�
`��#�_ly�&H���D.P���������zb�	�02-�)�Q8*SS��p����p�a�p�"���P8��c���r8�A�?t�|0��P��8���	����,
���h��3���xG���r9yG����D��c����p�!�D�;B7la(H��9c�����0-�B�Lb[;�b�0a?b�R/d&��nC���n�$���G�-`G;�\�@K@�&�(3hB�Z
4!D��@BHF(�P�	!��B2B���MQf(���
�hB�2C�&���P�%�@B�
4!�d�-���P�	!$#h	(��e�M!�@K@�&�(3hB�Z
4!D��@BHF(�P�	!��B2B���MQf(���
�hB�2C�&���P�%�@B�
4!�d�-���P�	!$#h	(��e�M!�@K@�&�(3hB�Z
4!D��@BHF(�P�	!��B2B��,B����@GGG����dR���
O!J�_���T������/�#�e"&&H�����Poo�BQJ|||2��-))	b��b?BQ&`�������b&���@��166���� �@L�?����� v"��#..���Ob&���@B!����@B!��P�	!�B�
(��B!�|hB!�B�
4!�B!_�B!����M!�B�W@�&�B!$�|��4�a�xIEND�B`�
#6Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#5)
Re: [PoC] Reducing planning time when tables have many partitions

On 7/5/22 13:57, Yuya Watari wrote:

On Mon, Jul 4, 2022 at 6:28 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Perhaps the bms_is_subset class could be handled in a similar
way, ie do a one-time pass to make a List of all EquivalenceMembers
that use a RelOptInfo.

Thank you for giving your idea. I will try to polish up my algorithm
based on your suggestion.

This work has significant interest for highly partitioned
configurations. Are you still working on this patch? According to the
current state of the thread, changing the status to 'Waiting on author'
may be better until the next version.
Feel free to reverse the status if you need more feedback.

--
Regards
Andrey Lepikhov
Postgres Professional

#7Yuya Watari
watari.yuya@gmail.com
In reply to: Andrey Lepikhov (#6)
Re: [PoC] Reducing planning time when tables have many partitions

Dear Andrey Lepikhov,

Thank you for replying and being a reviewer for this patch. I really
appreciate it.

Are you still working on this patch?

Yes, I’m working on improving this patch. It is not easy to address
the problems that this patch has, but I’m hoping to send a new version
of it in a few weeks.

--
Best regards,
Yuya Watari

#8David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#4)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, 4 Jul 2022 at 09:28, Tom Lane <tgl@sss.pgh.pa.us> wrote:

For the bms_equal class of lookups, I wonder if we could get anywhere
by adding an additional List field to every RelOptInfo that chains
all EquivalenceMembers that match that RelOptInfo's relids.
The trick here would be to figure out when to build those lists.
The simple answer would be to do it lazily on-demand, but that
would mean a separate scan of all the EquivalenceMembers for each
RelOptInfo; I wonder if there's a way to do better?

How about, instead of EquivalenceClass having a List field named
ec_members, it has a Bitmapset field named ec_member_indexes and we
just keep a List of all EquivalenceMembers in PlannerInfo and mark
which ones are in the class by setting the bit in the class's
ec_member_indexes field.

That would be teamed up with a new eclass_member_indexes field in
RelOptInfo to store the index into PlannerInfo's List of
EquivalenceMembers that belong to the given RelOptInfo.

For searching:
If you want to get all EquivalenceMembers in an EquivalenceClass, you
bms_next_member loop over the EC's ec_member_indexes field.
If you want to get all EquivalenceMembers for a given RelOptInfo, you
bms_next_member loop over the RelOptInfo's eclass_member_indexes
field.
If you want to get all EquivalenceMembers for a given EquivalenceClass
and RelOptInfo you need to do some bms_intersect() calls for the rel's
eclass_member_indexes and EC's ec_member_indexes.

I'm unsure if we'd want to bms_union the RelOptInfo's
ec_member_indexes field for join rels. Looking at
get_eclass_indexes_for_relids() we didn't do it that way for
eclass_indexes. Maybe that's because we're receiving RelIds in a few
places without a RelOptInfo.

Certainly, the CPU cache locality is not going to be as good as if we
had a List with all elements together, but for simple queries, there's
not going to be many EquivalenceClasses anyway, and for complex
queries, this should be a win.

David

#9Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#8)
Re: [PoC] Reducing planning time when tables have many partitions

Dear David,

Thank you for sharing your new idea.

I agree that introducing a Bitmapset field may solve this problem. I
will try this approach in addition to previous ones.

Thank you again for helping me.

--
Best regards,
Yuya Watari

#10David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#9)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 27 Jul 2022 at 18:07, Yuya Watari <watari.yuya@gmail.com> wrote:

I agree that introducing a Bitmapset field may solve this problem. I
will try this approach in addition to previous ones.

I've attached a very half-done patch that might help you get started
on this. There are still 2 failing regression tests which seem to be
due to plan changes. I didn't expend any effort looking into why these
plans changed.

The attached does not contain any actual optimizations to find the
minimal set of EMs to loop through by masking the Bitmapsets that I
mentioned in my post last night. I just quickly put it together to
see if there's some hole in the idea. I don't think there is.

I've not really considered all of the places that we'll want to do the
bit twiddling to get the minimal set of EquivalenceMember. I did see
there's a couple more functions in postgres_fdw.c that could be
optimized.

One thing I've only partially thought about is what if you want to
also find EquivalenceMembers with a constant value. If there's a
Const, then you'll lose the bit for that when you mask the ec's
ec_member_indexes with the RelOptInfos. If there are some places
where we need to keep those then I think we'll need to add another
field to EquivalenceClass to mark the index into PlannerInfo's
eq_members for the EquivalenceMember with the Const. That bit would
have to be bms_add_member()ed back into the Bitmapset of matching
EquivalenceMembers after masking out RelOptInfo's ec_member_indexes.

When adding the optimizations to find the minimal set of EM bits to
search through, you should likely add some functions similar to the
get_eclass_indexes_for_relids() and get_common_eclass_indexes()
functions to help you find the minimal set of bits. You can also
probably get some other inspiration from [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3373c715535, in general.

David

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3373c715535

Attachments:

eclass_member_speedup.patchtext/plain; charset=US-ASCII; name=eclass_member_speedup.patchDownload
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 048db542d3..0af3943e03 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7410,11 +7410,11 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7453,7 +7453,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7469,9 +7469,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			expr = ((RelabelType *) expr)->arg;
 
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Expr	   *em_expr;
 
 			/* Don't match constants */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a96f2ee8c6..2060b73686 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,7 +430,7 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..5953b79fe8 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,10 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +454,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 63f0f6b79c..caba68b839 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fb28e6411a..1043b67aca 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = (EquivalenceMember *) list_nth(root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,9 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
 		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+			list_nth(root->eq_members, first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 60c0e3f108..0e46c05287 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,7 +32,7 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static EquivalenceMember *add_eq_member(PlannerInfo *root, EquivalenceClass *ec,
 										Expr *expr, Relids relids, Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
@@ -139,6 +139,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +266,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +286,10 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -364,7 +365,7 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes, ec2->ec_member_indexes);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -378,7 +379,7 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
+		ec2->ec_member_indexes = NULL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -398,8 +399,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, 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,
@@ -416,8 +417,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, 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,
@@ -438,7 +439,7 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
+		ec->ec_member_indexes = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -450,10 +451,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -542,10 +543,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	int					em_index;
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -573,7 +577,17 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
+	em_index = list_length(root->eq_members);
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) > 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
 
 	return em;
 }
@@ -645,7 +659,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +674,10 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,7 +725,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
+	newec->ec_member_indexes = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -732,7 +747,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -794,19 +809,21 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +882,11 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +993,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1096,7 +1113,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1145,7 +1162,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,7 +1171,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
 		list_length(ec->ec_sources) == 1)
 	{
 		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
@@ -1172,9 +1189,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1204,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1240,7 +1259,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1272,10 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1315,9 +1335,10 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * pre-analysis of which members we prefer to join, but it's no worse than
 	 * what happened in the pre-8.3 code.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1445,7 +1466,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1537,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1560,6 +1581,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1592,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.
 	 */
-	foreach(lc1, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = (EquivalenceMember *)list_nth(root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -2104,7 +2127,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2142,10 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
@@ -2139,9 +2163,10 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2223,8 +2248,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,9 +2277,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			coal_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Assert(!coal_em->em_is_child);	/* no children yet */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
@@ -2269,7 +2295,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2311,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2336,7 +2363,24 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes, coal_idx);
+
+			/* Remove the member from each of the relations */
+			/* XXX check this is right. Is there a neater way? */
+			i = -1;
+			while ((i = bms_next_member(left_relids, i)) > 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, i);
+			}
+
+			i = -1;
+			while ((i = bms_next_member(right_relids, i)) > 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, i);
+			}
+
 			return true;
 		}
 
@@ -2374,15 +2418,16 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2490,16 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
 
 		/* 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)
+		i = -1;
+		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2568,6 +2613,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		int			num_members;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2580,15 +2626,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
+		/* Only loop over existing members, not the newly added ones */
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * XXX this is ugly. Is there a better way? bms_copy()?
+		 * Use bms_prev_member(..., -1) to get the final member first and
+		 * abort the loop when we go beyond that?
 		 */
-		num_members = list_length(cur_ec->ec_members);
+		num_members = bms_num_members(cur_ec->ec_member_indexes);
+		j = -1;
 		for (int pos = 0; pos < num_members; pos++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em;
+
+			j = bms_next_member(cur_ec->ec_member_indexes, j);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2653,7 +2704,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,6 +2757,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		int			num_members;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2718,15 +2770,20 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
+		/* Only loop over existing members, not the newly added ones */
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * XXX this is ugly. Is there a better way? bms_copy()?
+		 * Use bms_prev_member(..., -1) to get the final member first and
+		 * abort the loop when we go beyond that?
 		 */
-		num_members = list_length(cur_ec->ec_members);
+		num_members = bms_num_members(cur_ec->ec_member_indexes);
+		j = -1;
 		for (int pos = 0; pos < num_members; pos++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em;
+
+			j = bms_next_member(cur_ec->ec_member_indexes, j);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2794,7 +2851,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_relids,
 													   top_parent_relids);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +2914,6 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +2922,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2880,9 +2937,11 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
+
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
@@ -2896,9 +2955,10 @@ 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)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
@@ -2986,7 +3046,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3044,7 +3104,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3075,7 +3135,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,7 +3143,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
@@ -3106,9 +3167,10 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 		if (cur_em->em_is_child)
 			continue;			/* ignore children here */
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7d176e7b00..4c18ada572 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3073,8 +3073,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3092,7 +3092,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3116,9 +3116,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(pathkey->pk_eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = (EquivalenceMember *) list_nth(root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index b5d6c977ee..e3e87f162c 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1396,9 +1396,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1453,11 +1455,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			i = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((i = bms_next_member(sub_eclass->ec_member_indexes, i)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, i);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
@@ -1516,7 +1518,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1926,7 +1928,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1947,9 +1949,10 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			/* Potential future join partner? */
 			if (!em->em_is_const && !em->em_is_child &&
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e37f2933eb..b8aeb2e84a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6116,7 +6120,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6157,6 +6162,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6166,8 +6173,9 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = ((EquivalenceMember *) list_nth(root->eq_members, first_em))->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6183,7 +6191,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6214,7 +6222,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6230,7 +6238,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6301,7 +6309,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6310,7 +6319,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6336,7 +6347,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6346,7 +6358,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6696,7 +6710,7 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6737,6 +6751,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6746,8 +6762,9 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = ((EquivalenceMember *) list_nth(root->eq_members, first_em))->em_datatype;
 		}
 		else
 		{
@@ -6759,7 +6776,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 06ad856eac..4a5be95875 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -618,6 +618,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 0bd99acf83..f3e6c19fc7 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 043181b586..0ec3736e52 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index e2081db4ed..1b2b1fcf22 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,9 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -874,6 +877,12 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that memtion this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1262,7 +1271,7 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
-	List	   *ec_members;		/* list of EquivalenceMembers */
+	Bitmapset  *ec_member_indexes; /* Indexes into PlannerInfo's eq_members */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 54ab709c67..ba75d52107 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
#11Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#10)
6 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Thu, Jul 28, 2022 at 6:35 AM David Rowley <dgrowleyml@gmail.com> wrote:

I've attached a very half-done patch that might help you get started
on this.

Thank you so much for creating the patch. I have implemented your
approach and attached a new version of the patch to this email.

If you have already applied David's patch, please start the 'git am'
command from 0002-Fix-bugs.patch. All regression tests passed with
this patch on my environment.

1. Optimizations

The new optimization techniques utilizing Bitmapsets are implemented
as the following functions in src/include/optimizer/paths.h.

* get_eclass_members_indexes_for_relids()
* get_eclass_members_indexes_for_not_children()
* get_eclass_members_indexes_for_relids_or_not_children()
* get_eclass_members_indexes_for_subsets_of_relids()
* get_eclass_members_indexes_for_subsets_of_relids_or_not_children()
// I think the names of these functions need to be reconsidered.

These functions intersect ec->ec_member_indexes and some Bitmapset and
return indexes of EquivalenceMembers that we want to get.

The implementation of the first three functions listed above is
simple. However, the rest functions regarding the bms_is_subset()
condition are a bit more complicated. I have optimized this case based
on Tom's idea. The detailed steps are as follows.

I. Intersect ec->ec_member_indexes and the Bitmapset in RelOptInfo.
This intersection set is a candidate for the EquivalenceMembers to be
retrieved.
II. Remove from the candidate set the members that do not satisfy the
bms_is_subset().

Optimization for EquivalenceMembers with a constant value is one of
the future works.

2. Experimental Results

I conducted an experiment by using the original query, which is
attached to this email. You can reproduce this experiment by the
following commands.

=====
psql -f create-tables.sql
psql -f query.sql
=====

The following table and the attached figure describe the experimental result.

Planning time of "query.sql" (n = the number of partitions)
----------------------------------------------------------------
n | Master (ms) | Patched (ms) | Speedup (%) | Speedup (ms)
----------------------------------------------------------------
1 | 0.809 | 0.760 | 6.09% | 0.049
2 | 0.799 | 0.811 | -1.53% | -0.012
4 | 1.022 | 0.989 | 3.20% | 0.033
8 | 1.357 | 1.325 | 2.32% | 0.032
16 | 2.149 | 2.026 | 5.69% | 0.122
32 | 4.357 | 3.925 | 9.91% | 0.432
64 | 9.543 | 7.543 | 20.96% | 2.000
128 | 27.195 | 15.823 | 41.82% | 11.372
256 | 130.207 | 52.664 | 59.55% | 77.542
384 | 330.642 | 112.324 | 66.03% | 218.318
512 | 632.009 | 197.957 | 68.68% | 434.052
640 | 1057.193 | 306.861 | 70.97% | 750.333
768 | 1709.914 | 463.628 | 72.89% | 1246.287
896 | 2531.685 | 738.827 | 70.82% | 1792.858
1024 | 3516.592 | 858.211 | 75.60% | 2658.381
----------------------------------------------------------------

-------------------------------------------------------
n | Stddev of Master (ms) | Stddev of Patched (ms)
-------------------------------------------------------
1 | 0.085 | 0.091
2 | 0.061 | 0.091
4 | 0.153 | 0.118
8 | 0.203 | 0.107
16 | 0.150 | 0.153
32 | 0.313 | 0.242
64 | 0.411 | 0.531
128 | 1.263 | 1.109
256 | 5.592 | 4.714
384 | 17.423 | 6.625
512 | 20.172 | 7.188
640 | 40.964 | 26.246
768 | 61.924 | 31.741
896 | 66.481 | 27.819
1024 | 80.950 | 49.162
-------------------------------------------------------

The speed up with the new patch was up to 75.6% and 2.7 seconds. The
patch achieved a 21.0% improvement even with 64 partitions, which is a
realistic size. We can conclude that this optimization is very
effective in workloads with highly partitioned tables.

Performance degradation occurred only when the number of partitions
was 2, and its degree was 1.53% or 12 microseconds. This degradation
is the difference between the average planning times of 10000 runs.
Their standard deviations far exceed the difference in averages. It is
unclear whether this degradation is an error.

=====

I'm looking forward to your comments.

--
Best regards,
Yuya Watari

Attachments:

0002-Fix-bugs.patchapplication/octet-stream; name=0002-Fix-bugs.patchDownload
From e5be91deda817cec4752b87555e50b6944a96a00 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 8 Aug 2022 15:50:51 +0900
Subject: [PATCH 2/3] Fix bugs

---
 contrib/postgres_fdw/postgres_fdw.c     |  8 +--
 src/backend/optimizer/path/equivclass.c | 69 +++++++++++++++++--------
 src/backend/optimizer/path/pathkeys.c   |  6 +--
 3 files changed, 54 insertions(+), 29 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 5687d0182f..6bbf10d679 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7455,7 +7455,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		int			i;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7471,10 +7471,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			expr = ((RelabelType *) expr)->arg;
 
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		i = -1;
-		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+		j = -1;
+		while ((j = bms_next_member(ec->ec_member_indexes, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, j);
 			Expr	   *em_expr;
 
 			/* Don't match constants */
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index f0dc77a2a2..90c96de7d8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -365,7 +365,33 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes, ec2->ec_member_indexes);
+		/*
+		 * TODO: "bms_add_members(ec1->ec_member_indexes, ec2->ec_member_indexes)"
+		 * did not work to combine two EquivalenceClasses. This is probably because
+		 * the order of the EquivalenceMembers is different from the previous
+		 * implementation, which added the ec2's EquivalenceMembers to the end of
+		 * the list.
+		 */
+		i = -1;
+		while ((i = bms_next_member(ec2->ec_member_indexes, i)) >= 0)
+		{
+			EquivalenceMember  *em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			int					em_index = list_length(root->eq_members);
+			int					j;
+
+			ec1->ec_member_indexes = bms_add_member(ec1->ec_member_indexes, em_index);
+			root->eq_members = lappend(root->eq_members, em);
+
+			j = -1;
+			while ((j = bms_next_member(em->em_relids, j)) > 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[j];
+				if (bms_equal(rel->relids, em->em_relids))
+				{
+					rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+				}
+			}
+		}
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -585,8 +611,10 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr,
 	while ((i = bms_next_member(relids, i)) > 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+		if (bms_equal(rel->relids, relids))
+		{
+			rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+		}
 	}
 
 	return em;
@@ -2366,19 +2394,14 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes, coal_idx);
 
 			/* Remove the member from each of the relations */
-			/* XXX check this is right. Is there a neater way? */
-			i = -1;
-			while ((i = bms_next_member(left_relids, i)) > 0)
-			{
-				RelOptInfo *rel = root->simple_rel_array[i];
-				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, i);
-			}
-
 			i = -1;
-			while ((i = bms_next_member(right_relids, i)) > 0)
+			while ((i = bms_next_member(coal_em->em_relids, i)) > 0)
 			{
 				RelOptInfo *rel = root->simple_rel_array[i];
-				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, i);
+				if (bms_equal(rel->relids, coal_em->em_relids))
+				{
+					rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, coal_idx);
+				}
 			}
 
 			return true;
@@ -2490,16 +2513,17 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		i = -1;
-		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+		j = -1;
+		while ((j = bms_next_member(ec->ec_member_indexes, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2914,6 +2938,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2937,10 +2962,10 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
-		i = -1;
-		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_member_indexes, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
@@ -2955,10 +2980,10 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Found our match.  Scan the other EC members and attempt to generate
 		 * joinclauses.
 		 */
-		i = -1;
-		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_member_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			EquivalenceMember *other_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 1a38aa57a7..de864023b2 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1490,11 +1490,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			int			i = -1;
+			int			j = -1;
 
-			while ((i = bms_next_member(sub_eclass->ec_member_indexes, i)) >= 0)
+			while ((j = bms_next_member(sub_eclass->ec_member_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, i);
+				EquivalenceMember *sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
-- 
2.35.3.windows.1

0003-Implement-optimizations-based-on-Bitmapset.patchapplication/octet-stream; name=0003-Implement-optimizations-based-on-Bitmapset.patchDownload
From eb6e080b3053c4b45d97989f420e08e310b362c1 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 8 Aug 2022 15:51:36 +0900
Subject: [PATCH 3/3] Implement optimizations based on Bitmapset

---
 contrib/postgres_fdw/postgres_fdw.c       |  17 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/equivclass.c   | 200 +++++++++++++---------
 src/backend/optimizer/path/indxpath.c     |  10 +-
 src/backend/optimizer/path/pathkeys.c     |  27 +--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/relnode.c      |   8 +
 src/include/nodes/pathnodes.h             |  12 ++
 src/include/optimizer/paths.h             | 152 +++++++++++++++-
 10 files changed, 329 insertions(+), 103 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6bbf10d679..d2b69a4171 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7413,17 +7413,19 @@ EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
 	int		i = -1;
+	Bitmapset *matching_ems =
+		get_eclass_members_indexes_for_subsets_of_relids(root, ec, rel->relids);
 
-	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
 		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
+		Assert(bms_is_subset(em->em_relids, rel->relids));
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
+		if (!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
@@ -7456,6 +7458,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
 		int			j;
+		Bitmapset  *matching_ems;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7472,7 +7475,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 		/* Locate an EquivalenceClass member matching this expr, if any */
 		j = -1;
-		while ((j = bms_next_member(ec->ec_member_indexes, j)) >= 0)
+		/* Ignore child members */
+		matching_ems = get_eclass_members_indexes_for_not_children(root, ec);
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, j);
 			Expr	   *em_expr;
@@ -7481,9 +7486,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 9b6a5b08dc..394ae1483f 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2755,7 +2755,7 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 												(Expr *) var,
 												NULL,	/* below outer joins */
 												Int8LessOperator,
-												rel->relids,
+												rel,
 												false);
 	}
 
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 90c96de7d8..44505c0c36 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -378,19 +378,30 @@ process_equivalence(PlannerInfo *root,
 			EquivalenceMember  *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			int					em_index = list_length(root->eq_members);
 			int					j;
+			bool				relids_is_empty;
 
 			ec1->ec_member_indexes = bms_add_member(ec1->ec_member_indexes, em_index);
 			root->eq_members = lappend(root->eq_members, em);
 
 			j = -1;
+			relids_is_empty = true;
 			while ((j = bms_next_member(em->em_relids, j)) > 0)
 			{
 				RelOptInfo *rel = root->simple_rel_array[j];
+
+				relids_is_empty = false;
+				rel->eclass_referencing_member_indexes =
+					bms_add_member(rel->eclass_referencing_member_indexes, em_index);
 				if (bms_equal(rel->relids, em->em_relids))
 				{
 					rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 				}
 			}
+
+			if (relids_is_empty)
+			{
+				root->eq_empty_relids_indexes = bms_add_member(root->eq_empty_relids_indexes, em_index);
+			}
 		}
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
@@ -576,6 +587,7 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr,
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 	int					em_index;
 	int					i;
+	bool				relids_is_empty;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -606,17 +618,32 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr,
 	em_index = list_length(root->eq_members);
 	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
 	root->eq_members = lappend(root->eq_members, em);
+	if (!is_child)
+	{
+		root->eq_not_children_indexes = bms_add_member(root->eq_not_children_indexes,
+													   em_index);
+	}
 
 	i = -1;
+	relids_is_empty = true;
 	while ((i = bms_next_member(relids, i)) > 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
+
+		relids_is_empty = false;
+		rel->eclass_referencing_member_indexes =
+			bms_add_member(rel->eclass_referencing_member_indexes, em_index);
 		if (bms_equal(rel->relids, relids))
 		{
 			rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 		}
 	}
 
+	if (relids_is_empty)
+	{
+		root->eq_empty_relids_indexes = bms_add_member(root->eq_empty_relids_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -667,7 +694,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 						 Oid opcintype,
 						 Oid collation,
 						 Index sortref,
-						 Relids rel,
+						 RelOptInfo *rel,
 						 bool create_it)
 {
 	Relids		expr_relids;
@@ -688,6 +715,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		int			i;
+		Bitmapset  *matching_ems;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -702,17 +730,16 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
+		/*
+		 * Ignore child members unless they match the request.
+		 */
+		matching_ems =
+			get_eclass_members_indexes_for_relids_or_not_children(root, cur_ec, rel);
 		i = -1;
-		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
-
-			/*
-			 * Ignore child members unless they match the request.
-			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel->relids));
 
 			/*
 			 * If below an outer join, don't match constants: they're not as
@@ -809,13 +836,13 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
 		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
 		{
-			RelOptInfo *rel = root->simple_rel_array[i];
+			RelOptInfo *rel2 = root->simple_rel_array[i];
 
-			Assert(rel->reloptkind == RELOPT_BASEREL ||
-				   rel->reloptkind == RELOPT_DEADREL);
+			Assert(rel2->reloptkind == RELOPT_BASEREL ||
+				   rel2->reloptkind == RELOPT_DEADREL);
 
-			rel->eclass_indexes = bms_add_member(rel->eclass_indexes,
-												 ec_index);
+			rel2->eclass_indexes = bms_add_member(rel2->eclass_indexes,
+												  ec_index);
 		}
 	}
 
@@ -843,13 +870,19 @@ find_ec_member_matching_expr(PlannerInfo *root,
 							 Relids relids)
 {
 	int			i;
+	Bitmapset  *matching_ems;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
+	/*
+	 * Ignore child members unless they belong to the requested rel.
+	 */
+	matching_ems =
+		get_eclass_members_indexes_for_subsets_of_relids_or_not_children(root, ec, relids);
 	i = -1;
-	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
 		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		Expr	   *emexpr;
@@ -861,12 +894,7 @@ find_ec_member_matching_expr(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
-		/*
-		 * Ignore child members unless they belong to the requested rel.
-		 */
-		if (em->em_is_child &&
-			!bms_is_subset(em->em_relids, relids))
-			continue;
+		Assert(!em->em_is_child || bms_is_subset(em->em_relids, relids));
 
 		/*
 		 * Match if same expression (after stripping relabel).
@@ -912,7 +940,13 @@ find_computable_ec_member(PlannerInfo *root,
 {
 	int		i = -1;
 
-	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+	/*
+	 * Ignore child members unless they belong to the requested rel.
+	 */
+	Bitmapset *matching_ems =
+		get_eclass_members_indexes_for_subsets_of_relids_or_not_children(root, ec, relids);
+
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
 		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		List	   *exprvars;
@@ -925,12 +959,7 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
-		/*
-		 * Ignore child members unless they belong to the requested rel.
-		 */
-		if (em->em_is_child &&
-			!bms_is_subset(em->em_relids, relids))
-			continue;
+		Assert(!em->em_is_child || bms_is_subset(em->em_relids, relids));
 
 		/*
 		 * Match if all Vars and quasi-Vars are available in "exprs".
@@ -1610,6 +1639,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *inner_members = NIL;
 	ListCell   *lc1;
 	int			i;
+	Bitmapset  *matching_ems;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1620,18 +1650,17 @@ 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.
 	 */
+	/*
+	 * We don't need to check explicitly for child EC members.  This test
+	 * against join_relids will cause them to be ignored except when
+	 * considering a child inner rel, which is what we want.
+	 */
+	matching_ems = get_eclass_members_indexes_for_subsets_of_relids(root, ec, join_relids);
 	i = -1;
-	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *)list_nth(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
-		 * considering a child inner rel, which is what we want.
-		 */
-		if (!bms_is_subset(cur_em->em_relids, join_relids))
-			continue;			/* not computable yet, or wrong child */
+		Assert(bms_is_subset(cur_em->em_relids, join_relids));
 
 		if (bms_is_subset(cur_em->em_relids, outer_relids))
 			outer_members = lappend(outer_members, cur_em);
@@ -2392,12 +2421,16 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes, coal_idx);
+			root->eq_not_children_indexes = bms_del_member(root->eq_not_children_indexes,
+														   coal_idx);
 
 			/* Remove the member from each of the relations */
 			i = -1;
 			while ((i = bms_next_member(coal_em->em_relids, i)) > 0)
 			{
 				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_referencing_member_indexes =
+					bms_del_member(rel->eclass_referencing_member_indexes, coal_idx);
 				if (bms_equal(rel->relids, coal_em->em_relids))
 				{
 					rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, coal_idx);
@@ -2442,18 +2475,20 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		bool		item1member = false;
 		bool		item2member = false;
 		int			i;
+		Bitmapset  *matching_ems;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
 		i = -1;
-		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
+		/* ignore children here */
+		matching_ems = get_eclass_members_indexes_for_not_children(root, ec);
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			Assert(!em->em_is_child);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2514,6 +2549,7 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
 		int					j;
+		Bitmapset		   *matching_ems;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
@@ -2521,13 +2557,14 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		/* Note: it seems okay to match to "broken" eclasses here */
 
 		j = -1;
-		while ((j = bms_next_member(ec->ec_member_indexes, j)) >= 0)
+		/* ignore children here */
+		matching_ems = get_eclass_members_indexes_for_not_children(root, ec);
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, j);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2636,8 +2673,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);
-		int			num_members;
+		int			final_member;
 		int			j;
+		Bitmapset  *matching_ems;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2656,28 +2694,28 @@ add_child_rel_equivalences(PlannerInfo *root,
 		 * Use bms_prev_member(..., -1) to get the final member first and
 		 * abort the loop when we go beyond that?
 		 */
-		num_members = bms_num_members(cur_ec->ec_member_indexes);
+		final_member = bms_prev_member(cur_ec->ec_member_indexes, -1);
+
+		/*
+		 * We consider only original EC members here, not
+		 * already-transformed child members.  Otherwise, if some original
+		 * member expression references more than one appendrel, we'd get
+		 * an O(N^2) explosion of useless derived expressions for
+		 * combinations of children.  (But add_child_join_rel_equivalences
+		 * may add targeted combinations for partitionwise-join purposes.)
+		 */
+		/* ignore children here */
+		matching_ems = get_eclass_members_indexes_for_not_children(root, cur_ec);
 		j = -1;
-		for (int pos = 0; pos < num_members; pos++)
+		while ((j = bms_next_member(matching_ems, j)) >= 0 && j <= final_member)
 		{
 			EquivalenceMember *cur_em;
-
-			j = bms_next_member(cur_ec->ec_member_indexes, j);
 			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);
 
 			/* Does this member reference child's topmost parent rel? */
 			if (bms_overlap(cur_em->em_relids, top_parent_relids))
@@ -2780,8 +2818,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);
-		int			num_members;
+		int			final_member;
 		int			j;
+		Bitmapset  *matching_ems;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2800,24 +2839,24 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		 * Use bms_prev_member(..., -1) to get the final member first and
 		 * abort the loop when we go beyond that?
 		 */
-		num_members = bms_num_members(cur_ec->ec_member_indexes);
+		final_member = bms_prev_member(cur_ec->ec_member_indexes, -1);
+
+		/*
+		 * We consider only original EC members here, not
+		 * already-transformed child members.
+		 */
+		/* ignore children here */
+		matching_ems = get_eclass_members_indexes_for_not_children(root, cur_ec);
 		j = -1;
-		for (int pos = 0; pos < num_members; pos++)
+		while ((j = bms_next_member(matching_ems, j)) >= 0 && j <= final_member)
 		{
 			EquivalenceMember *cur_em;
-
-			j = bms_next_member(cur_ec->ec_member_indexes, j);
 			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2939,6 +2978,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		int					j;
+		Bitmapset		   *matching_ems;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2962,13 +3002,14 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
+		matching_ems = get_eclass_members_indexes_for_relids(root, cur_ec, rel);
 		j = -1;
-		while ((j = bms_next_member(cur_ec->ec_member_indexes, j)) >= 0)
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
 			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
-			if (bms_equal(cur_em->em_relids, rel->relids) &&
-				callback(root, rel, cur_ec, cur_em, callback_arg))
+			Assert(bms_equal(cur_em->em_relids, rel->relids));
+			if (callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
@@ -2981,14 +3022,15 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * joinclauses.
 		 */
 		j = -1;
-		while ((j = bms_next_member(cur_ec->ec_member_indexes, j)) >= 0)
+		/* ignore children here */
+		matching_ems = get_eclass_members_indexes_for_not_children(root, cur_ec);
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
 			EquivalenceMember *other_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3161,6 +3203,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 {
 	Relids		relids;
 	int			i;
+	Bitmapset  *matching_ems;
 
 	Assert(!eclass->ec_merged);
 
@@ -3192,13 +3235,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
+	/* ignore children here */
+	matching_ems = get_eclass_members_indexes_for_not_children(root, eclass);
 	i = -1;
-	while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4c18ada572..843b7c7be3 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3093,6 +3093,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
 		int			i;
+		Bitmapset  *matching_ems;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3117,14 +3118,15 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
 		i = -1;
-		while ((i = bms_next_member(pathkey->pk_eclass->ec_member_indexes, i)) >= 0)
+		/* No possibility of match if it references other relations */
+		matching_ems =
+			get_eclass_members_indexes_for_relids(root, pathkey->pk_eclass, index->rel);
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
 			EquivalenceMember *member = (EquivalenceMember *) list_nth(root->eq_members, i);
 			int			indexcol;
 
-			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
-				continue;
+			Assert(bms_equal(member->em_relids, index->rel->relids));
 
 			/*
 			 * We allow any column of the index to match each pathkey; they
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index de864023b2..6fcf0ccb01 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -212,7 +212,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 						   bool reverse_sort,
 						   bool nulls_first,
 						   Index sortref,
-						   Relids rel,
+						   RelOptInfo *rel,
 						   bool create_it)
 {
 	int16		strategy;
@@ -1134,7 +1134,7 @@ build_index_pathkeys(PlannerInfo *root,
 											  reverse_sort,
 											  nulls_first,
 											  0,
-											  index->rel->relids,
+											  index->rel,
 											  false);
 
 		if (cpathkey)
@@ -1290,7 +1290,7 @@ build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
 											  ScanDirectionIsBackward(scandir),
 											  ScanDirectionIsBackward(scandir),
 											  0,
-											  partrel->relids,
+											  partrel,
 											  false);
 
 
@@ -1343,7 +1343,7 @@ build_expression_pathkey(PlannerInfo *root,
 						 Expr *expr,
 						 Relids nullable_relids,
 						 Oid opno,
-						 Relids rel,
+						 RelOptInfo *rel,
 						 bool create_it)
 {
 	List	   *pathkeys;
@@ -1455,7 +1455,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 											 sub_member->em_datatype,
 											 sub_eclass->ec_collation,
 											 0,
-											 rel->relids,
+											 rel,
 											 false);
 
 				/*
@@ -1491,8 +1491,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 */
 			int			best_score = -1;
 			int			j = -1;
+			/* ignore children here */
+			Bitmapset  *matching_ems =
+				get_eclass_members_indexes_for_not_children(rel->subroot, sub_eclass);
 
-			while ((j = bms_next_member(sub_eclass->ec_member_indexes, j)) >= 0)
+			while ((j = bms_next_member(matching_ems, j)) >= 0)
 			{
 				EquivalenceMember *sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, j);
 				Expr	   *sub_expr = sub_member->em_expr;
@@ -1500,8 +1503,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1537,7 +1539,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 														sub_expr_type,
 														sub_expr_coll,
 														0,
-														rel->relids,
+														rel,
 														false);
 
 					/*
@@ -1966,6 +1968,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		EquivalenceClass *oeclass;
 		int			score;
 		int			i;
+		Bitmapset  *matching_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1987,12 +1990,14 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		/* compute score */
 		score = 0;
 		i = -1;
-		while ((i = bms_next_member(oeclass->ec_member_indexes, i)) >= 0)
+		matching_ems = get_eclass_members_indexes_for_not_children(root, oeclass);
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
+			Assert(!em->em_is_child);
 
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index d0bc9a467d..e71f115fd6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -620,6 +620,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
 	root->eq_members = NIL;
+	root->eq_not_children_indexes = NULL;
+	root->eq_empty_relids_indexes = NULL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index f3e6c19fc7..90a5dcae72 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -999,6 +999,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
 	subroot->eq_members = NIL;
+	subroot->eq_not_children_indexes = NULL;
+	subroot->eq_empty_relids_indexes = NULL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 520409f4ba..101742959a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -231,6 +231,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_referencing_member_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -645,6 +647,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_referencing_member_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -828,6 +832,8 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_referencing_member_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
@@ -1242,6 +1248,8 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_member_indexes = NULL;	/* TODO: Is this necessary? */
+	upperrel->eclass_referencing_member_indexes = NULL;	/* TODO: Is this necessary? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 34f2f39012..1cc23eec09 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -303,6 +303,12 @@ struct PlannerInfo
 	/* list of each EquivalenceMember */
 	List	   *eq_members;
 
+	/* indexes of EquivalenceMembers which are not children */
+	Bitmapset  *eq_not_children_indexes;
+
+	/* indexes of EquivalenceMembers whose relids are empty */
+	Bitmapset  *eq_empty_relids_indexes;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -891,6 +897,12 @@ typedef struct RelOptInfo
 	 */
 	Bitmapset  *eclass_member_indexes;
 
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that reference this rel
+	 * TODO: Its variable name should be more refined.
+	 */
+	Bitmapset  *eclass_referencing_member_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index c9482f86cb..d9dff21c1e 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -134,7 +134,7 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Oid opcintype,
 												  Oid collation,
 												  Index sortref,
-												  Relids rel,
+												  RelOptInfo *rel,
 												  bool create_it);
 extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
 													   EquivalenceClass *ec,
@@ -189,6 +189,154 @@ extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
 
+/*
+ * TODO: The following helper functions are placed in this header file to help
+ * compiler inline them.
+ */
+
+/*
+ * get_eclass_members_indexes_for_relids
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_members list for all EquivalenceMembers whose em_relids is equal to
+ *		the given RelOptInfo's relids
+ */
+static inline Bitmapset *
+get_eclass_members_indexes_for_relids(PlannerInfo *root,
+									  EquivalenceClass *ec,
+									  RelOptInfo *rel)
+{
+	if (rel == NULL)
+		return bms_intersect(ec->ec_member_indexes, root->eq_empty_relids_indexes);
+	else
+		return bms_intersect(ec->ec_member_indexes, rel->eclass_member_indexes);
+}
+
+/*
+ * get_eclass_members_indexes_for_not_children
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_members list for all EquivalenceMembers whose em_is_child is false
+ */
+static inline Bitmapset *
+get_eclass_members_indexes_for_not_children(PlannerInfo *root, EquivalenceClass *ec)
+{
+	return bms_intersect(ec->ec_member_indexes, root->eq_not_children_indexes);
+}
+
+/*
+ * get_eclass_members_indexes_for_relids_or_not_children
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_members list for all EquivalenceMembers whose em_relids is equal to
+ *		the given RelOptInfo's relids, or em_is_child is false
+ */
+static inline Bitmapset *
+get_eclass_members_indexes_for_relids_or_not_children(PlannerInfo *root,
+													  EquivalenceClass *ec,
+													  RelOptInfo *rel)
+{
+	Bitmapset  *em_indexes;
+
+	if (rel == NULL)
+		em_indexes = bms_union(root->eq_not_children_indexes, root->eq_empty_relids_indexes);
+	else
+		em_indexes = bms_union(root->eq_not_children_indexes, rel->eclass_member_indexes);
+
+	em_indexes = bms_int_members(em_indexes, ec->ec_member_indexes);
+
+	return em_indexes;
+}
+
+/*
+ * get_eclass_members_indexes_for_subsets_of_relids
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_members list for all EquivalenceMembers whose em_relids is a subset
+ *		of the given relids
+ */
+static inline Bitmapset *
+get_eclass_members_indexes_for_subsets_of_relids(PlannerInfo *root,
+												 EquivalenceClass *ec,
+												 Relids relids)
+{
+	Bitmapset  *em_indexes;
+	int			i;
+
+	if (relids == NULL)
+		return bms_intersect(ec->ec_member_indexes, root->eq_empty_relids_indexes);
+
+	em_indexes = NULL;
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/* Retrieve candidates that we want to get */
+		Bitmapset  *em_candidates = bms_intersect(ec->ec_member_indexes,
+												  rel->eclass_referencing_member_indexes);
+		int			j;
+
+		/* Check if the candidates satisfy the condition */
+		j = -1;
+		while ((j = bms_next_member(em_candidates, j)) >= 0)
+		{
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, j);
+
+			if (bms_is_subset(em->em_relids, relids))
+			{
+				/* Satisfies, so add it to the result Bitmapset */
+				em_indexes = bms_add_member(em_indexes, j);
+			}
+		}
+	}
+
+	return em_indexes;
+}
+
+/*
+ * get_eclass_members_indexes_for_subsets_of_relids_or_not_children
+ *		Build and return a Bitmapset containing the indexes into root's
+ *		eq_members list for all EquivalenceMembers whose em_relids is a subset
+ *		of the given relids, or em_is_child is false
+ */
+static inline Bitmapset *
+get_eclass_members_indexes_for_subsets_of_relids_or_not_children(PlannerInfo *root,
+																 EquivalenceClass *ec,
+																 Relids relids)
+{
+	Bitmapset  *em_indexes;
+	int			i;
+
+	if (relids == NULL)
+		return bms_int_members(bms_union(root->eq_empty_relids_indexes,
+										 root->eq_not_children_indexes),
+							   ec->ec_member_indexes);
+
+	em_indexes = bms_intersect(ec->ec_member_indexes, root->eq_not_children_indexes);
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/* Retrieve candidates that we want to get */
+		Bitmapset  *em_candidates = bms_intersect(ec->ec_member_indexes,
+												  rel->eclass_referencing_member_indexes);
+		int			j;
+
+		/* Check if the candidates satisfy the condition */
+		j = -1;
+		while ((j = bms_next_member(em_candidates, j)) >= 0)
+		{
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, j);
+
+			if (bms_is_subset(em->em_relids, relids))
+			{
+				/* Satisfies, so add it to the result Bitmapset */
+				em_indexes = bms_add_member(em_indexes, j);
+			}
+		}
+	}
+
+	return em_indexes;
+}
+
 /*
  * pathkeys.c
  *	  utilities for matching and building path keys
@@ -226,7 +374,7 @@ extern List *build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
 									  ScanDirection scandir, bool *partialkeys);
 extern List *build_expression_pathkey(PlannerInfo *root, Expr *expr,
 									  Relids nullable_relids, Oid opno,
-									  Relids rel, bool create_it);
+									  RelOptInfo *rel, bool create_it);
 extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 									   List *subquery_pathkeys,
 									   List *subquery_tlist);
-- 
2.35.3.windows.1

0001-EClass-member-speedup.patchapplication/octet-stream; name=0001-EClass-member-speedup.patchDownload
From 63956602b5278b3d65b5adfb76523bb00661b7f2 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 8 Aug 2022 08:31:50 +0900
Subject: [PATCH 1/3] EClass member speedup

Written by David Rowley <dgrowleyml@gmail.com>
https://www.postgresql.org/message-id/CAApHDvowVzvB2QdLKEPUTFBERuJjox2ZCuHQo4sieR5yvmmHpw%40mail.gmail.com
---
 contrib/postgres_fdw/postgres_fdw.c       |  13 +-
 src/backend/nodes/outfuncs.c              |   2 +-
 src/backend/nodes/print.c                 |  18 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |   7 +-
 src/backend/optimizer/path/equivclass.c   | 240 ++++++++++++++--------
 src/backend/optimizer/path/indxpath.c     |  17 +-
 src/backend/optimizer/path/pathkeys.c     |  21 +-
 src/backend/optimizer/plan/createplan.c   |  79 ++++---
 src/backend/optimizer/plan/planner.c      |   1 +
 src/backend/optimizer/prep/prepjointree.c |   1 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/include/nodes/pathnodes.h             |  11 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |   3 +-
 15 files changed, 263 insertions(+), 158 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170ce..5687d0182f 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,11 +7412,11 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7455,7 +7455,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7471,9 +7471,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			expr = ((RelabelType *) expr)->arg;
 
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Expr	   *em_expr;
 
 			/* Don't match constants */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a96f2ee8c6..2060b73686 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,7 +430,7 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..5953b79fe8 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,10 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +454,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fb28e6411a..1043b67aca 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = (EquivalenceMember *) list_nth(root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,9 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
 		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+			list_nth(root->eq_members, first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7991295548..f0dc77a2a2 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,7 +32,7 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static EquivalenceMember *add_eq_member(PlannerInfo *root, EquivalenceClass *ec,
 										Expr *expr, Relids relids, Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
@@ -139,6 +139,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +266,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +286,10 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -364,7 +365,7 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes, ec2->ec_member_indexes);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -378,7 +379,7 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
+		ec2->ec_member_indexes = NULL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -398,8 +399,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, 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,
@@ -416,8 +417,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, 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,
@@ -438,7 +439,7 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
+		ec->ec_member_indexes = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -450,10 +451,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -542,10 +543,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	int					em_index;
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -573,7 +577,17 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
+	em_index = list_length(root->eq_members);
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) > 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
 
 	return em;
 }
@@ -645,7 +659,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +674,10 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,7 +725,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
+	newec->ec_member_indexes = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -732,7 +747,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -794,19 +809,21 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +882,11 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +993,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1096,7 +1113,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1145,7 +1162,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,7 +1171,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
 		list_length(ec->ec_sources) == 1)
 	{
 		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
@@ -1172,9 +1189,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1204,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1240,7 +1259,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1272,10 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1315,9 +1335,10 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * pre-analysis of which members we prefer to join, but it's no worse than
 	 * what happened in the pre-8.3 code.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1445,7 +1466,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1537,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1560,6 +1581,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1592,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.
 	 */
-	foreach(lc1, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = (EquivalenceMember *)list_nth(root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -2104,7 +2127,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2142,10 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
@@ -2139,9 +2163,10 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2223,8 +2248,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,9 +2277,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			coal_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Assert(!coal_em->em_is_child);	/* no children yet */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
@@ -2269,7 +2295,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2311,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2336,7 +2363,24 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes, coal_idx);
+
+			/* Remove the member from each of the relations */
+			/* XXX check this is right. Is there a neater way? */
+			i = -1;
+			while ((i = bms_next_member(left_relids, i)) > 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, i);
+			}
+
+			i = -1;
+			while ((i = bms_next_member(right_relids, i)) > 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes, i);
+			}
+
 			return true;
 		}
 
@@ -2374,15 +2418,16 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2490,16 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
 
 		/* 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)
+		i = -1;
+		while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2568,6 +2613,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		int			num_members;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2580,15 +2626,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
+		/* Only loop over existing members, not the newly added ones */
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * XXX this is ugly. Is there a better way? bms_copy()?
+		 * Use bms_prev_member(..., -1) to get the final member first and
+		 * abort the loop when we go beyond that?
 		 */
-		num_members = list_length(cur_ec->ec_members);
+		num_members = bms_num_members(cur_ec->ec_member_indexes);
+		j = -1;
 		for (int pos = 0; pos < num_members; pos++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em;
+
+			j = bms_next_member(cur_ec->ec_member_indexes, j);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2653,7 +2704,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,6 +2757,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		int			num_members;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2718,15 +2770,20 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
+		/* Only loop over existing members, not the newly added ones */
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * XXX this is ugly. Is there a better way? bms_copy()?
+		 * Use bms_prev_member(..., -1) to get the final member first and
+		 * abort the loop when we go beyond that?
 		 */
-		num_members = list_length(cur_ec->ec_members);
+		num_members = bms_num_members(cur_ec->ec_member_indexes);
+		j = -1;
 		for (int pos = 0; pos < num_members; pos++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em;
+
+			j = bms_next_member(cur_ec->ec_member_indexes, j);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, j);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2794,7 +2851,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_relids,
 													   top_parent_relids);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +2914,6 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +2922,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2880,9 +2937,11 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * match.  See also get_eclass_for_sort_expr.)
 		 */
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
+
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
@@ -2896,9 +2955,10 @@ 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)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
@@ -2986,7 +3046,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3044,7 +3104,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3075,7 +3135,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,7 +3143,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
@@ -3106,9 +3167,10 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 		if (cur_em->em_is_child)
 			continue;			/* ignore children here */
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7d176e7b00..4c18ada572 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3073,8 +3073,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3092,7 +3092,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3116,9 +3116,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(pathkey->pk_eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = (EquivalenceMember *) list_nth(root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 1fa7fc99b5..1a38aa57a7 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1431,9 +1431,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1488,11 +1490,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			i = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((i = bms_next_member(sub_eclass->ec_member_indexes, i)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = (EquivalenceMember *) list_nth(rel->subroot->eq_members, i);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
@@ -1551,7 +1553,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1963,7 +1965,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1984,9 +1986,10 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = (EquivalenceMember *) list_nth(root->eq_members, i);
 
 			/* Potential future join partner? */
 			if (!em->em_is_const && !em->em_is_child &&
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e37f2933eb..b8aeb2e84a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6116,7 +6120,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6157,6 +6162,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6166,8 +6173,9 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = ((EquivalenceMember *) list_nth(root->eq_members, first_em))->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6183,7 +6191,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6214,7 +6222,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6230,7 +6238,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6301,7 +6309,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6310,7 +6319,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6336,7 +6347,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6346,7 +6358,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6696,7 +6710,7 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6737,6 +6751,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6746,8 +6762,9 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = ((EquivalenceMember *) list_nth(root->eq_members, first_em))->em_datatype;
 		}
 		else
 		{
@@ -6759,7 +6776,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 64632db73c..d0bc9a467d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 0bd99acf83..f3e6c19fc7 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 043181b586..0ec3736e52 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 3b065139e6..34f2f39012 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,9 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -882,6 +885,12 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that memtion this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1270,7 +1279,7 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
-	List	   *ec_members;		/* list of EquivalenceMembers */
+	Bitmapset  *ec_member_indexes; /* Indexes into PlannerInfo's eq_members */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d11cdac7f8..c9482f86cb 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
-- 
2.35.3.windows.1

figure.pngimage/png; name=figure.pngDownload
query.sqlapplication/octet-stream; name=query.sqlDownload
create-tables.sqlapplication/octet-stream; name=create-tables.sqlDownload
#12David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#11)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, 8 Aug 2022 at 23:28, Yuya Watari <watari.yuya@gmail.com> wrote:

If you have already applied David's patch, please start the 'git am'
command from 0002-Fix-bugs.patch. All regression tests passed with
this patch on my environment.

Thanks for fixing those scope bugs.

In regards to the 0002 patch, you have;

+ * TODO: "bms_add_members(ec1->ec_member_indexes, ec2->ec_member_indexes)"
+ * did not work to combine two EquivalenceClasses. This is probably because
+ * the order of the EquivalenceMembers is different from the previous
+ * implementation, which added the ec2's EquivalenceMembers to the end of
+ * the list.

as far as I can see, the reason the code I that wrote caused the
following regression test failure;

-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)

was down to how generate_base_implied_equalities_const() marks the EC
as ec_broken = true without any regard to cleaning up the work it's
partially already complete.

Because the loop inside generate_base_implied_equalities_const() just
breaks as soon as we're unable to find a valid equality operator for
the two given types, with my version, since the EquivalenceMember's
order has effectively changed, we just discover the EC is broken
before we call process_implied_equality() ->
distribute_restrictinfo_to_rels(). In the code you've added, the
EquivalenceMembers are effectively still in the original order and the
process_implied_equality() -> distribute_restrictinfo_to_rels() gets
done before we discover the broken EC. The same qual is just added
again during generate_base_implied_equalities_broken(), which is why
the plan has a duplicate ff=42.

This is all just down to the order that the ECs are merged. If you'd
just swapped the order of the items in the query's WHERE clause to
become:

where ec1.ff = 42::int8 and ss1.x = ec1.f1 and ec1.ff = ec1.f1;

then my version would keep the duplicate qual. For what you've changed
the code to, the planner would not have produced the duplicate ff=42
qual if you'd written the WHERE clause as follows:

where ss1.x = ec1.f1 and ec1.ff = ec1.f1 and ec1.ff = 42::int8;

In short, I think the code I had for that was fine and it's just the
expected plan that you should be editing. If we wanted to this
behaviour to be consistent then the fix should be to make
generate_base_implied_equalities_const() better at only distributing
the quals down to the relations after it has discovered that the EC is
not broken, or at least cleaning up the partial work that it's done if
it discovers a broken EC. The former seems better to me, but I doubt
that it matters too much as broken ECs should be pretty rare and it
does not seem worth spending too much effort making this work better.

I've not had a chance to look at the 0003 patch yet.

David

#13David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#12)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

esOn Tue, 9 Aug 2022 at 19:10, David Rowley <dgrowleyml@gmail.com> wrote:

I've not had a chance to look at the 0003 patch yet.

I've looked at the 0003 patch now.

The performance numbers look quite impressive, however, there were a
few things about the patch that I struggled to figure what they were
done the way you did them:

+ root->eq_not_children_indexes = bms_add_member(root->eq_not_children_indexes,

Why is that in PlannerInfo rather than in the EquivalenceClass?

if (bms_equal(rel->relids, em->em_relids))
{
rel->eclass_member_indexes =
bms_add_member(rel->eclass_member_indexes, em_index);
}

Why are you only adding the eclass_member_index to the RelOptInfo when
the em_relids contain a singleton relation?

I ended up going and fixing the patch to be more how I imagined it.

I've ended up with 3 Bitmapset fields in EquivalenceClass;
ec_member_indexes, ec_nonchild_indexes, ec_norel_indexes. I also
trimmed the number of helper functions down for obtaining the minimal
set of matching EquivalenceMember indexes to just:

Bitmapset *
get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
bool with_children, bool with_norel_members)

Bitmapset *
get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
Relids relids, bool with_children,
bool with_norel_members)

I'm not so much a fan of the bool parameters, but it seemed better
than having 8 different functions with each combination of the bool
paramters instead of 2.

The "strict" version of the function takes the intersection of
eclass_member_indexes for each rel mentioned in relids, whereas the
non-strict version does a union of those. Each then intersect that
with all members in the 'ec', or just the non-child members when
'with_children' is false. They both then optionally bms_add_members()
the ec_norel_members if with_norel_members is true. I found it
difficult to figure out the best order to do the intersection. That
really depends on if the particular query has many EquivalenceClasses
with few EquivalenceMembers or few EquivalenceClasses with many
EquivalenceMembers. bms_int_members() always recycles the left input.
Ideally, that would always be the smallest Bitmapset. Maybe it's worth
inventing a new version of bms_int_members() that recycles the input
with the least nwords. That would give the subsequent
bms_next_member() calls an easier time. Right now they'll need to loop
over a bunch of 0 words at the end for many queries.

A few problems I ran into along the way:

1. generate_append_tlist() generates Vars with varno=0. That causes
problems when we add Exprs from those in add_eq_member() as there is
no element at root->simple_rel_array[0] to add eclass_member_indexes
to.
2. The existing comment for EquivalenceMember.em_relids claims "all
relids appearing in em_expr", but that's just not true when it comes
to em_is_child members.

So far, I fixed #1 by adding a hack to setup_simple_rel_arrays() to do
"root->simple_rel_array[0] = makeNode(RelOptInfo);" I'm not suggesting
that's the correct fix. It might be possible to set the varnos to the
varnos from the first Append child instead.

The fact that #2 is not true adds quite a bit of complexity to the
patch and I think the patch might even misbehave as a result. It seems
there are cases where a child em_relids can contain additional relids
that are not present in the em_expr. For example, when a UNION ALL
child has a Const in the targetlist, as explained in a comment in
add_child_rel_equivalences(). However, there also seem to be cases
where the opposite is true. I had to add the following code in
add_eq_member() to stop a regression test failing:

if (is_child)
expr_relids = bms_add_members(expr_relids, relids);

That's to make sure we add eclass_member_indexes to each RelOptInfo
mentioned in the em_expr.

After doing all that, I noticed that your benchmark was showing that
create_join_clause() was the new bottleneck. This was due to having to
loop so many times over the ec_sources to find an already built
RestrictInfo. I went off and added some new code to optimize the
lookup of those in a similar way by adding a new Bitmapset field in
RelOptInfo to index which ec_sources it mentioned, which meant having
to move ec_sources into PlannerInfo. I don't think this part of the
patch is quite right yet as the code I have relies on em_relids being
the same as the ones mentioned in the RestrictInfo. That seems not
true for em_is_child EMs, so I think we probably need to add a new
field to EquivalenceMember that truly is just pull_varnos from
em_expr, or else look into some way to make em_relids mean that (like
the comment claims).

Here are my results from running your benchmark on master (@f6c750d31)
with and without the attached patch.

npart master (ms) patched (ms) speedup
2 0.28 0.29 95.92%
4 0.37 0.38 96.75%
8 0.53 0.56 94.43%
16 0.92 0.91 100.36%
32 1.82 1.70 107.57%
64 4.05 3.26 124.32%
128 10.83 6.69 161.89%
256 42.63 19.46 219.12%
512 194.31 42.60 456.14%
1024 1104.02 98.37 1122.33%

This resulted in some good additional gains in planner performance.
The 1024 partition case is now about 11x faster on my machine instead
of 4x. The 2 partition does regress slightly. There might be a few
things we can do about that, for example, move ec_collation up 1 to
shrink EquivalenceClass back down closer to the size it was before.
[1]: https://commitfest.postgresql.org/39/3810/

I've attached a draft patch with my revisions.

David

[1]: https://commitfest.postgresql.org/39/3810/

Attachments:

eclass_member_speedup_v3.patchtext/plain; charset=US-ASCII; name=eclass_member_speedup_v3.patchDownload
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170ce..e8996456be 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,19 +7412,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7455,7 +7459,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7470,19 +7476,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7496,8 +7505,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a96f2ee8c6..17a8c211c8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,9 +430,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fb28e6411a..a8289ddde6 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = list_nth_node(EquivalenceMember, root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5821,7 +5825,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7991295548..4e0a645c1a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,9 +819,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -729,10 +840,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -764,7 +874,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -794,19 +904,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +983,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +1095,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1063,7 +1182,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1096,7 +1215,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1120,7 +1239,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1145,7 +1264,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,10 +1273,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1172,9 +1291,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1307,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1215,9 +1338,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1227,7 +1350,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1240,7 +1364,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1378,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1290,7 +1418,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1313,11 +1441,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1326,6 +1458,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids, false);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1346,11 +1479,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1391,9 +1525,9 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1445,7 +1579,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1650,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1559,7 +1693,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1706,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1732,12 +1872,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1893,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1815,9 +1959,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1825,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1835,9 +1983,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1876,7 +2028,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2068,7 +2220,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2087,6 +2240,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2094,6 +2248,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2103,8 +2258,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2275,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2139,9 +2300,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2220,11 +2383,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2251,10 +2415,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2269,7 +2437,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2453,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2332,11 +2502,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2368,21 +2552,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2636,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2507,16 +2703,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2567,7 +2766,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2581,33 +2781,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2653,7 +2841,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2705,7 +2893,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2719,24 +2908,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2745,10 +2929,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2794,7 +2975,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_relids,
 													   top_parent_relids);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +3038,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +3048,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2879,10 +3062,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2896,14 +3082,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2986,7 +3173,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3000,7 +3187,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3044,7 +3231,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3074,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,12 +3270,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3105,17 +3293,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3199,7 +3385,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3235,3 +3421,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7d176e7b00..65d4ceccfd 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3073,8 +3073,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3091,8 +3091,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3116,9 +3117,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 1fa7fc99b5..d1f68ecf64 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1431,9 +1431,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1488,18 +1492,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1551,7 +1556,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1962,8 +1967,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1983,19 +1989,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e37f2933eb..d3b58abba6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6116,7 +6120,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6157,6 +6162,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6166,8 +6173,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6183,7 +6192,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6214,7 +6223,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6230,7 +6239,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6301,7 +6310,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6310,7 +6320,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6336,7 +6348,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6346,7 +6359,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6696,7 +6711,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6737,6 +6753,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6746,8 +6764,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6759,7 +6779,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 64632db73c..9d39b8287f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 0bd99acf83..8dac986f42 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 043181b586..0ec3736e52 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 520409f4ba..08cc90d4b9 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -645,6 +651,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -828,6 +837,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 3b065139e6..c60fa7b98b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -882,6 +891,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1270,9 +1297,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d11cdac7f8..f6835f80f5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -161,7 +162,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -187,6 +189,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
#14Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#13)
7 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear David,

I really appreciate your reply and your modifying the patch. The
performance improvements are quite impressive. I believe these
improvements will help PostgreSQL users. Thank you again.

The 2 partition does regress slightly. There might be a few
things we can do about that

I tried to solve this regression problem. From here, I will refer to
the patch you sent on August 16th as the v3 patch. I will also call my
patch attached to this email the v4 patch. I will discuss the v4 patch
later.

Additionally, I give names to queries.
* Query A: The query we have been using in previous emails, which
joins students, scores, and gpas tables.
* Query B: The query which is attached to this email.

Query B is as follows:

===
SELECT *
FROM testtable_1, testtable_2, testtable_3, testtable_4, testtable_5,
testtable_6, testtable_7, testtable_8
WHERE testtable_1.x = testtable_2.x AND testtable_1.x = testtable_3.x
AND testtable_1.x = testtable_4.x AND testtable_1.x = testtable_5.x
AND testtable_1.x = testtable_6.x AND testtable_1.x = testtable_7.x
AND testtable_1.x = testtable_8.x;
===

Query A joins three tables, whereas Query B joins eight tables. Since
EquivalenceClass is used when handling chained join conditions, I
thought queries joining many tables, such as Query B, would have
greater performance impacts.

I have investigated the v3 patch with these queries. As a result, I
did not observe any regressions in Query A in my environment. However,
the v3 patch showed significant degradation in Query B.

The following table and Figures 1 and 2 describe the result. The v3
patch resulted in a regression of 8.7% for one partition and 4.8% for
two partitions. Figure 2 shows the distribution of planning times for
the 1-partition case, indicating that the 8.7% regression is not an
error.

Table 1: Planning time of Query B
(n: number of partitions)
(milliseconds)
----------------------------------------------------------------
n | Master | v3 | v4 | Master / v3 | Master / v4
----------------------------------------------------------------
1 | 54.926 | 60.178 | 55.275 | 91.3% | 99.4%
2 | 53.853 | 56.554 | 53.519 | 95.2% | 100.6%
4 | 57.115 | 57.829 | 55.648 | 98.8% | 102.6%
8 | 64.208 | 60.945 | 58.025 | 105.4% | 110.7%
16 | 79.818 | 65.526 | 63.365 | 121.8% | 126.0%
32 | 136.981 | 77.813 | 76.526 | 176.0% | 179.0%
64 | 371.991 | 108.058 | 110.202 | 344.2% | 337.6%
128 | 1449.063 | 173.326 | 181.302 | 836.0% | 799.3%
256 | 6245.577 | 333.480 | 354.961 | 1872.8% | 1759.5%
----------------------------------------------------------------

This performance degradation is due to the heavy processing of the
get_ec***_indexes***() functions. These functions are the core part of
the optimization we are working on in this thread, but they are
relatively heavy when the number of partitions is small.

I noticed that these functions were called repeatedly with the same
arguments. During planning Query B with one partition, the
get_ec_source_indexes_strict() function was called 2087 times with
exactly the same parameters. Such repeated calls occurred many times
in a single query.

To address this problem, I introduced a caching mechanism in the v4
patch. This patch caches the Bitmapset once it has been computed.
After that, we only have to read the cached value instead of
performing the same process. Of course, we cannot devote much time to
the caching itself. Hash tables are a simple solution to accomplish
this but are not available under the current case where microsecond
performance degradation is a problem. Therefore, my patch adopts
another approach. I will use the following function as an example to
explain it.

===
Bitmapset *get_ecmember_indexes(PlannerInfo *root,
EquivalenceClass *ec, Relids relids, bool with_children, bool
with_norel_members);
===

My idea is "caching the returned Bitmapset into Relids." If the Relids
has the result Bitmapset, we can access it quickly via the pointer. Of
course, I understand this description is not accurate. Relids is just
an alias of Bitmapset, so we cannot change the layout.

I will describe the precise mechanism. In the v4 patch, I changed the
signature of the get_ecmember_indexes() function as follows.

===
Bitmapset *get_ecmember_indexes(PlannerInfo *root,
EquivalenceClass *ec, Relids relids, bool with_children, bool
with_norel_members, ECIndexCache *cache);
===

ECIndexCache is storage for caching returned values. ECIndexCache has
a one-to-one relationship with Relids. This relationship is achieved
by placing the ECIndexCache just alongside the Relids. For example,
ECIndexCache corresponding to some RelOptInfo's relids exists in the
same RelOptInfo. When calling the get_ecmember_indexes() function with
a RelOptInfo, we pass RelOptInfo->ECIndexCache together. On the other
hand, since Relids appear in various places, it is sometimes difficult
to prepare a corresponding ECIndexCache. In such cases, we give up
caching and pass NULL.

Besides, one ECIndexCache can only map to one EquivalenceClass.
ECIndexCache only caches for the first EquivalenceClass it encounters
and does not cache for another EC.

My method abandons full caching to prevent overhead. However, it
overcame the regression problem for Query B. As can be seen from
Figure 2, the regression with the v4 patch is either non-existent or
negligible. Furthermore, the v4 patch is faster than the v3 patch when
the number of partitions is 32 or less.

In addition to Query B, the results with Query A are shown in Figure
3. I cannot recognize any regression from Figure 3. Please be noted
that these results are done on my machine and may differ in other
environments.

However, when the number of partitions was relatively large, my patch
was slightly slower than the v3 patch. This may be due to too frequent
memory allocation. ECIndexCache is a large struct containing 13
pointers. In the current implementation, ECIndexCache exists within
commonly used structs such as RelOptInfo. Therefore, ECIndexCache is
allocated even if no one uses it. When there were 256 partitions of
Query B, 88509 ECIndexCache instances were allocated, but only 2295
were actually used. This means that 95.4% were wasted. I think
on-demand allocation would solve this problem. Similar problems could
also occur with other workloads, including OLTP. I'm going to try this
approach soon.

I really apologize for not commenting on the rest of your reply. I
will continue to consider them.

--
Best regards,
Yuya Watari

Attachments:

v4-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v4-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From d618d55bc364e9edd90c6cee5a7c96296a48c6cc Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v4 1/2] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |  13 +-
 src/backend/optimizer/path/equivclass.c   | 833 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 877 insertions(+), 285 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170ce..e8996456be 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,19 +7412,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7455,7 +7459,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7470,19 +7476,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7496,8 +7505,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 60610e3a4b..e036420277 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,9 +430,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 75acea149c..23b8f36836 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = list_nth_node(EquivalenceMember, root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5821,7 +5825,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 799bdc91d0..2b04b67b14 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,9 +819,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -729,10 +840,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -764,7 +874,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -794,19 +904,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +983,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +1095,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1063,7 +1182,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1096,7 +1215,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1120,7 +1239,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1145,7 +1264,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,10 +1273,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1172,9 +1291,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1307,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1215,9 +1338,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1227,7 +1350,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1240,7 +1364,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1378,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1290,7 +1418,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1313,11 +1441,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1326,6 +1458,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1346,11 +1479,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1391,9 +1525,9 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1445,7 +1579,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1650,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1559,7 +1693,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1706,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1732,12 +1872,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1893,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1815,9 +1959,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1825,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1835,9 +1983,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1876,7 +2028,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2068,7 +2220,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2087,6 +2240,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2094,6 +2248,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2103,8 +2258,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2275,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2139,9 +2300,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2220,11 +2383,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2251,10 +2415,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2269,7 +2437,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2453,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2332,11 +2502,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2368,21 +2552,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2636,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2507,16 +2703,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2567,7 +2766,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2581,33 +2781,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2653,7 +2841,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2705,7 +2893,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2719,24 +2908,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2745,10 +2929,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2794,7 +2975,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +3038,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +3048,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2879,10 +3062,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2896,14 +3082,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2986,7 +3173,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3000,7 +3187,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3044,7 +3231,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3074,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,12 +3270,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3105,17 +3293,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3199,7 +3385,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3235,3 +3421,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7d176e7b00..65d4ceccfd 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3073,8 +3073,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3091,8 +3091,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3116,9 +3117,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 1fa7fc99b5..d1f68ecf64 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1431,9 +1431,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1488,18 +1492,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1551,7 +1556,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1962,8 +1967,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1983,19 +1989,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd8a3ef7cb..6d30a24990 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index d929ce3417..15050807b0 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41c7066d90..3d322c353f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 043181b586..0ec3736e52 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index edcdd0a360..7fc86aeb0d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -643,6 +649,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -829,6 +838,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 294cfe9c47..bf6389fee4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1285,9 +1312,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d11cdac7f8..f6835f80f5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -161,7 +162,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -187,6 +189,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v4-0002-Implement-ECIndexCache.patchapplication/octet-stream; name=v4-0002-Implement-ECIndexCache.patchDownload
From e9921a25ddbac48887344330edaea792eae7fa56 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 23 Aug 2022 18:16:13 +0900
Subject: [PATCH v4 2/2] Implement ECIndexCache

---
 contrib/postgres_fdw/postgres_fdw.c       |   6 +-
 src/backend/nodes/gen_node_support.pl     |   3 +
 src/backend/optimizer/path/equivclass.c   | 179 ++++++++++++++++++----
 src/backend/optimizer/path/pathkeys.c     |   4 +-
 src/backend/optimizer/util/appendinfo.c   |   3 +
 src/backend/optimizer/util/relnode.c      |   4 +
 src/backend/optimizer/util/restrictinfo.c |   6 +
 src/include/nodes/pathnodes.h             |  58 +++++++
 src/include/optimizer/paths.h             |  24 ++-
 9 files changed, 243 insertions(+), 44 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e8996456be..ec5a98cd87 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7415,7 +7415,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 	Bitmapset *matching_ems;
 	int		i = -1;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false,
+										&rel->ec_index_cache);
 
 	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
@@ -7478,7 +7479,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 		expr_relids = pull_varnos(root, (Node *) expr);
 		matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids,
-												   false, false);
+												   false, false, NULL);
 		/* Locate an EquivalenceClass member matching this expr, if any */
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -7507,7 +7508,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/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b707a09f56..0a27831ed3 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -168,6 +168,9 @@ my %manual_nodetag_number;
 # EquivalenceClasses are never moved, so just shallow-copy the pointer
 push @scalar_types, qw(EquivalenceClass* EquivalenceMember*);
 
+# TODO: This is probably wrong
+push @scalar_types, qw(ECIndexCache);
+
 # This is a struct, so we can copy it by assignment.  Equal support is
 # currently not required.
 push @scalar_types, qw(QualCost);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 2b04b67b14..9b0754b764 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -765,7 +765,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 			continue;
 
 		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
-												   true, true);
+												   true, true, NULL);
 
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -918,7 +918,8 @@ find_ec_member_matching_expr(PlannerInfo *root,
 		expr = ((RelabelType *) expr)->arg;
 
 	expr_relids = pull_varnos(root, (Node *) expr);
-	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids,
+											   true, true, NULL);
 
 	i = -1;
 	while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -1458,7 +1459,6 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
-	bms_free(matching_ems);
 }
 
 /*
@@ -1706,7 +1706,7 @@ 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);
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false, NULL);
 
 	i = -1;
 	while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -1876,7 +1876,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	List	   *result = NIL;
 	int			i;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids, NULL);
 	i = -1;
 	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
@@ -1970,8 +1970,8 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	matches = bms_intersect(get_ec_source_indexes_strict(root, ec, leftem->em_relids, NULL),
+							get_ec_source_indexes_strict(root, ec, rightem->em_relids, NULL));
 	i = -1;
 	while ((i = bms_next_member(matches, i)) >= 0)
 	{
@@ -1983,8 +1983,8 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+	matches = bms_intersect(get_ec_derive_indexes_strict(root, ec, leftem->em_relids, NULL),
+							get_ec_derive_indexes_strict(root, ec, rightem->em_relids, NULL));
 
 	i = -1;
 	while ((i = bms_next_member(matches, i)) >= 0)
@@ -2223,6 +2223,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	Relids		outer_relids,
 				inner_relids,
 				inner_nullable_relids;
+	ECIndexCache *outer_ec_index_cache;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2242,6 +2243,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		inner_datatype = right_type;
 		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
+		outer_ec_index_cache = &rinfo->left_ec_index_cache;
 	}
 	else
 	{
@@ -2250,6 +2252,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		inner_datatype = left_type;
 		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
+		outer_ec_index_cache = &rinfo->right_ec_index_cache;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
 										  rinfo->nullable_relids);
@@ -2277,7 +2280,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		match = false;
 		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
 												   outer_relids,
-												   false, true);
+												   false, true,
+												   outer_ec_index_cache);
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
@@ -2417,7 +2421,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 */
 		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
 												   rinfo->clause_relids, true,
-												   false);
+												   false,
+												   &rinfo->clause_ec_index_cache);
 		match = false;
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -2567,8 +2572,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		if (ec->ec_has_volatile)
 			continue;
 
-		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
-								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		matching_ems = bms_union(get_ecmember_indexes_strict(root, ec, item1_relids, false, true, NULL),
+								 get_ecmember_indexes_strict(root, ec, item2_relids, false, true, NULL));
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
@@ -2643,8 +2648,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
-		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
-								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
+		matching_ems = bms_union(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false, &rel1->ec_index_cache),
+								 get_ecmember_indexes_strict(root, ec, rel2->relids, false, false, &rel2->ec_index_cache));
 
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -2784,7 +2789,8 @@ 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);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids,
+											false, false, NULL);
 
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -2911,7 +2917,8 @@ 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);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids,
+											false, false, NULL);
 
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -3062,7 +3069,8 @@ 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);
+		matching_ems = get_ecmember_indexes(root, cur_ec, rel->relids,
+											true, false, &rel->ec_index_cache);
 		cur_em = NULL;
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -3263,6 +3271,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 {
 	Bitmapset  *matching_ems;
 	Relids		relids;
+	ECIndexCache *ec_index_cache;
 
 	Assert(!eclass->ec_merged);
 
@@ -3285,16 +3294,21 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		Assert(!bms_is_empty(rel->top_parent_relids));
 		relids = rel->top_parent_relids;
+		ec_index_cache = NULL;
 	}
 	else
+	{
 		relids = rel->relids;
+		ec_index_cache = &rel->ec_index_cache;
+	}
 
 	/* If rel already includes all members of eclass, no point in searching */
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
 	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	matching_ems = get_ecmember_indexes(root, eclass, relids,
+										false, false, ec_index_cache);
 
 	/*
 	 * If there are any other non-child members besides matching_ems then we
@@ -3422,6 +3436,51 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	return bms_int_members(rel1ecs, rel2ecs);
 }
 
+/*
+ * Helper macros for caching the result of get_ec**_indexes**().
+ */
+
+/* Call this at the entry of functions */
+#define RETURN_CACHE_IF_AVAILABLE(eclass, cache, field)					\
+	do																	\
+	{																	\
+		if ((cache) != NULL)											\
+		{																\
+			/* The EC that the cache is pointing to */					\
+			EquivalenceClass   *pointing_ec;							\
+			Bitmapset		   *cached_bitmapset;						\
+																		\
+			pointing_ec = (cache)->ec;									\
+																		\
+			if (pointing_ec == NULL)									\
+			{															\
+				/* This is the first time of using this cache. */		\
+				/* We can use it. */									\
+				(cache)->ec = (eclass);									\
+			}															\
+			else if (pointing_ec != (eclass))							\
+			{															\
+				/* This cache is not available */						\
+				/* because it is pointing to another EC. */				\
+				(cache) = NULL;	/* Tell that to outside of this macro */\
+				break;													\
+			}															\
+																		\
+			/* Here, this cache is available */							\
+			cached_bitmapset = (cache)->field;							\
+			if (cached_bitmapset != NULL)								\
+				return cached_bitmapset;								\
+		}																\
+	} while (false)
+
+/* Call this at the end of functions */
+#define STORE_RESULT_TO_CACHE(value, cache, field)						\
+	do																	\
+	{																	\
+		if ((cache) != NULL)											\
+			(cache)->field = (value);									\
+	} while (false)
+
 /*
  * get_ecmember_indexes
  *		Returns a Bitmapset with indexes into root->eq_members for all
@@ -3434,12 +3493,20 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
  */
 Bitmapset *
 get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
-					 bool with_children, bool with_norel_members)
+					 bool with_children, bool with_norel_members,
+					 ECIndexCache *cache)
 {
 	Bitmapset  *matching_ems;
-	Bitmapset  *rel_ems = NULL;
+	Bitmapset  *rel_ems;
 	int			i;
 
+	RETURN_CACHE_IF_AVAILABLE(
+		ec,
+		cache,
+		ecmember_indexes[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
+	rel_ems = NULL;
+
 	if (!with_children)
 		matching_ems = bms_copy(ec->ec_nonchild_indexes);
 	else
@@ -3471,6 +3538,11 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 	if (with_norel_members)
 		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
 
+	STORE_RESULT_TO_CACHE(
+		matching_ems,
+		cache,
+		ecmember_indexes[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
 	return matching_ems;
 }
 
@@ -3485,11 +3557,17 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 Bitmapset *
 get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 							Relids relids, bool with_children,
-							bool with_norel_members)
+							bool with_norel_members,
+							ECIndexCache *cache)
 {
 	Bitmapset  *matching_ems;
 	int			i;
 
+	RETURN_CACHE_IF_AVAILABLE(
+		ec,
+		cache,
+		ecmember_indexes_strict[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
 	if (!with_children)
 		matching_ems = bms_copy(ec->ec_nonchild_indexes);
 	else
@@ -3518,6 +3596,11 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	if (with_norel_members)
 		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
 
+	STORE_RESULT_TO_CACHE(
+		matching_ems,
+		cache,
+		ecmember_indexes_strict[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
 	return matching_ems;
 }
 
@@ -3530,10 +3613,15 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_es = NULL;
-	int			i = bms_next_member(relids, -1);
+	Bitmapset  *matching_es;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, ec_source_indexes);
+
+	matching_es = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3563,6 +3651,8 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 	/* leave only the sources belonging to this EquivalenceClass */
 	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_es, cache, ec_source_indexes);
+
 	return matching_es;
 }
 
@@ -3575,10 +3665,15 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_es = NULL;
-	int			i = bms_next_member(relids, -1);
+	Bitmapset  *matching_es;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, ec_source_indexes_strict);
+
+	matching_es = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3608,6 +3703,8 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
 	/* leave only the sources belonging to this EquivalenceClass */
 	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_es, cache, ec_source_indexes_strict);
+
 	return matching_es;
 }
 
@@ -3620,10 +3717,15 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_eds = NULL;
-	int			i = bms_next_member(relids, -1);
+	Bitmapset  *matching_eds;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, ec_derive_indexes);
+
+	matching_eds = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3653,6 +3755,8 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 	/* leave only the derives belonging to this EquivalenceClass */
 	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_eds, cache, ec_derive_indexes);
+
 	return matching_eds;
 }
 
@@ -3665,10 +3769,15 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_eds = NULL;
-	int			i = bms_next_member(relids, -1);
+	Bitmapset  *matching_eds;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, ec_derive_indexes_strict);
+
+	matching_eds = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3698,5 +3807,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
 	/* leave only the derives belonging to this EquivalenceClass */
 	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_eds, cache, ec_derive_indexes_strict);
+
 	return matching_eds;
 }
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index d1f68ecf64..0604bf2699 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1989,7 +1989,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids,
+											false, false,
+											&joinrel->ec_index_cache);
 		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
 		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 62cccf9d87..d1a549d377 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -454,6 +454,9 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->outer_selec = -1;
 		newinfo->left_em = NULL;
 		newinfo->right_em = NULL;
+		InitECIndexCache(&newinfo->left_ec_index_cache);
+		InitECIndexCache(&newinfo->right_ec_index_cache);
+		InitECIndexCache(&newinfo->clause_ec_index_cache);
 		newinfo->scansel_cache = NIL;
 		newinfo->left_bucketsize = -1;
 		newinfo->right_bucketsize = -1;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 7fc86aeb0d..a4b2f8fa17 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -237,6 +237,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->eclass_member_indexes = NULL;
 	rel->eclass_source_indexes = NULL;
 	rel->eclass_derive_indexes = NULL;
+	InitECIndexCache(&rel->ec_index_cache);
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -652,6 +653,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->eclass_member_indexes = NULL;
 	joinrel->eclass_source_indexes = NULL;
 	joinrel->eclass_derive_indexes = NULL;
+	InitECIndexCache(&joinrel->ec_index_cache);
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -841,6 +843,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->eclass_member_indexes = NULL;
 	joinrel->eclass_source_indexes = NULL;
 	joinrel->eclass_derive_indexes = NULL;
+	InitECIndexCache(&joinrel->ec_index_cache);
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
@@ -1268,6 +1271,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	InitECIndexCache(&upperrel->ec_index_cache);
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index ef8df3d098..3eb1fd81c2 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -206,6 +206,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->right_ec = NULL;
 	restrictinfo->left_em = NULL;
 	restrictinfo->right_em = NULL;
+	InitECIndexCache(&restrictinfo->left_ec_index_cache);
+	InitECIndexCache(&restrictinfo->right_ec_index_cache);
+	InitECIndexCache(&restrictinfo->clause_ec_index_cache);
 	restrictinfo->scansel_cache = NIL;
 
 	restrictinfo->outer_is_left = false;
@@ -360,6 +363,9 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_ec = rinfo->left_ec;
 	result->left_em = rinfo->right_em;
 	result->right_em = rinfo->left_em;
+	InitECIndexCache(&result->left_ec_index_cache);
+	InitECIndexCache(&result->right_ec_index_cache);
+	InitECIndexCache(&result->clause_ec_index_cache);
 	result->scansel_cache = NIL;	/* not worth updating this */
 	if (rinfo->hashjoinoperator == clause->opno)
 		result->hashjoinoperator = comm_op;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index bf6389fee4..34c1ea03e8 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -556,6 +556,49 @@ typedef struct PartitionSchemeData
 
 typedef struct PartitionSchemeData *PartitionScheme;
 
+/*
+ * ECIndexCache
+ *
+ * Storage where we cache the result of get_ecmember_indexes and other similar
+ * functions. See comments above the function for details.
+ *
+ * TODO: Is this good place of this struct?
+ */
+
+#define GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members) \
+	((with_children) << 1 | (with_norel_members))
+
+struct EquivalenceClass;
+
+typedef struct ECIndexCache
+{
+	struct EquivalenceClass *ec;
+	Bitmapset  *ecmember_indexes[4];
+	Bitmapset  *ecmember_indexes_strict[4];
+	Bitmapset  *ec_source_indexes;
+	Bitmapset  *ec_source_indexes_strict;
+	Bitmapset  *ec_derive_indexes;
+	Bitmapset  *ec_derive_indexes_strict;
+} ECIndexCache;
+
+static inline void InitECIndexCache(ECIndexCache *cache)
+{
+	/* TODO */
+	cache->ec = NULL;
+	cache->ecmember_indexes[0] = NULL;
+	cache->ecmember_indexes[1] = NULL;
+	cache->ecmember_indexes[2] = NULL;
+	cache->ecmember_indexes[3] = NULL;
+	cache->ecmember_indexes_strict[0] = NULL;
+	cache->ecmember_indexes_strict[1] = NULL;
+	cache->ecmember_indexes_strict[2] = NULL;
+	cache->ecmember_indexes_strict[3] = NULL;
+	cache->ec_source_indexes = NULL;
+	cache->ec_source_indexes_strict = NULL;
+	cache->ec_derive_indexes = NULL;
+	cache->ec_derive_indexes_strict = NULL;
+}
+
 /*----------
  * RelOptInfo
  *		Per-relation information for planning/optimization
@@ -916,6 +959,12 @@ typedef struct RelOptInfo
 	 */
 	Bitmapset  *eclass_derive_indexes;
 
+	/*
+	 * ECIndexCache corresponding to this rel's relids.
+	 * TODO: The following usage of pg_node_attr is wrong.
+	 */
+	ECIndexCache	ec_index_cache pg_node_attr(equal_ignore, read_write_ignore);
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -2557,6 +2606,15 @@ typedef struct RestrictInfo
 	/* EquivalenceMember for righthand */
 	EquivalenceMember *right_em pg_node_attr(equal_ignore);
 
+	/*
+	 * ECIndexCaches corresponding to this rinfo's left_relids, right_relids,
+	 * and clause_relids.
+	 * TODO: The following usage of pg_node_attr is wrong.
+	 */
+	ECIndexCache left_ec_index_cache pg_node_attr(equal_ignore, read_write_ignore);
+	ECIndexCache right_ec_index_cache pg_node_attr(equal_ignore, read_write_ignore);
+	ECIndexCache clause_ec_index_cache pg_node_attr(equal_ignore, read_write_ignore);
+
 	/*
 	 * List of MergeScanSelCache structs.  Those aren't Nodes, so hard to
 	 * copy; instead replace with NIL.  That has the effect that copying will
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index f6835f80f5..0051cf9dcf 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -189,28 +189,40 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+/*
+ * TODO: Callers of the following function must not modify the returned value
+ * because they are cached. I think we should mark them 'const Bitmapset *'.
+ * Currently, I have not do that because we have to change the declarations of
+ * 'macthing_ems', where we store the returned Bitmapset.
+ */
 extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
 										   EquivalenceClass *ec,
 										   Relids relids,
 										   bool with_children,
-										   bool with_norel_members);
+										   bool with_norel_members,
+										   ECIndexCache *cache);
 extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  EquivalenceClass *ec,
 											  Relids relids,
 											  bool with_children,
-											  bool with_norel_members);
+											  bool with_norel_members,
+											  ECIndexCache *cache);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
+										Relids relids,
+										ECIndexCache *cache);
 extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
 											   EquivalenceClass *ec,
-											   Relids relids);
+											   Relids relids,
+											   ECIndexCache *cache);
 extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
+										Relids relids,
+										ECIndexCache *cache);
 extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
+										Relids relids,
+										ECIndexCache *cache);
 
 /*
  * pathkeys.c
-- 
2.35.3.windows.1

figure-1.pngimage/png; name=figure-1.pngDownload
�PNG


IHDR�~����sRGB���gAMA���a	pHYs���+��IDATx^���\Wa/�����\d�]�ecc\�%�IH�#�Pv�)��"%��1&��#�Q�
!	!�ZD`� n�K�%���rW��3:c�������3���]����w��3������������?dFH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	�������2�������+W������K
���������/��$�������}�����5����{�PS�X�xq�e�t $�j�g`` �*���|��ot��
����/K�.���e��ea��E������V�c5�~�5���Ss�����3�0�r?t�8h��I�\�;o�@/��s���*|�hGx�����x��g����]���9-���3����Z����k���g:IH�\(v��@zQ�-�����7�K�t��kJ;�)��}�@�C+�c:EH�\���:P�^�wq�7��d�R<��{�u�R��['_SJ�*@>9��!r)v��U���+.�t�x�����T��������&�|����
YR �����
�G��S������F)���x_S���x�g�-�*�_�*dIH�\�A� @��\�2�����9�l��0::V�X������ $�:+L���722R�>��x?�e�e[e��7A�"���6�����e �l@;��#�-J���[Y��4x������Oj����-~Vk�sZ�mf���)����J��je��x�B�&�@��� �!�v��[��n�B+�g���/����=�p����f*�>����o���Z��f�;�x��-������=��.]�J�B*t���;P��<mu�{�j.\�J�+�>��I.~�����T;?��}7#�VA�.���eF$�MH���lPE�)0^���K�������S�����JP�>����r��T��R b�i#fSz���P�e~��N�A���P3��;-Y�$��kvi1h��
��l��T��T�@/���J�ffT��b������Y��T�����
��h��T�?q�F&"�5��O3K��p��T�n��U��#�@�4����S�W5�E��Y����l�>�����y�R	�%�@�4��_���1��//|��
T��?����A�x�J�G��^���t��z��%/���m�[�~��s�K=����x���v]�X����_�����x����Z�G���1iU|l����n�u���\���+}��u���nz/,���n+�NF��e�����������t��it��%k��{����)��x{�]O�[�W�n[����F�{�-��~����_�Z�����F�m�y��m�w�;u��U�-����v�����6����cm<�K'�{�m�~u�vvJ|n'�1�u��&�y�v[J��z�������~����~��U����n�����V��c������W����x>7z|�e�T����8=�Y?&E���?��@��a��7I/^�pZ�f�}-X�����N~s����E��nw����������<)='�X�9���X���c���/�6fy<������*����<P<��s�1i�F�pp���N�hfF����<)�'cy]+��H����_��Q<���3���f�\�2�*�u��N��"�@�t����A�,;�c���`];�K�vwb�$���]�p��m�������,����v=�q_���6>����v�q_�����9����:�?���.U|L�U������-[�J����?�<i��ZQ�W<�:���3������,_����nX.k"Y��NR�6���Y�����h��s�v����n�<��~�zE|��`-!����m;��X)��,��(�7o�u|����&>&�Jkt�u��z�Y�<V�Y��I']��}�3�4:6�
K���g#��n��	�|R��L�7�������*DP��c����.y������f�=�.]�J�����Z�������|�����k}�e}~���c�.�B��:����v?=�Q���x���2����*���w�f���Om�\���7��Z!�@�t�t���3>v���������-����N�v�6<)�����m��=L^ow����7;0W�<)����z���N���|k��������V|��V<��Q �{}�v��iF��cot�J��Y�m��s��mm��,>&����;*>o��G��k�cgk�����S���[�����F�d?-/�������u��x>����d��T����yq��~_��Qx�����?p��e�����������t���v;J�fT�����}����->�c���/n���f�[�����x[�yL������j�(n�<wy��Q��=^��������;k�n��C5��W��>�����>w�.����u��q����-^��f�{�5�X�������$ncUm_�����	����fd�����q]�w�[�����j�S����s<n�<���'cy|����[���o[��+W���������f���4z���X�ktu�k"�g&r#�o��G�odF###c��a�����d�-�xV�X�j�����e���y�����v�K7��F����}������Vn/�k��+>w�T{mm����k��9��$�y*��x_����}V�"�G3���������s���,4z���9�Jg����}��<i��`��y������W���x����m\Ve�jmE�^������K���b``��c��V^��B*�B�a��hZ��a?�N���Y�6�8`2�����IVK7��v�K���F������E3��l���k��Vn,�������Vn�y��~��F�K����[�h�����Q��z��Q�=&�l��C/i�y��I���F�a<�y����Z�Q��]��z���]+�������->1���k8�"�@.4�D��m�n���a�O���,��S���7�����v���������q~��X��V^cHa�����Z������x*E��U�lnE���v
n���F�x�<&Y��N/��x�4�������{a�
Y<��}�k���
b���a��8�P�x��V��y\�ul@5B*t�����T��#u����h48���L��v<4��z����n�n�����F���2�21�{�5����}<�po��B�c���Aq?��n	�5�l��`3��^ozU�����h��v�'��{y��:����v}�����f?�������l@��T�Z1������\�gQ�7��E`��t����{������w�<iW�����xk�8k�7��
�7�|U���v�5zl�	of���q��K5z��}.��������A�z�a���X/��e�����B�����`��c���x��S��<	���b�y�D�[|��R7�m�,:���.���q�q+vtg1mx'�_^;��}����s���I�_�]���v���X�7kH����c���cV
g���q7?&P���>Y�T�u��T�^�����i��`��3{�����|Z��L@��0fq��������G�[+Mq�j������q���q��ctt4��>lg�}���v���v�K���FY�'��AN{��xkt�uK����Y2�N�����D��;��������h6�,�s����~�m4�xf���wE�?�g!>^�5>~i��3�@)!r����*���>��m�"��
P���F�Y2�w�9h��u�W��!���#�,-Wo���������^��d��:�-A�n���%��^^��$�@n�o]f58��D�y=��N<�Y�����n�����?Q��F��Y�,��BdY�'�����c���e��K��_�<'�@�	��;q�*.���q !t��s8���>�����Y8�m9+M�{s�p\^���e?�'��3l�?�pM���<��<�o��n1�����9���z�*���
�;Rc���+RKo)
��m``�0�v��h�������n�n��������c�:�w���D�8P/3Q���X���`y0�KO5z���9Yk����O���-t��Sq�[5�x��NB*t��iZ��`�H�rp��A��0Jy e��&r�6�_����Ylc]�"K����ly���>�T�v��s�R��v.�S/(�d��T"�mU�4��~{� B*t���]�bui0%O�q�*
�
�Aaz��X��=���~��4�]!�x�Z������J�1i&��t��T��R`�bGf�:����SNC)y
��N�b(�4�@�t��E��N���A�����o"�"j�Qx����zA
a����_o6�(���R�f�@��pJ�)E(h�,�W�Z�PA���h��?�>��5�)��it^vs�	�|R�q���3��;�;��V\��?�f~��
f���r�B$�f��7��PTs������
�C�(����C1�R��.��y���Ru��.yQo6�F��z�(�������	���5�RF)R����)�c ������??�zY3�s��%&J���3O1�<Q[��������,���f�����AH����J��=���b�#�������h���b"��nUo�����z3KD���O�eW�F������d|}>���k�����e��~B*0CCC�T[�/
��[�u^���W��y\���m���W�.���Y�^,�� �c�h�!T�9��)���F��Y�����v��_v�9��Ya``�03�X��h��]�n�	D��X�������
��������?����vL��{��9z}9�FK�4�a3��u���g�k%��h�n~�:q^7���?��i�F��F�;@=B*��F�Y�YR���7��h�r����>����f?��h`2�����~��^���p����:�X�a�8�J����%�,Y�J���s�s]sZY�����^?� o�T����N���O�5���[����(��e(#��n���6��04�� �����~��?>��fS�!�v�X�fV�m��[1���Ki�#|���e���s�D�^7�qltR�s��1���u�����9N<����
����EV���c�:������KS�=���h<�*�Q<�K�8(���e�f��r`?/�	��w��f�W�y��S>���u�eI��>_�kg��Q����U���Z�u#n�<��"~���8�z���gX�B��h�:�]B*���L0m<����R8k�]�C/P�Q(#���F������h�7���n�<�����F��E�yZ	^�����,��^j�{I�@QV�[�5��,f��R����:Y������h����1$��x	�@5���'�
�5���f��,�=(j4 ����8H�����5��h�7��N������T�}��o���A���y�3�����@I<V��>�^����2�J��l������F��,��FA�,BBY��86z�����H�xl�����%�m���,�����7��{������\Y-��5�h��u��K<�&z��F�v;�;�<��xt��%KR��x\���|.��g1���Y{���c�����������SM�O���XCB���X�~���y�ql&�������
��Q�l�����%���e9����J72p�?�^�������f^�'r��Nw�x��YT�b����_'�Y:��x=��nef�j���'H����a���Y}�jf��v��y?���"���z�������c'�>4�y���?!hQ�%��
���@j<��R��7��k@��~w����y�19�`F3���6t��!�w��x43p�M�G��T).���|F��U�Y�U�
�D��~<V�r������{`��{��f9�M��r��9���x��-���.;��H_��{.@$�-�r���al��^��f��ySo5����~8G�
���8�.���V��e�(���:�	��[9v��G3�yxo����Jp$>���6>�^k�_{�s�L�T;����V>������V�[9O�x�f��^4�������!�vl�x�[����x���vR�1hv�������O�8n���h�����)�7���� j��Jl�7�����A�����5$n�<�A%���@@|��������@�k�l3�c��"k�����������{;4{\���G�km���t+}�i�9)W|������}*k�[l+��x����s���FZ9O�o{�f�C�x��.hu������P7$�?!�f�=*�@B���>�-����O���2p��JP�Uy@iv`=����w�AK:��@@|���{��@qk���T72�=�����c	��+���n��?�������X<�J����~e�^��{`�m/?W��Ey{o�'��>~�^��z��{���e����Oqk$v7;g��<he��Yy@���j��Tb��#��<2�8�k���1�c2::��Y@�!~�����~V��1��,�����I^��i��w����_K<�dAH�(�A���Bq �����k�"��:w�<�2��������s��\��������1Q�lo�86K��j�R��dq�u����?� Q�?y}o�������}���g<,�@V�T`��,�T\�7���2	0���3�o���$�~�P���Z�re*1��s���s�g<����&��h��bT|_���1Q��f�E���,��c���,_�<���x��!��N[Y�J��^���v����D��c��x|z� KB*0N�*X��1<o��T���?�M������#�������}���s}��L��q\�nV� a^���x�<�u�E�g��x�C�kq�sS��f���sX%^W��c,�m����v>7������E�>������R<^�V|���p
�2����T� E�K���7��o��=cg�a�x��o�����jk�n1�f�C|>Kg���^P���?���wT��p�g�h��U�=Q�����@���Rq�|��y��W3���5�Pz�F�#��PF��*>6:�'�@Oh4 ��_����zK
���������4�
t?������8SJq�r�A��*��
���
=���V5�G�^\�'���*��k�`�*.V��y�!2100P��?*�-.����\�2�*��>���a���Z���Y�6�
����������]��������/���>�W���8�J���3��q��Q����
�W��y��@���q�V�J��X�xq*U������R?���/B*d".�SK��k6����T��7� ��,*�����3AT�!21o��T�n����4v1�Rk�*��W�a
�*�fK[�dI*y10��T��H��FFF����S�9q�*N�_/��e�@g���j-�n�}>QW�X�j@^�������R���<Pq��j��8`5N�^@�j�3B5���OB*d���<YP��6����y�/!2�����+��f�,�}�mR�2��r>Y��a�
z��%KR	�#!:b��Eadd��
�v�����������s����S
�#!:&,��N� S;fV)�S�����~/��7�����T��������U�
��+W����B�\���.4c
���g�j���}�{>�!2g�2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@���Fw���h��#���I��	��9!2'�@���Fw_4:Z��bw��:�2Q�0����I��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���J[�|yh������_^�:������z��o�R�����0V�I�V�o�;�/�h���������,[�,,Z�(�*5{]��SM^�
�����:�2Q�K~���
�����b��B��I���B��B��7i������Z��P^�
P�4�2V�I�vfR�c��c��U����+W����T����j,###a����V�[8�|�&��`b�I�nT~|�:��~�M��!ZV~ �;^�/[��QT����q��}0��u�t�o���^�[�[X�t�N_�Z
��'��T���-)��5vJ��AS�VF���U�����o��b��<��*�$�E��T|��7����0��l-OE5s\�Li�L������x�������&�z�N1�Q�<N�VfR�O@7��b&�Rm��z�x�R1��������m��}����IWP�3��`J����'B*4%��\�|��r�V�J�����J����u��_���8�H�%t�O@���P��L���������v��\�2�j���������>��O@7R���o�,Y�$�jO�C��m��o�(�S�3{J)�$���
uU[{�����W��t?Ey�7@��)E�I�!�*�E����J	�P�XgQ('�BMK�.M�:5����S����o�v�O@�	�PU�E�|�a��c5��6�������&���������S-�����B*���!\V�X�j��_w����}w��~���s��6���o��N�
7Q�K~

����f�%����~`����W�/�L*TU��ur��+W�R��u��p�����7���7��W�����SOL�J?�~j����PIM��T���)�l��T"�������:_�o��Z�^T-�
���O?���M����>6����J�kZ_I*"oRp��|:�V����2=mQ��F��c^�=�b�h����r|>�L�R(�g��u/t���5+L�$�
����c��������S���������&�z����K�'�$Pi���a��m����3����e�W6lx��b���+�y������������B���)��hF��cH=y�7@��'��	���v-��p��TjM��W�O^�
�n�I�!��7o^*�f��E��C\Bh,�]^�
�n�I�!v�j��Tj��+W�R}K�.M��Yj(��h7�$t3!v�x��T�a<��K�u���z-^�^q�����o�v�O@^�P���`*�M���,������K��
6��h7�$���
/(�`X�pa*�]��ebGE����:c��#�^�Y\��o�v�O@<���(�ht���>;J;bGB��e�bhhh�i^�3��X�"�����'B|~�n�Z(��)S��@�Y�n]��}{�<k��0i�L*�*�;���k��R�g�N%�j` v���}!u��Gi_�X���4O?	TZ�~}��m[�<s��0y��B���
^x��1c��
x��v����j}!z���]�����o��/�JF^�
�n�I�vB*�`��U��~qF�����I�IS.�����i����������,�}�4��?,���������e�@?	T�����&P�y;�_���~�B*dNH��	��9!2'�@��T���
�R sB*dNH��	��9!2'�@��T���
�R sB*dNH��	�@H%�����s�T���
�R sB*�'FGGS	���'�B*dNH��	��9!2'�}htt4���~��R sB*dNH��	��9!2'�}b`` ���~��!�@��T���
�R sB*dNH��	��9!2'�@��T���
�R��^�@*Q��
����������j�#�07��\��K�����h�]���?�j4CH����#�2::�Zh��
@����D��e���m*�Rh��{��_|sx�����V�4p�O�.Y��%��*!�:���p�����G�M-���
@
�<�\���5���N-���
@���Z�Ae��O�����9 ��GH���[��.Yn����R�)���3'��GH�������/�n��xj�n�������LH-�#�P��K��kn��j�y���������MN-4"��|�+k�On~$��;l�����}����A�B*����_F�8���~��������2�J��T����������T����v
�3'����X�}�3��>|�6�Zu���K���9��C�L-�JH�[������?�?���c���Tf�wja,�T��t�����{R��)S&��9!�x�>���R��W/�+����N��b@�����j���
�W�i���+�w�Zm���0��R��R��������w�Zm����0��S�vR���V�>����Zm��������T�]�T��w���O���T����23�qH��NB*@O��O��[S�����c�����T���T����}um����?L��WMO5� ���+�<>��5�V���G�����T#+B*@����M���W�Zm���#���:2����
�Sn�}s����a����R�[~vz8�
G�YRz��w?Q�A��g����~����{�tl��	B*@O�������	����Z�{��L����F���w�C�fPy��gSKu�;���o��jt��
�kmz.\p��p������^u��a���K5:MH��MOl	�_|s����RKu�<qj���OH5&��
�KO?�-\p��p��O���N�����9��DRrg������W��n<�Tw���
������Rr���	����j��p����sO��*�
<@�|�����5��Zu3��U�Ae�=��&��
���/�<,_�<��


��#�}4����~t�#�V����.<wN������n �= HbXd��a�����E�����*K�$�NV�h�_������>�j�M;`��*����
�\��I�4#Jb��Y����)���o�f}�����S�����>r���L*t!��P)�l��022FGG[��������0I���_J�[�w�xy3�����w[����O����cr������{����
�Xy�$Qb�d��Ea����5�+V����J=������Fq���T�3�4��o������Zu��2����	G��Z�FB*�S�f9Y�dIE��TyPe����T�|6�j���^T1�
0^_�3|����Zu�o1�r���v4���T�
��i �V�Z�J;�ew�T�.\�J;fS��|6�F��?/
��a6�$�������W/�+�j���9����O5���
����+Si���N3�/Wm���e�b����l*�X|���K�sG�������<qj����T �����H��'�a��|��y���R}�ej�?�/|����Zm�������j���
����I��	�4��P5�0�|�6����
�V��uVx����y!�}btt4�O�d<��N�O���W?>��u�V����������j���
����S.���T����Y�������vk����_8&����R��R�[�n}6��ss��X �J�#� @^���G��eM��v��
oy���F	����wl�|����O\������K�
������c=�������gC����d�����.Y�j����G�����T#��Tz�%��3����>|��w���>�Zw������������K�.M��,X�J�(�]�\�~S8���at45�����=tT��g�oO�h�#������m��B9vDM�<�Pz��u����;�p�5kV�4I&z���jn�����o�^X��U'�Ox���	'�oj�[�]�6�B�={v*1V�;dg�������


���O\h���V���E��`������1�h���/_/^�j�w����L�>���	���<uN����s�v���-����)<�l�e�_s���W��;��G�9����j�z�z���{W����aL�(�^����c��%��C�� J���K��
�����l��v�U�����[�j?���m+U��6[+��o
�[�8�2���-�������!�gPi��=q?q�C��$�R*Q�L&�[k�-��Z���Q���V�\�J;�����������Py����_{�>�F��s�TD|�~��X����?�8�)��O_?�T
��:�K�|�'��r?�Um��N��L���?�L)K�.m8S��e���E�.�Sz����O��-�v�������~a��
/�3f�p^��n����u��B��c�
S�L)���9/h���>����p�O���^}�A��wvw�����J�kj��� ���'�
�Dq���o���v��@g��E��J����1�*����^;Mm6����~n���V���m�~�JU����h{��-��K�4���������������R���wl_��]�6Ne_r��w����!�V����n�=Y�J��iHi4;K����K������*�}2�Tw�	��sB����Tr�'7?�J��������4H�p��TzQ��f�e�J�u?@oxv��p��7�5w��2�)��
��<!����C$?�)��<H2o��TzQ��@��/O��T�7����\�:����
5���}����	�La�u�a����u{X�`�������m{��mb�dhh��5��.]�J;�.TK��@��e�@o:�K����<�j�w����s��=w��Z�eB*9���-���G;t=�M��=��v�-���l��T�T���;�3��ke�@o��Koi�T����,�������z��
@N=���T�V��0�.�R288�j!,^��n�$�l����C��>��������R?����kk��{(���~����sN��kj�����t�x�d�n.L�bP�|F� �m�!����T��|6�����w�=K�@�������]�`�Uw�~�fP9��=R�BH ����Qxd` v��zh�8�I�l*Q�Qe�����$��T�4"i��j����7C��>����w�gc�U���S
�c�3��O�Trj�)����{�Z���B(�?^�m��3��$�T�Z��\�<�_>���o���T�n��&����K��#�N-�=�9����R6��?�g.-�D�g>�b{���X��iv�fP��s�w����{R��������N<f��B?R����s@*���s������w�d/�Db%JJ��>�pJ�����W��������j�}��9�����j�+!��s�>���;2�������j��	G�f�}�������3�j��='��m���$��s������$q:�qU����_���F�
_����V���cv����F�R��{�1�����SG��V��o�dc�����j�����O=8�@H�'�x���g>�=�J�O�~����eW>>���R����_3���CR
vR�o�����;��R�@�h��U�?����
��r�}�C���xk���[�xl��y���HH���x��������3�T�)����fP����p�����j����o~��T��	���u�<��uS����Rc����
�x��pJ����kW�y4|��5�V���G�_}m���R�q=��B�c�I��zZ!���%���\����?<%�g���2?P���6��/^�j����������4�	�@��UZrk��������Vih��p�A�)��-�2����'��n�cs�������z���N��s����W�_�1<���T�48wZ*@c���D8�K7���������C���tl�A}B*=��R?�;e�~a���R
��c�S��K��MOnM-���{��L5hLH �n�}sa��Zbg4����)T|���R��S��Y��R��z����.����jP�C���_�:�������'���S
�'��cqM��+6�Z���8$������.�du����RKu?3��p��OH5h��
�����T����bc��}4�*
�@�$�����V�Ae�]O���N�����9��R��zK��o�y������.�dM����SKu/=v�p�9s��IB����
@N������{�L�J�s��TgP��L�����sN��*b��8�rj��,*�N�=,8��T�J~yM�b���V����YX�g��vI-0vB*T��������k��nCCC�-�|,��7��G^�
c���-�{W?�j��:��T�J}��p�
�ZuG�Ga����5�������X�`AX�xqjy���pa�?��kEs��ol�����>��o�����	�P�_������>�j���n���a��Z`��T(�����E^4#^.^�1�R-�QM�\+����k����T�U'��[���>���u���������Q��H-�B*,]�4�v###att��m��e��R��1�R|���R����L��}�x���G�]>�j��NK%x�����o���T�n��'��9!�:|���#�B!XQ��A�+V������-ZTh/
������Z�R9�b���~c{�ff0���`��-�s�!{��sH5��o�}{����j��2e�0�����I-�^B*}.LJ���J=K�,I��ga)*�U�Z��(��>��J��}�x����a���S���YT(s�����V��j�}�������/����T���U�Ri�F�(3�����.\X(�+�U�V���t�Q�YI��o�z��Dg�qH*@�����w���j�]x��p����dCH���*�-[�J��0���h����M��%���wy����$y�7���S���N;8���.�@��������S��%g��tj�Av�T�Xy c��y�4~�3�4��j��ry�7���{(<����V�,*�������y[������?���R
�%����������{����T[^�
��������
/?�t�����������T����2+��t_|�s�T�*����xajz���4S��������O�+V?�j��NK%���k���u�V�������W�S���T�Xi �����HY�`AX�xq��hxx���(�/[4��F�~���o�����2e�$K�F�8|����Zm�����_�X�A����P���f��J�Y��zK��~H�s���@?������|eM�������[_sx�Ag	�����>b�4��l��022FGG_�b[��;�*���^�`x��-�V�,*��������ts��������#S
:OH�
1��h��0����Cl�?+�*q�vZ�pa*�_^�
@�bc*Uz�1�6����=.�dM�>�j��W�y���CH���Jy8�T�Y��*K�.M%��n���p��M�Vih��T���r��p����3�mK-����������L�����<U\���70���5���e~J�*���}�+V�Zc��/�/N����D�3�l���P>�����I2j��J_��������~����7>�j;�c�I���9(L������}�����XU{����pu�@��~����)S������_�m�1�5s��0y��B���
^x��1c��
x�����v�S�O�pSx���RKuo|����_��j����kSIM-��B�R���R�)_���K��\�2��/��n��	j��zw+U��6��w�R�~n���u�h��-��g����[���[@��|:���
*�;�`���J�3���7o^*�n��U����oy&<��v@�����J��}6\p��p�#����~�����������V����-��P�d���Haz�f��S,X�j;faY�hQ����V��i��P^�=�J��=��3M�=,N��}��B9N�ky/�]�������V��9�v������{b���n���T2}l;X���T�����&P�y�{bK�������dj���'N
9wN����4f�&Dip���/���\^�=�����f���T���l���JU��-����o�P��:����g��
��g���/^�0�r����9!����������Y��|���}7+~��T���u����+H�J��Kx�i���n���p�%��~�):y����sO�����J+]�'Z�re*5�(�R������Rk��/Q^�
�z�����:!��3������o����j��9z�p�9s��S��^��>W�,M+��,^�8�v�k���lf�����C��Fy�7�*����Z������K���ny,���9}�B@e�=���NB*}�|Y����T��|v�e�����J�c�|I�r���A�Z����ohF��~^�����C�H5z��Ko	�n|8��;j����s����n'�������a�zK��@F�,*��(*o_�`A��Gl�?/Uo����������{�L�JfQ��������J����[�����vK-���T###��C��UJ�*1�������-W>�H��f����9y�7�3|��T�4m�na�������y]���j������%~�8�������[�B�����>�7U���Pi&�/���4288V�X�j��u�!>�[�n-���6e����W�[�.l���P�5kV�4I&z����<�����_�j��=tTx���L5����kS)���g�c50�Cv������^��~��~���m��By���a����2��
6��^1c���<�E����m�~�}�V�>{N	��'����;��i�k�������x���E3�
�D1�Q>3I-�r�=��o���+�;���-������7������**���
/���b0#�U�+����l@�h��E��+��\l���x�V�u��Zb
��W/������0��~���W�����I��&M�;'�4c���������il��N����e��������
����V�����0w��F�1}l{Y��O�,�;��	Tr^d�����BH��?��9�/��jL$�5�Y��K
_�1�*5m�gOi&�r��OP!��T&��G�
���p�U:��CR	�^��#��/����V���cvXp����%�}���kE�YT��3����I������o���T���v\x������
����R���O?$���.�@/�������u�V���<#�5�.�CH`������>�j�,��[~p�Ca����Zm���c���<,��7�L��:��w����Y��y���	K���T���7������!�	r�����M�JfQ�W��h8����V���:*��k�H5�-B*d����Ti���08wZ��g��<\p��T��m�g�j�{�T&H��~��N{��O6��[}��p��7���lO-���U��o�����I�'��������j�,����}2\p����3�RKu?��C�o�����KH`\Vg���7�9z�T ����T8�K����kQ)�_V��[f��6!����'���6�Z�A������<.�dMx��gSKu�y������T��'��a�WlL�J{�>�R?9�������w?�tj��������9;��?�t�����eW�^�'T&OH5�d�S[������Tj�n�����3'���t��������j���NK%�����fP���'RKu���/\x��
�IH��.���,*���?���W������.Yn����R����.<wN�2�L��'!�������;6�Z����*��},��;���3�������GH�C�R?�L�g���SN5��#_Y~r�#�V�1��.<����^SR�'!�x��muC*fQ������a���S����^X����wK-���T:`������k�;-���O�����kL��b0%.�3���S�7!��7��+O��8XG@^|�_7�WlL����k�B@%.�� ���k�>6��d�U��@~|�������R��=w�.<��p�{� R��eufQ9t��a�I������#���M���L(��r�1���HH C�l�Rwm�A�����]vg�����j����)��K5���
@���Ml����O+�	���j�}��9a��R
('���zK���e�C�-��F���{��~��T���w�g�tj���d��7=�~��T�48wZ*�����������Zm�k������TjR�H�YT���g�{��_�U\����X�j�}�-3�Y�[��!����y&\~���V��3t\t���������.�j��_�~�������
@����	�t���H���kS�����c�//8,��f�d`���!���~H��]R
�nq��G����:�j{��Q�m�9<��f	����}(<����Vip�YT����6�.iP������#S
h��
@��[���#�/��_��
n���p�����m��������p��N5�UB*mt�����kM�J�g�E���z���K������Z�{�������1����
@]Vg�]�L
g	�t����0���'������N���T�JH������J�b@e��&�d���/��/;mCCC�-�|���;�@����3���W��6=�Z�{�i�?|��T�CH�M�,*����j�,�@�bXd��a�����E����-�<^�U1�Ro��=V�U��{6�������I-�-8���'o��j�x	��I��~N<f�0��}R
�/ObX��r�U�e��S���K:e�[���	wl|:�T������}B�� �����D�n��T�48wZ*@�U���������e����������*q��f�]*^��*t������xu�/��Sg�.<gN��"���ufQ�{�)�� 3�!�D��������-ZV�XQTi&�K����j������y:e����*7��9�Tw��}�G�9!LH
@���������S��Yg&�� #�V�J��,Y�J���J��+W�R��K��JQl/��M�nr��7�k���	7:����G��v�urj�IH`�.�����s�S�R�@V�g,�")U>�J-��;��t�fS k��C}6��ss��X�����	W�y,���1}���s�f��!�0N+���r�q�����J5�Vi@d��2B��-[�J�-\�0�v0�
������������k��8�s�U�����������x�����
?���B��#��P��������
�8�x���o��28wZ*@6�L)U����y�R��E����%��3����>|��w���>�Zw�������������������{���'�iSwK-@V�T���+H�J��7O=8� {q���P����.��l���e��Y�re*���t����R����RKs6��,;*UL�g�p�9'�#�3�YR�'�����T�8$� ;K�,I��.]�J��������.�jv�S���_�Pw6��F���a�=����fX�:FH`��,*�����Q�,!: �p�l��T�1����PX�|yj�!����gQ�5C�Xf[��t?��8�J\��=^�Qe�]&���N8z��t��
�
_�1�*��������H5���E����H���,^�8��-X�`��H��X�"��;}�����U������G��N��oj:EH`�^�X�p�S�Vi��i��gD)
��gF���fq��/���Ipw
���W�|`8���S�$!�1�K��r�����'MM5�^q)�8[J3��*������p��T��Y}�������j�T�Z��{h��`L�TZ�������<�j���E���A���|�L)qV�������Ol/�����UP��'7?�J�������Iqb�����
���
���1�m��B9v@M�<�Pz��u������Y�f�I�dR�W9�'�?|��p���j����������j0~k��M�f���J�U\{�\'�B&�z�}��V��K��R���v,��7�[V�X�j�-_�<,^�8���X/�'�>}z���M�y������N���#�����Z�q��p���	��+='��TW��B/6@�����J�~���T��)�$�*Q�y�U�T�~�k������b���f�9HV��6[?n����f���T���s��u{��(����U��fkfcl�TZ�����gR��Y��J����J�fg;Y�hQ*���P	����;x�����V�8��~��X����?�8�'����&����SmgGO�#���SS
��r?�Um��N��L����J��V��
����Vy<������h��$qf�$��
^8/f�������v�ma���8{��a��)�2��N�k��~����j���N��w�Akn���T�_S��~���cs�����D�s��t���S�9������S�Ri������n;Nm�~�JU����o[�j?���q+U�����������g���o�5�16B*M���R��������K��z)*��u?������JmV,��z���T�4\'�*���c���h��T�a������y���4�����}��`9���fN�+�2�p9t���	?�����c��Z%��0J��iuI��������O�����e�����C��@��������.U������IB*M�bc*U:�����f��j�9�K�

�R}�K�,[�,�vV�C0��W.��4,Sk�Pn�5�s�].��;�s[����yq�^��R	�$!�n���p���R���YT� qI���TUb�d����C�%y�����W+���~�����?S�����z���Z4��O���(������p�Q���IB*�'���
��.���T�����08wZ�@�-Y�$�v(U�/_�Zv�!��V$��$O�l(��K�]k����u��$/.��������5kk}9(�R����:1�r��J5���T�R���w��'Ab��T�,^���(n1D�J5
�D���D�����x��YX������p�EW���O���_T�PO��=uT����tL*AO*@�]�@����T�d��A1�R&����J��+*fT�%^.^�}����?vu���;��m��M�<x�T�b���*�?�3��x���L!�:�����c��~1]#N�a�V�X�?�������*Eqf��{���+��*��~����s7�e_[�}��������>����rZ����T��������������Z���cgm�c�-q}��[���j��)�2�{��[�o�^(��5+L�$�
�����[�~2��_]�j������~fZ�Av��]�J!��=;���<I�N��L�����@�����m���3g��'O.��_m������3fx���������g�W/�+��P5{�>9���G���zzjy�%��3|��w�Zc1�gP�v�_�X����5������{�19�P(��w������n9��K���wZ��J'������?2���Wj�Yl�?��P��"�P�s[��]�gp��zK�����)����J</x��S�g?���;�<#���.����|�{��
���S�.{e�����������s�4C�R�����<�c��j�:��T��u��}��������O�fL�+\���G���;b����]�L
���H���:����U���������U�N�������`����u�7�����sJ��_<6|�O	�O>0��NH���y��������;6�Z%��@��O06�������3�}�Uu�L�����.=����@R(3|��T�4u�]��SN5��3|�����^��_w����y���spJa�����Z�~"�Pb�S[�.�3h�O�����W�|,|���=�Z;�������	~�	a���P�~&�P"NO;:�*U���J�����	�|����oo
����Z�w�)���������tjj���
���z#-����j��������=����O����F�%��3��cW����tjm�[^}x�����//��Z�T^p�����{�L�J�����|x�G�
_��]��9�z�A�����_8&�����
���
@2|��T�t����3O:0�z�U�<>�������><�������G�������}|8��=S+���T����[���P�U:�,*@�����E_]��7��o��Z��]���yF���Yx�K��V���T����,*���i��;���=|��w�s�]���Sks��������~���R@}B*����R���O9(�����������]�q����9?�������7����n�S+@cB*@���M��{z:�*Y��%W�~4�����Oc}x���Rkc�N�%�����Y��C�L��R�^�YTb��'�j�u��O������ook�|"�66u�]�;^�_��_�?��H��c'����~&\~���V�,*@�=�����o�~��?
?������_]xD������N4s
0~B*@_�bc*U78wZ*��7t_8������Oji�k^~P�x��������.�����j��zK��1-����T�����h��O]>���cOlI���x���/�{b8����#�#����
����������K�J���f��O��|eM���n���9�6v����������=)�6{��
�^B*@��7���#�'��7�����l
���m����6�\�pjm��_wD�����_a�c [B*@_������kM�J�fQr��F�g_tu���M-�y�i�/�����7�LH��R���+6�R�]�L����V��H����.|��7�����Z;i��a�{O�������H��R�R��~b@e�]'�@wY����_^.�du���'Rkc��-������������O��#���Py����.�k�e��l~jka���}����N��y�YG�K�;=�5{,0q�T��3\g�8��	G��j��_~po8��������Z�g��������
�����W���D�~��T�;n���7<���k���y[x���3��{�����������p����V��%�����O-��1EH�
���D8�����/�	�}2�6v������q�/����Y��V�� �����n+���j�b@e��@�t��'�����
��������I��M�4�=tTai�A_����
�7������=�*
���J����}Ox�G�
�~�}��9Cs��)�|��a�p�.&���zK��:{�p�a{�@������o~����q{x��m�����_������m���vK��KH�7lx<����T��q�I��b������[���?�Z�~��a�������p��}S+@�R�B�YT�w�����j�zt������������~4�66e�@x��Q�+zZx�i��V��Rz�Oo
�WlL�Jg��S�����p�EW�o������7���p�y��w�����?B*@����0���
���O������o��yn[jm�����z�����23��n� ��T��Wo�W�85~���^7��9�����E_]���tjm��C�����/�����c�I��&����oy,�v�S�Vip��Th��.|���>s}�r�c���]�L
����p��S��SN��AH�iq��Z��{8��SS
�=��{w��/�:|��������+
���i��^{Dj�-B*@���X��=�r���������p��k���yGxn������9�O�����o��w��
�{�T��uY�YT"!�n������7�����p�O������g��w>�/	/9f��
���T�������T�����Th���=>��u��}C�f�c����w�~���	����>
�~!���U7>�}��T�48wZ*������p�EW���O�/�T��3
��wZx�kO-�CH�I���9��=�����j�����w����w���FSkc?3�����������}vI��EH�9�>�L�����Z��3�����
��E��),���p�CO����=l�����K�%����I��IH�9�fQ��:��T���G�	����?���Oo},�6�����{�tl���^���R+@Rz�������}���j�����w������0��1�~i�a���No�����HH�)������M��Z�A��
�YZc8����
a�f������|Y��_���%�
B*@O�����#�'��/�vv��M��>{c��?�ZX��Y3��.x�	�#��	��wj���
�3n���p�-����;-�^t����)�o����Z�g�)��~����?<%�?���
@-B*@���>�n�L
��[�x�����++�,,�Sov�j�������N+�@s�T��Q�3��3	���O `�����B8���uWji��'>��fP�{�)��f��zB�<���T�C*��},����>��u��G�M��w�^��s�����0���R+�Rz��+6�R��f�N8j�T��==��kk����n��xjml�=�����c�g?xJ�������Rr���6��\�;-��~�u�h��;w��|�����L���_?;=\z�i��LO-���
�{q��Z���g�n��G�����]�*|��w�����e�/������tl�k�)���Rr��-������54wZH�/\u�����>|���C��K�������%�Cg�=l��
@���gQy��m�V��3N%����������
������oN�����.��o����_^��R+�&����S��i���=l�TzU�i�K��#���������q������p�y��7�yXj +B*@n]�~S���'R�������������>zU���w������������7����n�S+YRr���H�J��kx��-������������o��n��Z�s�>���|I��w�9t��
@'��������+k�T�NK%���~�Sa����?�����;k��Zn�����ef�?�wr8��R+�$��������T��R?�[�~v[���n�����\�Pjm��.<"\z�i��_yhj`"��To�y/���{�y������������Z����.^|j���;:����Q�������oy,�v���V�,��@O����������3��!<���������>��_�{����C�H�L4! wV\�1�*M?p�p�KL5 �6��d��W��}��p���Skc��[����
��������Z�B*@�<�����?������i�����l
���m����6�\�pjm��_wd�����_�o�[	��r���Ru���|���{��]���������������p��
S&�V���
�+�W>�J�^����A���j@������u]����rKjm����e�=1���g���#����T(X�|y����/����

��#���5������N�JfQ��X����/�	\�:�z����i������'��p���S+y �B����S)1��`����3<<\h�����>��oZ7|��T�t��{���? ��n��������!�����ox8�6��g.=�������Z�!2g���&^.^�Yy�7����g
3��2h�z���{��]�m�����8{j��{��0�'@ny	���M�e������g)522FGG_���/���$y�7c3|E�YT����A�*����O\>����OoM���l�~��������z\8���S+y5��6����8O������MQc��??��'.�S����oC��8���'J���m+��m�<yr���u��������f�
�|�zV������+�C��K������mV�A�Y�vm*�0{��Tb�����:���D]/PI?	TZ�~�����3���
6��[w)f���L�R(��[�~"��ew��T{v�j��[x�YGfP�;8/`g�k��b���j��T��]��YEj=��^���$y�7c���>T3����������o�mC���������I���#K��{���V�\�J!���-^�8�v��(�?/����/��}06�]�@*U:�����3�M5`����������_~_ji���i�pJ�A�����?!���p*��p��T��N�e���R}��_mV����������U�<�j�|�
���{(������������X3^~�����sR����
��-����T�\y(c��y�4>�K5��E��Rmy�w7�>�d�S����L
�s��@�������ssX��7�?�����M�5����������p�YR���
�h��ZJ�"�%��������K��i�w���50��%|�_7�����?�k��Zn��������W��������
�O������Cb(#������")�����^�f<!����T�.���u�]�`����T�d��_��=�]�*|�G������g��K�;-���G����
/�!�DY�`AX�xqj}���p�=V�U�e���(�OQ^�
@�����>���S
����������	_�����-�Skc�����O��Y���wK��!�>WF���f���b����S���-w=���)�*
�E#�$���o���������
wn|:�6v�!{�?}������p�1��V���
�-[FFF
���V>�H�4���j����Ti�=�X�:�����������\�\�hjml�)���o<:\�������S+�NH��]~�����NY�hQ�?~j�!��X��V)UmY��Z�pa*�_^�
�o�yn[��~b@%.C	d��ww8�����~|ji�����p���~��G��!��V�mi���Q\�'��*���j���.gX��R��~^O���4�R~<�u���?�a��}����~x��7����������n|*���O�Z�%o��O��j��J����g�cU�5��M���J�I^��W�I�|���_�m�V(��93L�<�P�~�a���u��By��a����t�5�����p��������s@x���/��OO�����]�6����R�/�__}.�0�;��	�D�����
��o����[�j?��l������<o?��v'��#v
�0����l����4;���5��s����u�/55��@w��������>��k[
�=m��g�:>|�7^"�@]B*�I�e�T����j�����~y�w���������;����j^q���tR�,X������KL����K7�O!<�����__>��7�k�>�Z�}���7�����^^}�A�j�s�T��3��
���-�-J����iu����������{��Ncg�1]'��8=o�|�����zW�������0|��T��A������S
������J��m�����:�7�D]/����f<��wJ��{4�j�J�������~�����eM����
_}~������o:������g��B�����r?�U�1jE&�gZi����������[�j?��l������<nO<��f@%:��C������@-���+�KZ���@b�V@%��[P)���[�g�xy��@w����w����w�P��9��|����7�P�e����]��xh��[:�c"��Q4�YEJ;`cI��n�����B�?��[���>k�,3�@�����}O����j��r�ia�������fN{U�t�/d���)?����-�*�a���P�[�q�$P�L*��^�1��
��K������Z�s�a{�w������Y��_�Iv����j}!F��\�<�[�-\�0�ZS~[��'�����+H�Jg��@h��%�R1tRK�l(�*Ql/���T�{���3�/�������8��s�����`��Q�HSQ�5j�����7��&�B5�@o����&��h�� ����H��tX��v�=��2ugf�L=���3�9�9gfav���g��y�t�_�������/�C@0B*�RK�Jus.Nx�<��sq��=lg�e��h}ntl��R9p�Z�\��~@`��6�]d�4=�B'���3��~*!����S���������
^���&�%O�|���z
�!�ba����7�]C��vg��z�c.�Km�>�h}�Ps�J	 �,��P�3?U&���G�3TM�y:�v�u��8w���n*�IV��������ta�_��]8��<��s��kJNS��CH����vSqdx�b_�P!���3wW�x��
�N���;���#W���@��k:M���Y�F/���slG�j:�o�q������4?��3C~��������!�3�Z�CH���*h�-����2T7O]C��_]��)���;_��Q�#Z���=���c��j��t<�l�X_;���-�5�)���V���L��.+=Q�z���cd���z-�#�bq�W���*�mXU!C�A�/f(,�K�9wQ���V����A�8o��h}n��h��ic�%?'E�@g8�0��t�@�%��L��kjn���4������������?N����^@���W]&��gN:D?��?��N\|	d8w_�F1�,Y�G�����jjj2�Ua*!!�X{���#��������%>�L*�b�xk�<��z��_!����`M�v��K"C��K�,u��P�B��uu��}HE]d���<�?���=��*���N�p�w����b��A��}��Icc��<p�@ILL4�#�[�.+�%uz�o.�Un��G���k��E�q�����B��
FQB���z���O|��
��I<Q��S�����]�5��*���
S6�*���"���������������p6@�R�ANl�BqXQ�T��3a�Q���;�=�/W�8������S��f�i=r5sB7�:CuQ��jLU@t;�z.����2���d��r�����D��s��o�+�Fv�k-��,B�ihcX�������:(��]�G�T'��>_��i�>�\L�+q�vG�|
�2��������N�p�t?��H�����E�����ytG����1�OF*���<��Q����~D����������P ���}@EM��@t��������;�2��|������^=��
 "��
�T�
!�:��XG���|rR~��3W8{��Q2j`����9���
�����?]T�tR����������ch	����:y��j�w�A��M��D�||��������o�����^����j��b��4!�:�����w��M>�Y�G����%���h=@H�\�
$������������y���!�i�"�g��Nr�Yg�=>���MF8e��Z��7Yi�r��t�:"M������k�������c@E�9��^������h�"�"Y}c���Q�<��i�*3���C7w!��h*��rW���N*�u�I��h>����y���z�(%9^^~�|II��`C's���I��.�)��~�n(��IE�.�I�}����������_��Hbb��Lo|pL���HNW�7�������3zK��z
�8.�H�{�n�D��w�B*�ER���
`�z�����|$�5mE
gW_�C�~� =�R1!X���[g~�	��}.�@�p�w����b��A�<��}��[��0~��%���E��`�^��s�e��3������ �By\��z�w�j!|j �
��PQf�g�a�T?��O�K�q���}b��c���;��o�+��%;Y���A��o�&��:�T���u'���Q�eX�,=f�4i�^���y��R5�Pgt���j����o�/��yo�)��77M�-O�\ya���BH@��+���+�#W�'v�K����Y����b�
������6�'O�K������?]//�sX���%��������W���$>�D/������dg$��	L�@���������*����e*gj�����S
k>�^"��f����}RV�y
\g��g���<\��0��-M� zRu��R��sH��
��5k�^��)�L��1��������`u��T��O��>�]vU�������n,���h?,O� �R��OH}c���5��
�r�L�>]/u�s7DY�x��}M5v���1����j�������z���\����������'�_�����CH@H�=��\��+��J�#IT7��,jJ���8���)�S�����`��:*w�l������7��+��o�|qN_IL��k�-�T���e�5�5������Y�d�KGO�~j�f���r��m����O���Z�F
��E_!��e���O�k�M�T]��z���g{��|=�X�f�^2������b�P�;�(j��U������G����}*?n��>��
g��R�������F����z- Z5����B*����Q�vR�9��^��Il���'�-�Q_CuJ��:������*���)_��Fy�i��7���#O�`��9�����+�%�h�����,{XJ�P��r����RTK�{�(�T������?�X^]uT���:~�����Y}%>N�D���;�n��R�������J������%}���t|��4��=�
!A�d�q�����]�g�T= ���RltN�������Q����A9�������4D��<"ZKM�4�Z-5+�(O�+%?�,e�E�����m|C��?�{�3��u;J��c�z��.*�h��P����O������G��Z�ztI1�)��w�����"I��=R���R��O����R��K������w�*
{?��:��M���
��Y���T?=�Se���z@���m�?��������m��Z���D�������4 r��U�@���7����PQAXApR'K���������������M���g���5�������`��>�����
 l��?3���������7K�O�S���|v����2�'B��
��X��s�+��h��S���'������E����!���}���K���2M��x�c�]�w�|�)]<C�~{�T��#�[��4��w���t���R�k=�T.9�@�f'����`�,x�S���;���F��;5���[������Q��Z@�5����K���_I�_���] �O~I���N�?}G�+�������C�N��	_����K���[�B*L����r��V�\���
 B�T4��_�'_��f�h�i�����8���������e�8���ji�����v�?����R�����������/�<#�����$!I�����wJ������J�5���I�I��Y����#�!��t�k��%W�J�q�r���y���r�O�����k|3���������3��535���������7R��]R����_��%�H��e�\vL���gI����>����S���I�]�K��oJ�9�H|V���"��T�O���<_y6kbw�@x���I�{������Y��N]t�������������#��������R�����W�����H��OK�������4l~������$��[$s�/$���7��z���6���
S�;��\����1� \�����u�����������>���{��_|y������:������xOj�~T*��)��$)��mR��ER��Mi>}H��J����6���u������n)�s�mtOQ]T<�T�j�z�!���IfZ���������7�Y��(�k�KJ��/��#O�+��2�tF��]R��e����R����t�eR����f���o��4��=�#��9�z�\��~����k���B���W�6�NI�?V����B*L��'����^�\��
����Cr�O���k|3yx�<tK����z
����
i��Fj���T<}����b){�&���O�n���tr��38��s%i�I��>�����������K����$�{��w���D�R`�%k��%Wg���Q����Z���|���7?������	g�������)����Gi�:���u�z}��=z����b����!��=Hsm��38���q�I��~,9_I���B�n���]|�$
�(����D��/+S�;R%v���+��Ba��r��������K���k���=]~p�0���F��L��Z�k�q�:�]��T���d����=U�>(u�_���������)I�/��i_���/y�{Wr�{A2�Y )c�����zOD2B*L�t�	��*%)^fM��G��di�<�������\D�,59A�tey|�y2mL�^h:]$u�����.��?�&%/���}E��~T�w�����gp$��������� �_{V���R��xT��U��\$qitm�F�T���E���R�=��$'r��-+�;~������ih����������Kz�5`QM����'R��)�x��R���R��k���R����xx��1HS$q�xI�z�d���7�m���+�q���2�zI<k����*1���.*��z���~����'���},_rP�Z�Z��?'O~������I^S�����cR�u�T/�����r��������z�o�a�
i.�|A���zI��9�~�<���?���>��/�E�.�I:�b�����D�!� `�k=����#C�d�����\�������]r��V����t���d�=����Yz-���C[���g����I�#WJ��/���+�k�%�E�[��=�����I�w��N�]2o\,��~Kr�����O%���$���'���
���<X)[����+���r�t�����r��[d��2�����������9O�����@lj�8%��WH���I��_���/����!�o�J��Js��gp��t��3$}���u�����%��'Z��j]��gw�{���Zo.��������`����f�=Y�b�^�>}�^�4i�L�<Y���Z�j�455�S�L���c@���g�477����x2�@������/��7>8�G��������G:k��]zId���z	���!�BQ	���e�/�K�_j0�G�p�w����b��A�8�m�?��?����s����f����D���}��Icc�����D��*����xd�4�,�E[���Vi:�����FJb�Qm�����|��V�Q5���9y���=�X�6�j!�TL`���?_����Y�����y���|_� �XG$���Mr��J}c����
�z�����G:�����D/JQa���B��?�-��!���� �-]w���.*��pD�}f���)h�0p�qZ���)���HC���W�����E��6)I�U0e�Hb��j
���F E�k*++���Jo���[�B*&
4��	���:��	��������O�\�6�j�o�!�b.B*�%�`�'V��:	���
"���GuO����>���F8e���z���0p�qM'�Jc��P��R�YgKB��F�LI���i*�r��	#�r��))--�[\RqDH��
���+K�,�# 0_� �XG$���f��*��#G����}i�!sR��
�����	�Xu�!D�������e���z�o2���}���{�5�����+���k��2)M��J��-F0�����^ ��r�;���>�%.%Co����2�P����+B*����p��#�;�s$�Rh!�3P|���
`�>�7�-���G�~p�0�6&_������D6�)�f�x���t!�b�IW�T	��[��K���)��njO�{Jf�9��a<����|M����rh�4����FB�AF(��KJ��z��;�R�t>�EH�!�NR����)�=���������:L����X!�:�}������t�	=r���,��h�!sR�L�>�p�B����:SQ��x�I}�V�q�IW�Tno}t���(��k|s���F8ePOs����x�U�-M
�RqJ�+�%>���e�K\B��A��.)�)M����2���dHI�=Z��s�F�P�TFQ�HQ���k3RqDH�OG�9oqGWQ(AgQ|���
`�<�����?Z�G�n��G���W������DO��)��h�[�\.�m�IW�T.v���-������i�{N�E�1!�U$*�Q�s�4�Z-M�v��g$�&IC'K���F #�KR�N)�vx��	��$�����-����l��Z���]B)�DH�!?�B�:Au�N`C�X�PQ|���
`�<�_|����?�����'guM�#�"�b.B*�e���.uN	����+\,����� ���5��y�c�:=�NO��f��k���k���
�*R�����J�{O��wi�-i���G����tf�c����\v\o
���d�)�$���e�i����� ��VRR��Wjj�������S�(�T|����P�S���T�?(��AH��p�_��'r�X�9R����5��������D��$��g��*��bu�!�JcS�<]xP�]~H�����4���H
���TW�>.n��%�H�!�;�$�)i�����c���4�o���tx�4��Ks���P����HI�3���YUyy�C(���Bo	���LINN6����e���z�������*�
��bF�QT�F�{�pZ���c@E�5��^�7*��B��(���j0����M�\��������~T���/~`�|��!	��<�sJ��w�T����}�3W�S���������������H�o���_�����I��H�g�P�3�(��n�������������~.��b������e����E���������G!�`Trss�/��B���k����6�Z��u3���wtRqCu-Q"�K�j��9���B�:����}��;��M���Q��4�������I�\tR�����������J��N���
�i�����C��@�^��a}2���}���yzM��Ip��BuPQG�=��9�7��g��d�UZ�N�){T�����4������Mz�4��$]RF�$X�w�:����n


zkp��{��������P���]-��a���[(��AH��p�����������/��'�\�[��������N�"��`(:Q#�XZ$�|rR��Mnf�1�������!�U��������AE�F��^�Lu����z$�xt�4�����,M��-���k�DIPS����}��QD9u���8q�X�y�z�cHQ�IIIz�g�k�#�X��:����������%����?� ]����Y(z���
�$�+B*0S]C�q������(s/�%���#i)��$��
�qQ��Q�)z�)T�����$��RZj+�������RT e��1%.9Mo�����P���'����)�P��svo��xGH�0�����S���a�T���~��>����z�h��������@��\�TP'\R�Y�����si����o:������f���=����"��
�q�:����N=RLJ��PB�!��)z����z�5UTT8�R������222:����3����F��B*�u��xK�<��z��_!����3Q�0!�IW�T����S��8X�����~Y��>�6�C�R\����~�Q�]i�E%���f�Ql�N�k]ge%%%F EST(���Zo	���l�.)j�k�#�AV�^m�O�<�����`�T�����'?���o�9��������Q�0!k�w�������6��yo�)��7j�XN���zMd!��
�qQ����tl�����\���KuEQA��^m���������B)���zKp��(��uk�����+����B8�2���������o���V�������>���j=T����%B��Z����-�5U����k���q@���O���4��<��qP~-M
������e�^6��*I'H���%���H�w�K��_���=(���d@E�[�=*[�l�+V�s�='��-�O>�D:���:�S��#F��i��������3G��'}��
Y@���g�c��x������7�=�~�8RXX�G�f��%K�,�#�\+W�l���+���F'�:By�?��g���Cz����P^~�|�J��2 X�2�\����N*�A�O.\h�W-Z$����[�P��PJGT
:�Xu��T����?*OIYU�^��K����g��>�����E'�U�����i:�_�N�������d�����TzD��J�:��Tq�������S��n���s��b]uuuF��MuL	�s�:��`�����#��;����B��j��+|�+t��X:�����`�T���~�C��t���fO�&�8D�EsR�N��x���������Q'\R�/>�^"�XzPvU�5��?���g��<�&�R\z\��UI�IF9�_�}�i�F�J\B�$�y&��{��g�5����!�RZZ��Wjj�C(�k��zK��^�!� p��:M|����?���`�T����b�I�����p9���%#d��`��a.B*����?����.���P�������#��T�?�����z�o
rS��}����5���
������������wI);���������N��y��$��i��x?XVV��%���Bo	���L�PJNN�����x����>qg��Iz���5k�����*�������*��b��z�������N�o�����8T�>������S�O�bACc�/��+�{5�WUm�������_o�;�r�e}�������
��T�J��
R��e�~��R���R���R��iR��]R��O���I��5~T\��fT�O���M�l@�����c��\��W_������v�Z��P*���2d��3�k��F���j�����l4T�y�pv9���B�{��_����R��J�0W�A'�:Bq��;Z%_��F=r������z��`��s�I%������T����s�[��Xu��Tb���U���O�G�Kd��*���A=3���yr��.rN�,���WW5����n����K���3�J��T�&:�I8������r��uWrP�KJ�1in]m����,��0���Y=�m�����r����N)


zkp��(�S�������D?�5��I�D�W�x
����iE�>}�^j��<��k=wQIMN����\�]T�T�q�i��q�:�����A���6�3orPQ�z�]���wg�����G6�c���W@e��lY����[�F}@�������#���Y�������{�t��R�x�T��+������s�$������d�E%F�����#Gd����l�2y����������7���G�PQ!�=z��Q����o��F�9s��w�y��w��
�����d��z������B��U;��BI]|��T?3'Hr���Q�O����A6�{����@D�m�����U��"��7j�8�xEX����������C.�t�M����G�%c����"��z�a�R��_R��B)�.)��%�T=O}����v�K���i�*���=I�����W]]-�����^z�%y��we���F��`u�T�>3f�FQ�u.�B**�Bg6�CE���C$�
$���B�H�d�q���|�����+>� �����8a������a��8X���G=N=����,_��Fy�i��7���#O�`��9��C 5���]�����������wJ���J��/������%�����H����\S��9�%i��z���q����I���;�v��zs��M�=R��OEE�1-��~(o�������6�'��gN���w�?222���2~�x��������7��>|�1��B*A�<2-:��r���;S�����%�Cu@�4e����/���~}�����Y}%!�~���|�H�w�'���.���H��|����ER�WI�?�)�����O^���-�R[���=%i�E�z���q��$�K����'��%Yw<&�s����Ib�1��#i��gz�D=�z�hRRRbPTER����T)/o��9992x�`��������k��F&M�$C���\�g�s����~�AD:u5�V��������%��y�5-��O��K���-�69wP���k#�;7
������B���gR��;R��I�|i����9��R��k���oI���I��7���6��������[��M���wJ�uI����.VK��oH�����Y���q���>�%.5K?������%s��|fSS��)z�T=����1u�[o�eL����QS�K��]e��a�,"�]w�\q�2q�D8p�dfr��AH%�<M�.��Ka]Tr2�����TM�h �*����]K��!����]�.)F0�W���1�s�Z��"M'�J�������H�����Go��M���]+��> �o�A�7�)�Gv�4����G|�~�|�%�:�.��~�d�_��GH���.���F�g~S��\��
%9]?�?I}�x�|�~s�����{��;�y��E���F9z��l��Y�/_.�?��,[�L6n�(�����|�����{��2b��6m���;���7N���+�����l;��B�T�d��)z$�h�"���U�Y��~��c����U�<Z��X�r�477�jn8��@l��gO�����q��+X�{m}�\������y�
�z�����GBA���Q-u��8�_BQ	���v�g����B=r��9���������%K�VB�p�w�^ijj2�
$			�2"���U�7~�Y��K���}f�mF_c�J���cEuHLL4��47I��}�tj������m����>%���������S�rzI���K�YCD����pp��,yDm�k|gL�3��������Rl���b�%�������@�����n��-P�k�sWQk\��)���[Q���(x�O1��/�uR�#X��k���^��G��������]��s(z��]�$���������=��;
��sAbu�!�����>�g�>�G�3{bw�uFo����W�R�YZ����~i<�O�O�{H9��]�/	�JB�����W�����F�5������#����K����[rc�����[���)i���K(TVVa�S�N��'���Lo	�����@��u��Eo�Y��x����R�$�+yl�
�ee����w���})� (��AH��`����&�}�R�M8;W~��z T(z���Jtq�f�B(,0�*���S]������E�pEH%z|��e��*=2_Zr���K�e��l�����_-�5!���:�R|P�A�t��B����E�qq|�Z9�{�-�����W�M�rVv��2R�����Fii�C(��*x���egg;�R��E��;B*&s.�t��@��f�����T?�/�uR�#����e���<�L]p�0���|=*=�EH%�8_(���T�SV�X��x���6�$�+B*����Y.����[�/��k�n$�OZ��B(��QJ����_qI���P��H��W��������b����u��S��9RF��G�Sa�PJ]]��\�3�}(EuNAhQ����J�Z$qW qP�@�`��X!�:�q������t�	=rT��"��p�%��"�}|�P�]w���EB�F�pEH%:/�������Q�~��������L7����������z������
E�Pt�Ko��9�}\� ��
���X��]��]e���F�#�=�
���)j���3�la�-99YoA�P����J�x��+z�+�PA0Q|���
`f�eU���}�G�n��G���W��EsR�N��*��*��6T@�pEH%:�8X!_��f=
�?���2�O�Y!�h�.m��rJ�QTE�S��_�Nq�Ym�Q
�O��:�$���{W8��9EuPq��������'���������z�P����zm�)�c
�#�����TL�
%.4�U�CF�O��vd�o���:��a����;���o�#WO���5U��Es�+�����k���Q�e���*��l�@6�IW�T�C�:��s�x��G'B*����T[ERl��4W���#���s�tCQa[����z���q��!��-��6��(��}f��a?����v�����-����jQ�u�f��T�D>�5��;�B*a��$�&M"�����X!�:�>��Y��|v�Z�]4��<��s�@�Q�0!kQ�Y����`=�IW�T�CCc�\>�=2�z�b����EJR��7R�^�J{7��{[������
���EU�D�Pl]R�����%\��
�wf�OT�x///o���n��`���h��[NN���hB��;B*��Q|���
`f�k?-�<�]�\���s���]�@�Q�0!�IW�T"�����w������>�	�A=3�O��#�"������tFQ���2�G���*h��@�
���
�D�pj�5���T(DM�SSS����z*���)�cJf���T��k�#�X��:��a����;�=U�t�wA����c�@8P�0!�IW�T"���U��;���O������}�����uR��%����+�F���{D�����J���������>�1}O,�q���Y�t���?�;��U������bJQ�� �P����
`a_� �X�Y����:��'��4���~r����@8P�0!�IW�T"���UF����O�5������K��7G��}������^�����q�C)��N������%QM�cSH\jl;�>.6m�$��m�����K�����R��a
�k�#�j�c3L�<Y/���X!�:�:����H�ZrP�\=��	�%;Y��EsR�
�`�$�+B*����y��������w�>�q�i��n���|q]TB*��t��4�n(zz��S���
�4��="G|���!5E������d�=�%���[o�%%%%��:wrwN������
�D��;B*A��"k�������5�[�h���7O���Q|���
`f���t�;]�G���-����	n=�EH%z-^�XV�X!���zM��Xu�!��;�z^��)��T����#����NuOQ]T���J�Z�C(M'u�����u�$�]���P�{J����P���s�=�G�����H����[`u�k�#�*�2e�=2!���`�T�0�x_��Xzj��Z���2vHl��D3��"��f��mj8����5Q'\R	���zy������#zM���~��L~�?�d��l=!57��P�C)����6���yT7u_0�=�"	Izt$�Emm��=�����}�v���}��s�`���v�de1��^�!�+��R��(��AH�3��>��|���9�3C���=N=�EH%�+���=�&�$�+B*�WZ�`tNQ���9�5��������<t^Q�=||b5����gRq��X��	����W��S�$.�=��P�����^� ���N������9}��J��*++�������F����n��IZZ��t�z�w�TL�)�7k�,��y,`Nd���`�T��xW�������� �M��G������D5���)����(K�,�K��$�+B*�SU�(/�{���7��.���c��i�dP�c�m������`�1������W����V
����8�PO�0J�A�GIH��8vHApr\TUU�Ql����j��;u����Y8?�W\!99tF�P����q��R�w]T�~�HF��B*�uz�?��g���Cz�(>>N^~x�d�Y�J1 RP�0!����EE�S����N�"�|u
�F0EM�SS��Z�c���2��^2�O�^��oo�g�.�#�T��A��zH����mz�S��tHQ����{D���T�/h���6=�'t���@��z\�n(�RT��H������n��&�����xGH�D�W��	W� �Q|���
`��s\+%
z�h��������@�Q�0!������Z���
F�pEH%x����9dT���>�����]d���2b@�^�������cZ��G���3�����"�3����X	����K�
��n(v���R�D�Q\J�C%�@�Q���{ �����.����:c�H���'s���#��k�#�b"��
D:�����I&�����������g��D9���%#i�D��"�]�_7.�Y�����K�6��9]^���n�9y��>cwn���f9]� %����,]��$)�:���-��R]�B1��T=-�'��#.5�ezu��e��H�m�6#����Q�2��������`P_��y���?r�H=z���^���cV�!��	�!^3D:�/�uR�#��}�����]�z�������o��G"Es�+,����_7�Z�Y��������G��)�K��&0vh��0������ "5��\Y����+�*����ii]i��s$�@�Pl�F����@$�uEQ]Rl�Rl'b���3%??_��Q������� �P|���
`�=�U����z������+.�`D��"�]� �������7��9r�V������2����T<�p�T��O��QN�����.)-U%z�������GR��yZoj���,>���=�} ��l{�YYY�T<����|��gzK��s/�s����k�#�b2������`�T���������++������y�����"Es�+,����/"��P'\R	L�����>���k|wN�,�{I/�<��^�H��Js�1#���(�S�����="G|VA{W�x�E�STH�A�������)''�=�b������m
�������*]�v5�E��;B*&�={��KQ�H�HG��B*�ut�xonn���V*k�
m���|�����@���a.B*�e����y�
��@�pEH�s�o8iL���p�^��!�3��i�����6���4�v��b���V�="G|N���ytw�T=j�D����0�����7����+]�tq�$%%����<yR�-[�Gm��'�x�g��RPP�G@�Q��������$�V����'�Y(��AH����o}t\ya��z�[��R������D����K�,1����N�"�����N�Sv��k|7��t#�2c|7����!���E�![(���R���`���itB1��i����,��E]]]{ E�T���
�?�IPT �>�����[�������uN�jb���:��L�QS���~3P����J���U� R��M�I&� �R��3����~�l;��Hq�������������D'����
E�pEH�7k����9����#}
��i}f��]�A$�Ri:y�nz�}�����)�X����]zKbA[�>�����@4���q��[8)�e���(j���Hg��*6l���?j���c��A��^�!� q��B����e��Iz����l_� �X������U�7~�z�����������������D'��v����U5�r ���5Q'\R���OK�i}6����]Rd�%���I=�D���J�$�����"�?3:�����f����S|�~m!�|�0�������@����6����TU�?��Y�����4����~���J	5�QEuV�����:�f�^�!� qW$	�*���7O���Q|���
`���}i����1=r���(/=|��4=�EH%z9_(d��D�pEH��
�J�i}��,�k|W��"7L�)�N��� R�45�PT��3�~�9�C��nl;��$��<v���B)����@4���4B(���5%\��������U��L���r��a��^+g����W/�������������xGH%T@E�����
�F��B*�u�s���5��?Z+
���k�������G"
EsR�N�g�6�!��D�pEH���}�F�����k|���$7L�e�g!��4��wBq��:��� q��!��Fi�7�(�����<�a�\WW���^JJ��P7[w���,��o�`�����@Omm�����^��#��xGH�d�
�(�T`6�/�uR�������G����#W��;O�uO�#������D�`T��D�pEH���&��w��-�z��2��`�
�$&���G����!#��B)'�����="G\b���<�P
bCYY���=���zk���-�b���{|�! \��xGH�dHf����:g����SQ|���
`���>�Qvv?g�����g_�G"EsR�.]$h�EY�d�^��P'\Y=���P����ay��Sz��R��e�%���}�R"�ukij���S�\Y,��]%.+_�bl:���*�	e�����s�z�\rX�9��R��FQS������{ �wF�-�B���'��5mOzz�/R"�8�^�!�+����D*�/�uR����}��r��c[��������c���d=�EH%���H��u��UC*��V����e�O�5�S�Rl���.*���h���\)
�VK���z�	=�I����<l�$���F�����0J��<mS�4��{D������/����RlS�t���@,P��m!�����e[�&233��} EM}c&B*�#�5�R1�sHET�����`�T���x_��n����dy���@���a.B*��9���o�@��N��ZH��D����ay���z�>qO�{J^V�t&�Y������y�v���v�}z��J���������w��Lq�Y��m�$�h��b�Qr{�=+��F[��!E��Svvv{�JIII�[���
��z�w�TL�x�b�?�Q A���X!�:|9��*��?^�G�n��G���W�D*��"�]�_7.�Y����R9v�N^x���g�1��?�N�iL�S���}�pp��,yDm�k|��{����_����kBCMA�0=������4�����c�B1���S�����{ �444�Rla���R�5<rss��(�`JRRxr�TG�k�#�b"��
�"��:�������;���o�#W���8�������G��\�T�����4?0u�U��TN��������^��+'�0����5���T��A��z�a��-����NTG�Y�l��'��('����-�T��="G|fW�#�����(�S�>��4���a|����w��R^^����-�bJ���=B*�#�5�R1!D�/�uR���������g������Q]��_8[�D2��"�]� ���b5�RZ�`L��n���;{bw�{I/��-M����J��w����O���p�����{��tG���cg��9�zk]V����2�G���*0:�8O���E����cKmm�Cwu_QQ����z��(]�vu�Dz-��pD��;B*&�Ay��(��AH�o���%���n�#W�u�\8�� 2Q�0W��"������B!B*0u�U��T*k��)jj��&���^:�@�^�[����D�����8���o�������'��O��{�%��N(FE�T��~i���{D����F%�[�����G�Q
J\z���c|�����)�[ee�~F��S@QR���D#������l���RXXh,S$A���X!�:��?yz����~>��i�����#������D���W��6�f0u�U��T���`�
������1mL�1���>�zMdRS���~��?�Tl��?j����E��Q�C)-��;u�S|nO��m��CJ�r\j���s�0>:���s E�T�%))�=�b���������pD��;B*&s.������'�Y(��AH�����%ur���z���+��M�{��HG��\�T����B�f��%K���L����Y�0]��^?e����cu����O��������=���������n����*7L�)#�g�5���h����N=RLJ�hq����X�G�#�Ko��FQ7=]O\J���\|y��<*�b��G��4>�����H�����!���Q����J�I�*�T_� �XGG��?��S�����N�.YIz �Q�0!���|�A��9��<��u��z�#��q8��$-M
�RqJ�+�%>���e�K\��|�Ry��#F8�t�������3:��;��)`"A���J�J�.*1&!����<�PJ\R��#4�0>������(�������RSS��Q�����q)8.G�k�#�$�'��+G&M�D`��
`�T���x��������&�tl�|�VN��hB��\�T��sPEQaUQ����|�������HEZ�DuE���Rv���c;��3z����%y�TI�3J���!��>jL�s�����������q��������W�����0����F�[V�\"��Nii�Cwukhh�[C/==��;��W��q8�^�!�������u���h�����
`�T��t���R,�}�������0
��EsR�.*a��L,}�]H�<�����b�
��:
����:���:wY���V$�Ij�?*5���!���%����0O4�T�Xs���r���i>T��.�%���k����T���zd�������������(F7���uj[$����Pa�@��wO8�n(��(�@JZZ��
g�#�5�R1���M;��
�FH�B*�ux:���v�p{���l`���c�@���a.B*����f�X�8���O�-��{��O���D��	u����f�#�xh�^����#%m������u0O4�T��=./�{D��k|7��|~ZO�<��^��|*��U�Z�_�A�S����'��m����R�X�Q��F�+l!������p�7���v��������Q������� �R���
`����5r��
�:w�v��njO=-(z���Jt!���s8���v|}�s
��0�����Z�
w�DuNQT���F�'������;QUTg��RY��I�s��#Uz�����4�����|�&�4�#M��Hc�fcj��������KJm���J1����{�>���z�l!�.)�����>]����SIE3��������
�
!�:����x����s+������e����� �P�0!��BH�;�:���'[7P�>}��Z�����s;wi���8�u�A������F�F>���]�^��{���
L�!��6���9,��*��
<+]�^�[.W��D�����0�!H�,ME[���To���_^9=O\r��2(*�bS
J|��z�����������Pl����2�5<rss�@�}(��Wpp\���xGH�0B*�uR����~����tE���������s��hB��\�Tk�������]Q�C3�x1V8�$���q�����������zt^$�TVo)6:�l?P����o�t�aZO�=��^��K�J�
��N)7K�a���+�3��voK�~���%$��5�a�H]]�Cg�\Q��qi��GP��(j��g�p\���x���om F,�p�c@E�5��^�BI�M�4I/n��5z����Mw`��?fT�|����G�K���m�������J������������J��mR���R������+[oWH�������)��%���>7��c��*VTSS#G��m��a��^{M^~�ey��wd�����g��4���r���2d�9����Nm7�t�q?q�Dc��N@�����t�	����~Y2b@��X�$�<y�^2_g�{��z����H�{O���b����y�/@��xW�|�/�e��e�^��
)�M�{?7@���8�rR�6�Zj��a�*�~�Q���W��O&I��o��7)�[�Hs��gd��u�<�L-����*9t��l��EV�\iR^}�Uy��we��MRTTd�*�G�n�d��ar�����_.7�x���9S&L�`twRS�]��>�T���H�l��y���tQ�rT�5���@�vSW�u�$\2k�,����+�����5�<?�6�-��o���y�|��D��M^V�|�������Sz����|���}��T��P��+%?�&�����|B��m=Hk���/i�T��X���<x����
��P�
����
��2����,��w���>���v��W���s���.�q����$77W�
�E�L���[�CbO8�ZZ{�����fcy�����b����l[��{�1c�YZJ����DIJ������\���E-$\_�M�N
�eY�d�NQ���,Z����y�fm��+��i?�����=v�4�i,�W���J��9�>�G������~\���<.�(��="�o)�k|���(7L�%s/�%	�fI�iij��C[���fi(�l,7W������^"I��tb�^c������K��#k��o�466�4�}D:H)..�����[]]��z�������~����dff���F�x\�D��;w���n��I|��$\�����hnY�����5�;@DRA��T����������2�����E+�mPQ��X�>�������C# R�*����c������PQ��;f��g4An��wH*����a��R��)��������{�z���a��A�$�%��"�s!9��)9���������{������D���2���l��A�~�my���?���1���~*��i@%==]z��%#G�4��\s�5r�u��%�\"c���~��P��8���~�
��tE]U��|���m�������.�=:��A'�:l�������*�ZW�}k����G�W���N*�������#�T�C}�c�E���������6���-���������������R��z�J���u�������C���������������'��%&��wN�L���GvH����X��)���!�%��2�$��hI�=��O�;Z$!You�pp�T<~�.��'%��=B$u�PQN�>��E-���p����.]�8tIQ!�>:����x���U�q9C$��z����}8���
�AH�B*�u���_�V*��5������_~�����Es�+,����_�p�U��)6�R���������������s}��!��C[��/w�Q�d���k��	FH��D����!Y���p���)*��������������QM�S�zp���W�����m�$�0J�QF %�`�����������������k\���}����{�5���������0^���Ql��Z_8dee!�P����DHpD��;w�����#�HOW�,X� �aw���
�EH�B*�u��}��:���K�W�n"3�w�#������	���d*,�p����=<�S�Q������*6��/w!��T�:����7�W���W�G�Z����[�?��)����Y�G���Sw�[�q��I��X-����5��dT���.���	�G%��nIj�%��%�%��HL���!��?T
�HS�PiN��;/��N�X�t��k�^�����R5�i,�������3U����6nUUU�����'�K���b�B��`@4�?&�����w�Zs���"��1\�]Ub��]�Y<S�h(� 2R���
`�x���e���Z��QNf����D=����]�$��p}�h�<5����L�>=(�O��h�@(��+oS��?w,�Tz������F��6=0�Su��-����D�OIe�,�\+�l���S�����Y&�.��$�x�$���L9�[�kJ���k���FJ)*
����`�����myU���u�T�{�!���@���1)�V[��\>T����)jY���t���t��I�3[`E�lx��`�����!�:��������'OH�����5�����G�!sR�L�����+�\4�-�b�K'�H��s�O�������$��-n!�����wM���^U�w�1���^�=B���Y�o���7u.�r���pJ���j��5�F���S��@J��]��iR�j��R0��o�}WK�M%��I:���������X��5������Pl]Ql��pRl]QT(�H�f��^�!�x+��S'��*�x�)���G8�"�X!�:���fyau��zb�X��=M�D3B*�"���],dc��(�j0��_�h
��8�A��?8�����T���:I��?J�{�����.�GR�M��������8p�O�EeM�������ail��8WS�~���d�Yz������C[��h�4m��������K���>�%��HIl�O�=J��#�����AZ*NIse��gv���|�KH�[��������2`�����JII�q+--���OKee��z����g��t�����}(�������[/Q�����	�	�J��3��BH�B*�u����r�d�9�xN�����z �R1!���kX��2��;��TT��~�k�����c9��:Ic�)���zd��/?-��G����{���
�����k2�)*�R[��\r^��pIo���pJK}u�q��=���[j=������_��0J���hI�N�X��[��Up������ ��^uK	pH���� ���^�!���3��0��e_|�:u*Z1��
`
���w��U�\���a2��|=�(z���Jt�e�������BRQ��y��y�7�
����,Tj�{B���6�.I����_B*�[J[8��TT�}H���������/K������4m2B)*��x�S�%�T���>��N)	�G������������WA���:�kJ�$''��Pl�����B*�#�5�R	�@+�D]Q�i	� X��AH������e�O���ny)����@,��a.B*�+���������I�b�s��){:�Rq�8?��?���/� �u����!��<��;#i�T���7zt��������;����}7��\8��|~Z/=0[�q���i<�����KJs�q�%����Kb��0���G�����[���������jkk���KIIq���������P �8�^�!�P'��
�(�p��6Or,D���AH�}eU
����#W���#w���GbEsR�
���o�%�k0�?[�t-q�8�T���5l�-�	�Y'i8�Q*�K��~���R��9��������Ryu��s���zc�������i=e��\��Ms���i{���K����Y�=������;�$t��� �����O�c��i|�%--�=�b�dff��@�!�8�^�!����AH�}��sH�3=r����{^��=�EH�����8�L�{��>�`�&(T�]'QS�����W�x��)��o]��z����,e���q����sH���N�����w�38���g��y������N)j�������������!E��R2�V����R#�bJ�}��A[0%==]o�!�����r����	����
��^��<^�G������9L��
��"��X����0�����m�^4L��DB�DuT�Y�H���Q�=��WR/�C�g�����T������6I�	���?Kn��M�eI���m�R��Hsu��#���5�(�CJR�����:�~JP��(j��{>��<��uS]S�hGHpD��;B*��R���
��~Z"?x|���������mW+�=�EH������p�s�D��g���=U�=��PK�DR�DuTQ�U|��*-�w�H��UI��e=|��7���O*����n*�*�;-w;*��:.��wK����:���Sb�s������)%>�@oD,RA*� �m9���T �k�������"6RQ����
`a�T� ����<�SVn:�G��tK�'���#������ ����*��Oo��}��Y��E�$q�����`<=o�vQQ"�N���4�\)�;WI���z��D��)�4l��45H�S_k}P���^��$��[����O��>Gd��J��c��?��	�eh�9'�3�kv~�Y�m���=��SJ���+�V8S��jjj���VRSS�N�T'R���(�{5�O8�������-���������
��z�w�T#�X! v�(��[�^�\�}E?�izo=K(z���
b����7�v:�={���w�L9N�\'Q!���S�\Y,��]%.+_�?����T���z�Y��$u�uz8Z��X^|��l?P����������eH��^S��:H�)���3����'���[���S�����r��#��L�*z��)�z����|�6�����������zkx���������VPP 		��`m�TG�k�#�X!�:���K���Ez���'H��d=K(z���
b�
�,\���@��S�����N$wP���:I��eR����G�
?��Q����K�(T>�^b�S6�-�k���-�w��~���?��_\rz{ EM��:���e�������e�V��[9r��=Z�BGuv�R�MuI����
6�7����C���0pBHpD��;B*��R���
�n��z9~�N�M?/_�w�0=k(z���
�@JV�Xa,�V����s;aT8e��I~_�%V�$u^��?�G�e��[I�_���h���\)
�V{��)i�dI6U���k�g��y����aw[8%Qu��2DuIi]��]����^�d7mOB�s�������a�����v�*c��5��Cuu�K ���Jo
=�	�D�������+�����FH�B*@lZ��X~j��Z��2vh��5=�EH@,�Ij?xF����y��$�_��$��W �j�?*5�=�G��]|��]z��k��2�s����P�����{�r���A��8�@Jb�s%>����@��)���3�C��p��DuTQ�U��'� �����Fo
������
��[NN�����8.G�k�s����
`�T� ���>��|���9���(O�����0��
WX$\_��X�������,L���K���;�(	g��� n��%�H�!��\�I�=R�f�/I}��5����c�����x|�N�/=�N�-����>m�m
I`jR��*��-��6��(��}f���sG5=�}wu���	�����@���1YYYzk��a<���pD��;B*����s+jN�5k���]lF��h}�P"�X! �����m�#W7\�)�|n�;�(z�+\a�p}]�b�NR���R��)=r/>��d���$t����L�s����L��7���}�z'���������
cz����R�g���,I��$tdR�[�Q���z�IM���)~<Q�U�vVVV�H�����OE
iii�u4����8.G�k�#��N�={��<���T�\��!w�����>���C��
`�T�������s������w����UF
���0��"� V�$U�����{Q��K�>X2����gv�k`�A�������S���*}�=Ov�Q���g�th�4m6����[�/.)EO�sn[��>�$>��GCMM����1��%���;R���KOO7����ddd�������+������ST'�)S��QB*�oTR{��%z��h}�p �X! ��}p��T�/
N:;Un�8���q=�EH@,�I*_^ ����#�T� ��?J\r�^�@����)~��w�_�>SS�d���A��jC��3���-�\q�m[���etGQ?7j���^#����S�d���z��~���_�x����
���RT��p��x����z�w��cP�F��T����P���vS]H���Uo�������'<T�Ig�����~�$�s������|�=B��?fT�|��S��KJ>_*��[�#
�W=���u�$O�Q2o�����F����9��z�-T"�����R�@&����y���'c���K.�D���z���k��#F�!={�k@�z��r���P<u������[O�s���s��.tR��N*@l���m���R=r4�G�<pM�����6��1��bBq.���U��IZ���_��}k������J�M��#tFc�#Lr������>R��eK�1m�(9��/�]IKb�4H�^�4o���1
���K�{�Irrr\��INN�[##W�#�5��{�B�� �*�r�*�Q�E�W��u��$Z��{Wy�(K�K��8��������E���R���z�����R/�DvX�%�����Z���������}�]�����v�=�x�(#����v���i@Q\�?^f��!s���+��B.��B9����{��QP�����\��U<p�����?�u��q~s����s�O����':��A' v<������#z�(=%A!��
=�;`\�c.w���<.\_�+��I��OH�S_������R/�I�/��G�G�c7K����������>|�$Qv5���n#d������	��Uou�w�^ijj2���9jjj���LJKK�{P9}�����^��{�`S_���k����������+������O��*�a�E��������I���7��W���>}�^j��+I�>7�?��[�p�q=r5sB7I��<�l��s{�����U����5��~�������W-M
�����s|��8cqK�|�t����Z�a�����H�{�&���c�}��*���:9q���a����e������/����*+V��
6A"������
,�����!Cd���2g�������7�?�|��	T��	��1�
(�7g������$��f��G�-���d�
2k�,�7���(�����\|�Z����(\{\�k��btG�T�2FQ5���}gX�V�!��J��/�7�J�S���jV>�z{R�����{�~��R���n&5�r��yx������a�����f��������j����s���]#��m�����F�hhh�S�N�<y��w� �
�����F@EU�?nW�1�+����	\p�\~��r��7���3e��	F EuL�,B*&�F��8�S��c+��}�c"�P�����,X�@/���@����7���)\wB/�38G����#�E�.T�D�Q�k��stU�Q���V��k�d��+/]j����~��Aj���&�]��T��P��|��<|��=:W*_^ u��w����"N�%M66�#/������+_���|�f�<Y?WV5N�c-�r��.��3J~x�09�_��$��RLu=���l��Q�{�=y�����_��K��G}$;v���G�S�t��|��]PT�T����k�AH�$���Ka��H)��_m���	GHCuo�H�>7����0n����q�vb�
�������<Q������PJ8A2o��yV��ER��?zd�������Y�w�z��R�����O&I��_��7~!u�_���[���^?�
�:�8?�/��&_�Z,?��W^j�\67�#5-)z����s��_)�u�����"�JKK���>�-[���U���7����{N�,Y"|��l��]>,�������������= Xx�a.��8�<����#�E��Lg����=	o�{]���|���.*��Ir��=�:l�;G_�p�^�%��i������gU��X��^���� ��G���V�^��QS�Ai����,T*��
)��)]<C*��Oj��N��,��������dR����T���8T/9R������2����B:$��m���_�|�My��g�{5V!���")//���@��q~|�^������]@E�T���N�4����*��#��������i~����&Y�����9��^�:<TTmE]����b��9_��j ������s$�����g�}��,�X�E��~T��YJ:_J�B��r�q��j�����4-�M�tt��mxM�����?y���l����sR��<�y�	i��Z��=���H�f��/+h�-M��GmF
�6����)���ka���*9r��|���F'�w��^����?�r�J��i��9EuP	4 b�@��q~|��=����`���.UQ-��u!Q���}����uY	&�B��7uv�FuQij�\��EH`A�:Q
������G�j�U�J��yI��M=���_�����)�+���c;�ZGj����S�I����B�������W,��EJ�(e��U�~Hj?|Vlh�/r�[���lh��D���%?��ly��Qr��.z-QSS#����;w���ke��e��K/�k��&����|��'��~)..���F��������}���Q����W^y��9Ro
�s�F=o~~����JV�ply��p���9�2�|��-nU�&������%�E�s�(\��*����I���z�58w�U�:������	����wJ��/��#�G�-M�}(�:��.+
7�5��\v\�w��������#e��FJ~v�T���R�������H���{���E�>n%�{e��o&���h�2���������'e����~�zY�|���������u��?�X���c���
���,�����1B.��"�������o�+�������J�>}$;;[F�-]����*�]T����&B*���&�p�z�s[�PM���
�3E�������p���5��^�:�;�2E�s���$�j��MR/�E��p7qH{��=��p����V�x�.#�b�����~�[R]��x�>)Yt���z�T���z�cR�}�4��C#.%C��'��$��}Q�i�Po1��
��<{����1r�yt����vr��)��w�l���������������[�Nv��%�����:������0��>|�\x���t7�x�\u�U2u�T9��s�_�~�����T>c���Kg�2��m��v�|fS�@\����&��T�����
������M��}�pL�������g����
'�����1Z�;��\������:r>;��p�������=r�5+Q~r�cKo�w�:����C��%t�����8����v�������sr�A�����;��$���xk_�W��O���W�B���T�7�k�]���{����c����zi<�S���j�?���J��!|A%>��$t*�g
��C������m
��r�������Z�0���������c���JbB�FU�����X4h�$$$��H����R^^.eee�M�++#k:���T#h�����%''�=�u�V��y������m��RK����F�A��)�b��}���y8p�$&&���q\�T���z�{���I�$��'�r|8_%���	v(�W���)Z��l���7n�{��n;7n�"�V^��1���?4��1���q��-�n�8w����z���V�q�%y�L�(��'�������U��G�����i�����~J�^Z e�AN?|����N�z��R��%i,���JB~I5K�g|]2o���~g��~{�d��;I��>�5�T��
zI���k��9������X�
�<xP�l�b\<��o�s�=g�U}��mr����TT��[�n2d�?~�\z��r�u�7�wg��q2x�`)((05���`��3\��q�x
�����P�BH���UC�
� 0�����4��/�k�������O��?�o�\������g�%���\J~q�T<u��,���m~S�N�����$I�5BR�_'�W���{����%��H�
?��)_��!I|V�S��T��%�]M���+��Q{���i�w��i��G=��}&�UTTa:Q����z������5:}��JQQ��=%\T��P��;�<�6m�\s�5���^.��2�0a�qu����.*���/�;��A��z�z<@���5n�UZ���F�>���t?�?��Sj�����	�u��c��6Z�;�����.:Voc�2���v�����@t�{�'Rt�}'�)�����Ggp���{�n�D�X3�;'�9\��n���^��t?�>]/B��~5m1�����s�����#�=�T=C��1mO�!zk`>=X)���&=j34a����j���QS��*����K��=
�PM�SUU�>M����6]�m��H�~'8O��������{D���b9|��q+))�k�����^�zS���w��:�5\q\����;w����L��s�Tr�~�����/���@K!���)����H�>w�Q|�c��=���j%��D�����O|�G�~r�9r��.zt�;`=����8��p}�h��%�O��|��:�������O�Q�����BQa��[�
����[�s�T������{���������k����y�a��N,�9�~7���BIJ����!���Z� �
����v'o��Q�������~�������%--��F?�a<���pD��;w��y7b�~0+V��Km�P	������?��=O�>7�'K���K�z��
�`E��t�����H�H��3�F���{��s���Y���/�I��������d��kI��+�t�%�T���G^�#w��cy���mw/�
������J�,9��[���&)[���2��X�W��~S�T��I@%���r��I����l��_y���|�rc�
��}�PQ]PT������I�d��9r�M7�W\a\�9j�(���o�TH���0�(R�T@$P����\A�_�����Sw��,J���Eo.\�PR�S��E���kh�{�����B�u�Y�.�K��������=W����/�s����9�r>�WBQ	���v�t�����sU_��{������ %��G�QG��o=������tG9k�$to��G�����`��{�Y���^����:��Sc������g��N���4�v���t>���#_��W���['u����b�E-WWW�="Czzz{G�)\��1p�q8�^���Z�Z�r�N��7�w-g��T"q����0������|�n'Z�;�(�����@t���"y����z���������XEs�+���<.\_78�v�u	_B*���J$^d���NrFs�Q)}�
=j��_W&���q�]�):��n���������L^]uTVo)�k|7�W���Q�G�m>�+�qy�~���rv�,=
.�����QQQ�FQ75�$j�� ��%''�=���a<���pD��;w���p��B�*x�@�/T�9��DZ@��T(�F�6�^���Xd�xg����
��K�.W�1���8�k�z����B������$��4W:0�PQ�8!��$��!���'���Vr��D��-���� �3�!��f�-��nG����������P9���Y'W�}L�	�(j���������>A������e����t=��m�����3��j|����T���������7��j��k������XV��6�����=�\Z�U<�S�wAE;�;��7�jNN����zL$��u��z���+�l�����F�>w�p�`tV�������S;������������+�w�:�2�\�����J�s4QT�Eqw�}]����Q'9#h�T��'y�I�{���g���d�-���-�F��M{�����%Fg�����|����R�����b������(������H���w���n���k�c���pD��;w�{��3rN���QP��N-�
�8��U��8�yU"��}<����/�u��5=<�]>��D�
��!z`����XEs�+,��K<U��� t"�N��]555R[[kLg����_KS��<t���������$.!2���p���8��n!���1}E�7�5m��QO�9I&����Luu�C����Xnhh�{���yv��i{233�@d��x����z�w�T�(��J$T���TH������@H�>��C���k�=ru����S��#�8����a.B*�-��
(�V'9u��>|X�9"%%�!���<�������K����Z��=v�4��G�P��l��z��{������G��������Uz�.�U��|��;8���T\���@�����Qo�D��la�[]����"Avv�KE����W�#�5����P�6�*n�"����u�����@����������"Z��p�	��*1!NfM��G���|��o�Q�Q� *�4�7o��K���m��T�^mW�����<,�.���K��x�@����|���#/��T@e����7F��_8������E=�z^�E��T@���?6����W��W^��������e���r����TT�>|�L�4I���#7�t�\y������G��~��P�T��6�:ip6}�t�>R��q'�N*��4:�5q��I���K ��h}�P��
`tV����IIE�9��������g��upe����[l���l��h9oGhEB������a��N�T���c�JAA�^���-R��;��|�_~Z{����hlj�WV�WW�Se���{3������g���zM[�Q������U�����t��(�W�#�1p�q8�^�����j
!������@�[��I��3gN`�����dD�l=������"� �u��[����b(8�{��S�iN��f��R��zd������K������m2�)��<"��m�����g���*H�k���i���&�����
����U�a<���pD��;w�3T��[���^r5�_�OD'�A�9�b��
�����P�z�|fPA�����W@�tE�<��gr��k��%���)8�^�K���x��k�
�(G��K�<LJJ2:������G}�r�����>�9����d���2d�������
B{W��=ez�j��nz	�HM����n)6��q�|��6�~�d����N��_�/7=�N�]~H��:��*#5An��W���D����� �s0Cu�)))�#������� ]�t���1c���/����Zn���1c�L�8Qz��!���FpB*@-]wB/�JKI�Y��b��|R\\�G�P��n���H�;�c��G��`�����w�7D=�z�P��x�����r�O?�WV����N��d���~F8��Y}$;���555z�M$tRQA5E�������2e�\u�Ur��7������/����K�^�$33S?
���v���0,^�XV�X�G�Y�`�L�<Y����{�e��g����]���x"GcS�\������I�qt���r����w��u0����]��ZH��n�Y�z��Y�����%K��$u�S�N���K���������g��%]�v���4�(5K��C[��S���?d���*��UGe�z�a�������>�,�����5���e��(�T�������
������w�45���4���X��}����m���Jb����8.G�k�sw���R	�J���9-Z$����# p�T��Ck r���q������?�?F���#�8����a.B*�I�STG��=��p�I6m�$��m�#��9���a����J�{O��wi�������/7�)+7��k���{�|n�Yr��=����������o��#�98�Y������@�����!B*�#>�\q\���x���1U�#���T���^r5fp�_�$X ��9����R�	���I�����������ZGj��n������J�
-����IDAT�O|*����NT����y7�����s@�������w���_~Y>��C)**r�(fT����W_-��M���;O ]�t!@��EH%T����U7OfM����=*�*�PRR�G�����wM������{%��g%��I���5)�^��z�]�Ll;-���U���m����z��F���1L���2c����eeeF�5�����.�����G����eb����c"b�:r�����7�N&�4=�&M����<y�^��t?�u0���4?j�wr3����&���8���}����������/^,�����6�f���Q`��XS��$UUU��k��Q������>�9cj�X���S���#�����wG�
�5���`x���S�N�O�SZZ���'������1��'L�8bZ����z�w���5�T:�]���
���
`|h
D���&��GIc����7N�-�\�O�|��XEs�+,����_7u���y����P�I���C��Y��v��G�m�����#��p�^�����uS{�yCr���;&�2�)*Lif��)���z\�TG|���Q���]-�*�IT�p�p�	���~�D���T��������8�h�����E�����T@��s��7_-?�{����
'��~(/����X���p!�����{g8?U�PT�P!�b
$�d��z�����I�niz:�EB�V�I)z��Y��0I��_'Z47���+��m��o_�+E'j����M�x�Yp�0�?K�mS__/������W�/� �V�2�����{�g������G��*�?�#N���B��
D������z�j&]Tb^iU���N�#�8w�x�����k����Y�����������I�]~Hn|h������s���{�����wn"�{�	�TWWR�y�y�����>���O����$����\t�E2w�\����e��Iz��t����\;v�^b!�Nrw�8[���^r�=/E��K�n:2k�,�D���z)*s���K��#�_|��1}�{��''Nx����O?������|������N��������j���We�
��%������o~&��c�����S{�?��o~~P{g���2��m�,[�L�������������@��������@�
7�`�����'���������'����^�e����{����z�|@,"��I�'O�KmT�H���0b��'��������d���zI���h����RqN����������~���&�����:(���fy��C��H�^�H�W��~j���(��?������:���"��m�[|��� 7_�[�����5��wqq�l��I�|�M���k,�<��=�?�����s���.�L���Z�8q����Kou5r�H�1c�1U�=[@��=���W�S�W��*B*��y���z	�|�E<K�u��S����y���Haa!
�t���t�J��U)��X�^��
=�>a�W�X.���V���������Ez�o���q����N���_�+�.\//�{X��{�s2����s?� w]�Oj+����?��^{�������������K5j���3G���*9����[7����(���A��zT��@Il-g�������e@)� ����QW~�T7�)S�TA�IJ��A=3���9FX%�9l�mj�����o��������=r��^oq��&���-�����=�q-���(���;S�S�W���p�����r��
������+�M��\���L=[d�����+�]�v��)UU�����{��F��+�4j�*������v����V��\j��>s�L����X��f\�$���;UQ�u��`�����p[�r�477��g6!!�X{����~������������%����z�j��������9����k�^:t�^Bg��6�ZH��n�P���lT�[�.+�?�Q'�����7�2���a��
w�g����=�R��SF=K&��gh�EL�d�I����%�5�r��m*#u4�c�� M>wQ����hL]�nMMMz�9��=�[Z��TL��~�jjj���VRSS��i�������:4��!,o��}���6-���%11�X���pD��;w�0j
!8J[��� ��R�������S;d��b=r��[�<9�u�;`=�EH%z9_,�RQ�,Y��`%���|�Y�|�w����=Je\�2c������������-���X�$�������/v������%;+[��E�~Z"OT��b����Wj:#�-���g����,�����r9z��^k�!Z�����)|����
���W�#�5�R	w�@q5�FH�>���xI���p�����~r���z�y��uP�0!���.�b���:���:hL����*{��J��:��=�''*�:�d�4��n%rN�Jc���'�d��)�k{���y���6���2�k��������V��g��p
:FHp����+������B�b(D������{�&v�K��
����9}���Y��
q�����}�j�8[�O�t�q~o9T�[^��Kv�j{~+���G�����^�����*�@���3�8��3X\�S���Y*7�wB�qD��3-����%g�}�\z��r�������O@� �T�x�b*����Z���^ru��n����G�T`�p�I�zu��FM�����d��9]���:R����|��i:����]3@��������2t��R:T�g�=|wNA����8u�-�b{���w�5U���bsVV�����|~�a���cr�Ye�_���K�.2j�(������J��+��"�:�p9�����#w'�j������I�����<y�^��j���v�L��6���k��b��S;���/�6R�v�`��8���}�����������EB�����3�T���_j0��:���GM���zK���(iI�R�/�
	�����CQ�Yn���9:UV/�o<(G�����6��k�Xe����������,*����Q]R����Z��~GLk���Q���]-D�!��	�����[
�T�T��Ck �<��|���9�+C�x�=
�;`=�EH%�8O��.Z�d��	u�m���O��;��BM�:���[8�������-RS~L�q�~G�w����B)�{�6�)iiiz-�BHp����+������B�bw��5k�Ru�k+:Q�1�����M/_9O�L@�BL~���;�x��W��5����V �^9]f��a�<���*1���_���3�&�p�
2m�4#�M@�^�TL2o�<��Z���^r��'��u�k��=*tr��>2�g�^�H�W��~j��*((��S��e�]ft#��������*���x����s���.����KRR��@4#��d��z������JT�R��|av_��c��E�?�7)�^��z�]�g�n����_,��O������6fM�����,9�����K/�k��V�?�|�_D7B*��ZL6o|BJ+���������Y�f�%����{^�HQ�j,=z��i�.��6�vQ�U^^��5Jf��-W]u��;V�w�=3��t�����R���W�%XQa]TF���������Q�lV�X������N/�	f'5���1c��+��9s�!�.]������J��A^�p�^���9\%w���+5���y���%���B.LV[[�������i�d��2|�p����kX
!�8ITkJ ��m+VQ���^r���@H��_(4e��*0���$���z��Y�T��'''G/�2B*Z�j�^:T�P`�M-N�3kB7IL  
@ ��B�f����������@ ����R�B;����g������K4>����[e��52�|c��*������I�&������
T555�S�Nu)�{�����fcy����@����c������?=0F���#sq���k�.�$2t�P���r�l(j!������b 5���H��-����}�\XuGo������������d��9z�H�w����b��A���`,V�o�>ill4�(����2`e�#�5����R�$u���`�YT[�$0��:���o�~�l?P�G���+��:B����XEsR�.�^73�=�&�$�6o�,[�n����"�c���#G����G�"!���8.G�k�sw^A��n�o��
�EM���C��Q��=�R�@�a��w~~�EH����N�%WyYIr��="W~~����� ����@Q�v�����;5��5k��|�&M���'���+W�O@[ �1���Mr�?��f���o��K�����;`��5��������F;5�r01��5Q'q���P������w�����]���Y�����~GLk���Q���]-��
`_��Ck 4^]yD{m��zr�y��[��;`=�EHu�N�<)��-��6��w<�3c�)(��`� �8��x����z�w��8�:�p���~.�%��l*P2z�h=jc+*��(�p�}��]�Y=��~�dw��=\�G�fN�����2r�H������-�b���(�q���y�!�OK���K�ztI�)��@4QPf���w�D��G�R�PR� o�y��� V�){la���<���Z����9�e� pFH�C���z�=B*�%j�>�3g��t�Mr�5��u��j�������Rqc�������7����}��9=D���<wQ�:��t�K�#�����3w��y����x���0�+�^���E>�p�i):Y�G�fM����3B*��:����{�L<��<���
�����d��b=r5sB7��!����y���������s���7����*����2��
~sWq�n3o t�>(\{\/��l\��f&�p��
���M��Di��b��#������n������+C���G��`�B*n�Y�F�L��~�H`]�W��OK���������}�e���z-�U�T�,]wR/�JL�g�|DH��������Y�IzJ������%..�S7_�����������t��
����'���A�\�ED�������P!55���SXXh�W��*O�,]wB/��?��X����kF����!����*e��2=r5{bw�~j{�-������S��U�VIKKK���9��tT8#���Q���D��m��I2o�<�oQS��S���'�Q�j��`O?�>�T'�M-�TT@%!>N��o��5z��s������������A���R��;!�uMz�j]T@�Y�b�^j}�2k�^
�s7o������M`��
��p�q����!92�W�D���B�$2}�t����W��6�-�Ks��tS���-m�g�����u�l����c��zf�dlV�\)�������S%>�������~�<�����������G���2D.�T8����k�^:t�^Bg���N��ZH��n$s~M���	�X���0��)S���)y��\#��y���*�2o�<=��IW{���������
���c��}��Icc��<p�@ILL4�+��Q���]-D�!����<"�����`|h
����[������$/<4Q�������"�9��&���P�T�y������%K��Qd�N�"�8��x����z�w���9����FY�����5��^�k���KmT@EWT�D�o�g�6��b��z�*��3B*��t�Iij�|���	�1��'*��(����N�����zX�VQ��:���R�%k�O��\0������G��>��!��B��T��P!��5���s7��y�����P���T���#WtQ �T�w5�`�b��E�\jZj�sGh�u�EHh�t�	���{��2��D���W��3T8E]5y�d���Z�d�#�b���@��>}�^�B*�����|�I=r5{Bw�YTEuG�uHQ�p�3Vq����?�P�k���5��V��P-N�� ����t+W����fcy���OF
�U{��i?����������~�G���`�t�K�����X��]������C�:+.N�C������_�P]>:�?��?��|��k���n_'����������ch�q8����k�s���*6,���~�����@9wSq7uPg�X�B/E>UX����c���vn��x��n;7nV��s��7���9�T`in/��5z�j��nz	 �L�>]/�Y�f�^ 8To����S?L��X�t?�u0������CVo)�#G}������Q��x���~����l(j!������5a���s��-Z�H����G"�g����Bc�����c�H�Y���L�2��Q@�}��ISS��<p�@IHH0�����466����Dc�2��������O���s�t����2kBw��&M���\�N|a����B�FM�q���������Y�f��vn��x��n;7nV��s���
���K����T? ��^�����y: _9�[:�<��CH���������$'#I�"��zOMW�nj��O��8Owm?�������:����
,i��b9QR�G�fN��
�|�S��3%�s��E��%�V�X��:�p�B���9��.B*n��DZZZ�o�H��%k��%WCzg���9z�����d���p�J��n*�AO�N��+*�mZ!��>,�K�`�T`9���u;J����	z	 �9O��"��*j�}�Duc�t�����i�<U�z��S����w'�q��}�? �S�P�BP�l����0)�tL ��f���i��i����&2���m���Y�8r;i��v�M[M���i�)�����.j����|C��9�����y���$
G���?�G%!
g��#iU���-t�� U����PZ�+U�����q1D���VP��m��V���Pb�������T���P@%!
gp��J�����e��4��Ib����J)?�!�(\�
��i*q�J��s����R|~v
��P(���Hx��WSUk����
�y�@H�d�$���������� �D���� KH�B�l��7\�<���3R�\b��&�!���J�\��&D�01�R>V��&�0!
���_�8�l�ju�����!������G��t��L�|��87�MH���s8�j�vr��~`	�P��N��O����}I[��zR��	/IU-[���R�&����5g�U��*`>������h���GSUk�)*0��Thy{�U�g,
�{�y����
-����'���XB*��PKU����0��Thi��LQy�V�K�;9U�|j{�Qs[����w�2n��]a�����������
6�>�\�r�-att�����k��%2j��8p�z_�z���B���gB����Z�����xN�����c���i��5k���jk���j�i��mv���iU��K�S,�$P����add���������5��C����pi�j�����QZC��.��~���z!B*�$�����_���a���a��������7���������_}<U�.Xqr�����T5'�;��G}	�4��?�����W
���>}�%����\PM�fjy����j����@LOOO:�T�:����h����
`\��l���4A�?!�&�*�*�3��pZ��R& ���v?sPnVl��� �*����{�����;w�|My[��������
�
��[(������_=�R��mz��a���MU�r�Cq[_��i.�a��*�S&���m�{6Q�������l����I���~��mM������L-�"�2G�Q�m�L���6p���������&	3����Mk��w=>�'�NU�������>;U������Q_B*�)N������&�lo%�k��4nj]�$PKH�y3j�.��~���z!^}�A6���4��H|^lt��+��#h����PJ��4 ���G����+Om��
I6��$3	�d{+�\1�R)�O�W9��!�9(��-���5�Hl�d�FI�I@���:v��d�jmYw~Z�������t��5�A�8Y%/��m�������'�2K��Q9&����l����&
�T>'r'@��=��*��
4���<q*�le�-y��sv���V�\	��IWWWZ�N���9e��Nf+o�/h��l���w��:}i��fS9ev�*o4���R�sL�m}�$GHe��������n����g���V���i4���Hv�,�\�Th;&��r�%��7w��*`�	�,�v�J+f���/�=��NU��u��Pd;w�L+`�	��RWWWZ����I���n�=�
��=&266�V��&����cI�"�M���;�B������>��U��Re�e�����ICHe�6l��V�b�c�M��u��R){�(d���=���V��rR{��f�y����}������MF�s�UY��R����47n�qP%>?~]������6� @�)*��0��Z������$$��h�&>��ukZ�����V�&
�3'�2iuB�L�Q�)���
�D��K|n��Y�h��~��ryx��g�
h6�&�l$�M����u�P���T�������$�dfGHe�������r�$>b$���c8%�����*'��'�d)� @Q�������MU�����bP$/T{&S�`b�%/����(>?�������	��Q��U�b$��H6�2�x���~��������78���O�����*�,42�L^@e"�}�M�`���A9�2��k���$�b�,��W����������,Y��*�����\�@�_;��I�W���J���I�Q2��J|nlx���(��{������P8!T^~e4U�l��'�Fb�d�a�r_%N����R~�d�`��m�5�,����]�v�B&Y]]],��o����}����%Kd��U8p ����i�z�j�;-�W~������������}�tM�Z���c���i��5k���jk������F}�V�/Q^��>LD�j<x0���������������:t�P.�W�Z:::Jk(2�T���Z^/DH
B��������>������Z������o;/U������Q_B*�>	�R�j���Z���_3��^�W_4�{��U����|@���
D^J
���/
��=�SU�{��i��ICH����L�;�!Xt�T����?�����n��<���78��w�~E���SR��]�v����Tf���J��"���app��`~|u�3���/��V���i��N����7���EHe�b@ES`���~&r�9'�w�qE��V*1�47!�9��eT�S��v~��T������Z��
������������C�������6o���������aCZA}�r�-att�����k��%2j��8p�z_�z�������?���OU�����p�Y'������8����V!�Y�&����eL�B�B�}��DST�������`�I�j<x0���������������:t�P.�W�Z:::Jk(2�T���Z^/DHe��CCC,Z�/P�������jx���SUm���
��[��bp�Cqhz���Js��l{{{S5~����@�`v�I���
T�f<�r]@5�����B����� �$*���w=5a@%������J�m������J���`~
�=�V�._yJX���S��������y	���=����Kw<��Z��W��X	�����d�Jd�X��Tf���+�����+����=���V��y��T����;�B��sgZ�HHe���0n����g_IU-ST��m��9��f'�2}}}i����
�z�3�V?W]rzxs���Z���[��������T�FHe*�$QOOOZP�=�b����T��Y�2��VVy�Poo�m��I	�����PZ����*%��c��ST�-]b�(�x�Pwww��'���������������C�(�Qv��]��'+6Of�orWWW��aC�`�n���0::ZZ_{��a�5hU8~��^���NS���	���j��}o��W�3U��z����Z��f���b���b;��B�B�}�]��@��o/�$T�^�m&;)�b�'�Z###�uggghoo/���:���K�U�V�������u��k���R��x�N^0�^��F4Ib�'/tS������K��������	���{RU��~�M����HU1���84=�KH����������O��T��7�������L-�5�W_�� F���r��7�.�x<>g6�t��870�{�U�k�\^��
4!J��������I���s�By�rn`r�|>�~��T���������~f���|���
6lH�����ml�m�V�����l`c:[��hh���ncl�8l�A+���>>��GSU��S��_���m~'�7�;����'bf��g��������/��O�l��lk�\PM�fjy�!����?��5e� F����T?+����_�s7���7�iv��:~�{����7G�~d�E�����T���C���i��j��@-!���x����j�5S���x�U`1`QP�T&
�D�sqRH���f�~n�L<?_��
Lm�mG&�D����$�R`����?S�����sgZ��n�3Q��,~>e��__�Y�
Lm`������5g�U��*���XWWWi2J9`1Uc&���J�	,��ysZ���J����v�����x>U�LQ��%�R`1�'��z�l8�,;�%b�#;�%O������#iUk��ea���K�l�T�������8f;�e�������@��/����Tz��L+�	���A�����G6����z>�v1q+����T��(�1��Fy���4�����������l��'�'����z>��#���b0%>zzz�����qCCCiUkpp0����<OY�����I����
+�����*�	���<)&S�#����n�������C����Z���@�R!�d��l��Y@���=����������sR4+!�[�n
cccU�x�R�s���~������4e%N[�13_������|�����?��Z[LQ������{N=��R!��m��6}}}�m~*��JOOO�fgp������~eZ����G�-#�"a"�v�
7nL�����������nH������)��<6����[n	�����E]T��
�������i��O�#�����;O
���g��,�;G���f���b��������}�Z�}�k��6,Y�^>8x�`)�;;;C{{{i
Eu���0<<��X�jU���(���\Pm���i�_3��^�W_�H�d'�l��=��c���iU�z�z�MP��}T�����bz�~��	*�;����u��Jy����h��!f,;]dpp0�f���^J�Z����tY��fg�����.-����SN���T~~�������r�m��c�-��8r�|�����w�G�|9���z�j���^~t�E�"������ie|l=�������T��	�r]@5��L-�"�RG�����|�+�F___��uk��l chh�|(�K`��5/(���n4�(��&V�z_�z���E�O?�@���=��Z�q}8����"���C���i��m5�v�
�w�.�`����7(&}�%����\PM�fjy���� �nooo�9R��B����?{�XO�7oN�����Jy�i�s�v�v$�jmY�R@�_{������`��"�2G���������;d�_9E%�N��������J�����	_����g���V����
`<��)@sR��`h��v���N
�[�L��
�T�o��V����<�zn(�{'��������:��
 �@�R���Cl�������9=�=�CvK��2�m��V���CY�d�-Y��������j�s!����a���SUk�)*@��^����y=��>�����cl|y��X�!r�}�+��F�M��nWC6��5��O��(6��wEM�����n�[n�%������^{mX�DF
Z���_��W�v�������
���TU[��$|��������&�z����Z��f���b��������4��6�8����7US�"`:�I�������Hi������.��:���K�U�V�������u��k�����k��[���b&L%;%Pb���/1�#�UT�����e���@G����=��M��������q��[��)**@���J4U/X�bl�$�Y���&I3����E6l1�����2������nwAq���b�c���[qO�j����9\}���b*�w(w���I*����-��[$���I��I*P������j�5S3I�\1h'1d1�y3��&7��I&�7��G���jp��ST��j��
�0!Jb����!�l`%��s1��7�-m�]N�����������P�zn(��>n?�l�j��~��e��Y�n�3��"���Bq������>q0|z�c��v�)�\����cb�w(�c��v?��r;�x�I���>	���T��	�r]@5�������R�����I���^w��
�k���i4;!�9�[��UNU����#����w����0��-v�D����T�FHe*�$Q?@��LQy������NK@���z{{K�03;q�r�1�?��51(Ty���}1"�"�2GCCCi5~7O|A�Qp����8��Z��V�@�x�Pwww��'�
D��\o�����>��b_,���m�H��l|y��X�!r��(�w��}q�'��7���+l��!U0w��rK�f��k�
K���A�:p����}����w����@�s8U��Y�,��Z�*f�������
a��5i�l�7��������*o��}{)Q�^�m&;)�U�?��v�������b�%�g?���e`` U��>	�:x�`)�;;;C{{{i
Eu���0<<\Z�Z�*ttt��Pd���_3��^���,�;B��)����I���|����5���/
������p&?��K�/|���b�\�P��%��\������
�*�tC*�	����sK�j	�@5o�C-�T���Z�kz���7q��do�t�;?��/q�\LgK�Tn���
^4'!����G��V�5����;%U��8�d&��d���4���x<T�~=�f��Y�cNw�����������U���1��6m��q�@�������w?�����Z������fE��
�;����e���2�!�V�n9�MO�~�r��d������t~f{zz��1���\�j�����j�.��~���^k�#5�5H��h�@qx����7?�?|������E��n�oO��z������F�E�})���H����*C+S�T�B.�	��`Qef�_���I���
T�f<�r]@5�����������|��	*��u����r�����Y�D�S�c�e:Zy:
s#�@�
�9�V��T�O��R9�d��mi57���z���i@�	�@A!
�B�{$�j��-����:)U�[v��F�K����L��1�T���w>y��T��^�2���������6?e������N*�@������m�����F�={g*��;��\ ��7�|����iShk��?��8FGGK���W�%KdRYX���}�Kw>��jW\xj������b�\�P���O���Y�V�V������4�������k�%�K�c��&�ST�����KS}>��g6�TR5����,��u}�u���022RZwvv���������Caxx��^�jU���(���\PM�fjy�����W�$3Sn�T��;Wqk+5Ih<�(oZ�H�<�r��7���Z����?���S�\���84=��Qa�F}�V���_�����9Z���t(B*'��@-!���x����j�5S�{���=G����4�{��U�-�l��N=����c�m��=��%����y�����T� {�	@�
�9�V���;?,?����e�rP-N�������XHq�J��M�f�'6&���X��m��5��Z�1�P���Q�����o~��x�����k��:�L��z��0>����N��o+��5�\�/Q�:f���Bn����-����>�������T���.��k�U^�5���_����K*�0�X�4�X.a�R����5������p�������KO��_�9U����CH���^-D/�Q�����$����n���b������e!C*��7��g��Or�E�>��~M��^�.�,��Y����
PD�=�����{������;w��8�b��?�[����]<S���3��'�h&�@q��@#����>���SU���K�'n��p�2?���z��0I��5��Q����e�G���k�l�rJ+OR��e�I �{��7�����W^yehoo/�������0<<\Z_q�������"s]@���-�����Z+�y��A2����.d	�@qx��F�����8�5���.��G:SE=���8�T�+���B��o��mv�n��T��k��B*�L~^+��c�H�j<x�xH���SH��;t���7�W�Z��xx�����L-���.6s2�����h����
�f�y������6��=�GH�9�{$�j�q��p�eg�
�z��Of��M*e?���:;�&N���������Tf)�"={�@������C���V���i0;q����;w�Q����m����'T~~*�����}{Z����(!�9���$���vL2E��S;l��Y����A7
-����t�������*��T����M�h��::�V?���mi
0[q
��';Mx���������J������d�$��Hh5q���WFRU��^�7
���������{`������:P�_g�*	�����@MP%6J�s���
�9�V�����p����
`nb�ahh(U�A������=����T������������XW���Na!�Y���F��s'6J�����b}����Q��=�~��T��[��C�����J���\z0LO�Q+;Qe"�y���%�2K�w�.�Q��"�[�L��3O
�}�y������Lc��(ccc�J60��q��	*LDH�9����!�-��,Jq��2)?b=1�'�T�+>�����bR`F�	cc��a� ���,������XT`��s8�ju]�"\t�������I=��R`�n���p�c/����~���0mq���\t�������*�jB*L������'RU�{����%�����d�Jd�`2B*�L���)}XL&������s�\�*��m��]��K[[[:,��J|l|y��X�!����oO��
�U�����u��T���|�����M�6i�A;p�@-�W�^�,�Ie�v��T�O�v�j�������g����z����Z��f���b��^-D/�Q����p���;S5=�_"�
�I�j<x0���������������:t�P.�W�Z:::Jk(2�T���Z�k�x�����3'�L���
!����r���zZ�7��1��*y���������T��\�P��%����
B7nLU��oPL�$PKH�y3j�.��~���z!^}��|T���xi��J�������P��������tww�������G�*_���i0?�P)�_����`!��A����G�Q��sq"Y*��2YH%T�8�XO`��)*Y��R����T~.�_�#k��m����
�`!��Av�Jl~��G��������b %~MY<_^��Q���'�����Z[l�����w���B�=��������g�U�_�cll�*��}������,e�$�����6U��5�w��_XL�N�Zk/==�i��T���$�8e2m��-e�9qg`a	��R6p2Q��2�����V��=�4I����G__��L�j��,����D7	M��2�>
0�T�d�)*S�$a�����L���G�����K�T����g��(D�>	@c���������&�l��Xcc!NR�Y�2���=U����e��4���'�2��;ae��X�)NQ9��p�j��,6���2�i,��	��Rv��t���;�&7�������y���a����
`~UN>��������t����J���hw?�|�������m�
� ����&�2K��y&�|Ry�OoooZ,N�{�U�3N�����*���l�Iv�mZ���������x�d�i*������q,�^�t��PikkK����ukZ����I�j��&���"h!�9����q��R�$B�>o���i5.>?� �~
�B�{${u4U�z��L+��S9�6NS�A��~����h�����OHe�:�fF���*e�;yb�$�����Y��X;�>�V�����p���
`�l��-����J��d'�e����K9�?f��>�_B*s�m��e'�

��	���7b�r!��r�����MU���@#��(e���������l@%�n%�/!�9�M�@�j<�D����
��N�Z���,�����
`�
L����n(�����_B*u(�Q��fI�v=�9�U���s�s/�v�=��Z���A��gll���}���z�7M&~>�w���q������b���k���{���s��td|dT�O��rK-������d������W�^�zgZ>������}��u�x{����S�b�z����Z��f���b���b;��B�B�}��������hPA�j<x0���������������:t�P.�W�Z:::Jk(2�T���Z^/DH
B�������/����}����j������?u�XL\�P��%���@-!���x����j�5S���x�Pp�������V?@=����G���E����Y�*��R(�'�}%|�����V�)*@��
�j6?.�~��v�
�w�NUctuu�
6�
��^�P8~��^���������?x U�>��w�s�<)U,6�w({�W�~��i��]������q�n��V�>	�:x�`)�;;;C{{{i
Eu���0<<\Z�Z�*ttt��Pd���_3��^��J�� ���MUc���i�PW�/P��f&�����G�|9U��������^�\�P��%��x���,���7(*}�%����\PM�fjy����(�/������V?@=	����G���������T���~r���+���;U����6l��*�;cl�8l��t<��������T��g�e��M������a|l}��g��[.7����I�j�����j�.��~���z!B*P�/P��f:�����C������~G8�T/2;�;��G}	��$PKH�y3j�.��~���z!^}�d[�lYw��
PwB*���?�|��T��^�2��GH�`'��r�eg�7�Z�*��R��]�v�������s�k�fp��/����L�j��~���K���KHe�������q������?b?&�%�b�c�)*'-["�4T9��,y������s�4'!�)��I9�2��0������f�������E��w�NZ���@c�~J9�2��
Cn���]�I�*3�)�*�b3��px���T�2Eh��G�N8%+�mU�y�Lb��JwwwZ����g��d��~��yfX{��X8�2Y@e��K4����R��D����066Vz_����g�0�-����wz.U��MQ$��)y�������Q�����
�rsd��
�H��[��%Y�$,Fq�n�e�)*�O[j��!��&���)yb_&~.Tq�s�O�p�T�i��H��(�NT��sgZ4��WG������%�,��2�
B���4�v�J+`�R��mj�m�3�8Q��d{*,��=��+����e��Q�7�d�*���kv���V�b%�2
]]]i5=y{#4�d[��c�Y��NM@����2�~
�xB*9�}��q�,ccci@��~������SU�{���Xx��@k�efB��1�T������
��'��r�Y'�����T4�
6��,�TZ�s/O����u����RhQ�T"!`!	����=������Y.:��T�?!��w�����^LU-ST��&���&�����N	]���*��!��b����o<��Z���
 ��b&����h!�3��pZ�z�[�
�,_�*��#��Bv��dx���SU�{���XXB*��q�����6�Gooo��q3������O_
0}�m�s������=+U����`n�d�G��|}|GH�E<��K��w=��Z��MQGH�E�6��%mm�{���XxB*-bp�����e����S:R�����c����j��]�����S�]]]a��
��������_��6m��6�������z���a���"��������SU���oo\�<U�
�;�����*�5k�����zh!z!����YZ5���[��"�'�Z###�uggghoo/���:���K�U�V��7������L-��V<R�
)z�Z���7������]����IU����~�W��*Z���C����T}�%����\PM�fjy��t����G^�0�u�;?�GH�����HZ�:yY{�^�2U�#���F�B��w����u��e��4�w,�X�}q|�<��!�&6��pZ�zS������S�XB*Mj��G���>��Z[��L+��RhR�{��U���v��XT�T�������MR�^o�
���4�{��W^MU�-�����
@�s8�j����p���
`qRh2�|.�����e�
�	�4��I���{�������*��CH��<���a�mGRU�g���X\�T����T"[����
@�,���7�.<��T,.B*Mb��g�}����Z������
@��s8�j]|���]oX�*�l��]���?���U=zzzJ��b>�
@�Rh�?s,���'RUk���i@Q�I�l��1�����'�����&P�f��
@1�4���G�*_�z!�"�� ��(��O�+>7/��g���8�T���IB*��v^X�|i�(���Jwww
ccc�}}}�������L=�����T�sP,B*����x2<������e��i@m��=������@��aC:2n�����U��t�l&��^��X�T��=�����Oo_sV�(����8A% �?��z2������*e�x6�b�
��T���x9����SU��b��{wZ���m[ZM,N=��s�������2Y&���A�2�T�$���
N2E��MH�����J�Q����B$3��T�N`������j�i*�	�,b�{��U���+��v�
�"���8e`` �����3�
��e���@�td'�@��
�"��k����{%U�LQ`6��M�>�������X�T��LQ���3�5W.OLO�����7U�&
��%\R�~*	�,B�}1|u�3���m�
��)����6n����J�Z���i5��I�y(6!�Ehp����u��%��`Jq[����R0%>���P��>0B*������[���������&��g���*,8�n,2�{���_NU�����Noooi�J��2b��T��������p�%��
&�m��066v����W���R�����
�W�k����	�E+�Zn�������M�JwK����att��^�zuX�D&���{�h����=U�~�'�*m����c���i��5k����{=���F}_���kW��qc��


�l�S�3�-��Z��0e��g��Or�%��>B��_[D^[��������;QO�����kB�&_���x���Iq�&������Q�.�8�u�����LMv���7j�s/$!(oZ7�������_>��jg��4|�����;��J}��Z�^H��/���SS�B������d��_l?��}��.���`��k���Bt�)�A��<�w�d�Ql*�G�\�A����x��d�.�w6� 7�� ��WF�������	*�K6p�0�bl��6w�K�dG�NG��<�;g�2�;s����`�
��
��S�
��7�RU������NM���HLR����C�i����L5�����<I%���I �{��7�����W^yehoo/�������0<<\Z_q�������"s]@���-�����ZK���*��(��z����W:^)~�TU���A��sW����d�f=7Ldp������ug�0�&��d�$S�|~���������G�[y���(��R��=<�����y��=�r�}���W�+&;!e�����1�Qi�	,�����;w��y��d5����`q���g�==��Z�� O�a&N-�w�����y�����l�e��Y(�$GH����B�3�55�w�L4=${</�Q�g�M%i�s�DvL2E���N
��|n����;w���e�I���t*�������V��J����;��m��-�&�
��fH��T���e�������4�� ��/OR�6E�i�n�$;�${SR��z@Y���S�V(!��g� �����K��e���5V���0��=��*��~�H�'1� �D7/U�F���l�$+~�rB�t{+��J���E�Kf:w��TvJ�t��f���i�s�D&����7��sr��Z�IT�vb@���'U���+�$��'�y)����q��A�x<~���~�$�R`q���cll,����2��������t��������~eZ@���rP�r�k��G[[[U@%�j��4�D��;���J��zN���	�0c��e�ce���4������n���`��l��%�������|1244��q1����[��G�dC$Q����$�i-Q�������'���4��
3�mh��m�������=Q����X��O����u��L�T�n?#>o:��8�7;Qe"�y���%���d�������0;{�U�n!f �h�a����Vb?�)�y3����(q�������^�Th,!�-T�ST���������U��o;/�X�,U0}1$C(�C%�G����z�Q������z���&����T��1��x��ysZ�_�������'�cO���Z��Z��
S�����F�����{$�ju^tZx���R��^{��/O��9!�(�G�N��-�h����+�������T��<6����[n	������_\��
����-Y"��~f8����LU�{�a��NML���Qy��Y�&�����C��Q��U�'���k�]
^s���022RZwvv���������Caxx��^�jU���(���\Pm���i�_3��^H<R�
� !�m@%�K����'�����Y��h���/�0�,�O�y!~��TU[��-�]�"�z�&9�4=�.���B��k��j	�@-!���x����jB*S���x�E��TX�T���^J�Z�\s��
, }��_�����.�b�N�������[S5��s�{"I����*��$
h]1�^��c�������?�>zO�j��/�!\s��T��\�PH+w��C�&�4���*�$�6m�'�����L��Z��f����^k�#5�
���-�����a��TM�\�?�yA�f=w�i�@q�7��������i�����]�k��IU��/;=����9U05�;��G}��Z�^H��/PK�j	�@5o�C-�T���Z�k-]l��W@%��ysZ�L�f�R�y���p��&�D=�W�@�R����(�5PZ�LWWWZ����n4w�4���#iU������u��
�O�$�!�B�*yv���V���}{Z���?C���b�{��V�,��W3�uy'�����l@%�IR��E___Z����n��?_��R��Y�zn�kp������{{��6EhqB*�����'��Kv���7N�����+M�eN�����m��~��yf����S���T
l���i5����t,NW��c"��"1�Q����u6��n�i.�zn����
w��\�j��~Z]�k����	q�Z[�����g����j;�������@�&���n��o����M�6���x�����att��^�zuX�D&��~����}��TU;�����~c}�`f\�P���O���Y�V�V������4����I��������6�����������:t�P.�W�Z:::Jk(2�T���Z�k-]l�]nd'�L$>o&A�f=7�����0�w��~LQ�BH���[����[���N�a�8q$+*=o�������G�����.��Eg����5jZ�1�P��X\~��}#����������o���S3�z��0>��l���@-��@5��@-�T�����~�7<;a@%�6E(!�y�����g�6���T�>!�y�������6qHe�)*@�����I��D���FH`�=�V�6���p���SPB*u���O����Z���HH��&�����N	�z��T��
@y�X���O��V���iP,B*u�c�)*��~��R���=������v^Xq��T��
@�����p��c��e�
PdB*u28��U�����T��
@<x��p���NU�nST��R��{��U�%m!t�_�*�bR���B*1�r����(&!�9���O}%U�l� �0g�{�U��]~Fx���SP\B*sp�����y6U����P"�0�{��U�S�-��@"�0K#�ca�$!�������y�`�b@����SU�V?'���d[��e����KNKB*��������}.U�LQ�&�0�{�U�3O[��!�JB*3����I���^/��%�0C;�	�#c����neZP&�0C1�2���;;\���TP&�0�8�l����SU�{��~�������iU���O
����TPIH`��y�����>��Z[LQ���
�4
�=�V�l�01!�i�1IHe���	�89Ud	�L��o?8�b�jmY�2��#�0
�m�s����w���T�GH`
G�9n�����e��i�D�T�0��pZ�R���
�&���}o;/�8cY����
�$�n2~�X�ju�_�VLFH`;&���y�i��W��*&#�0�)����RU�{��i�T�T&0��pZ�j_���L��
�&��'TN;�#ULEH ����xx�����e���R�1���_qFx���S�t�d|����{�IU�-�V��%��1x��STN9��V?� �Padt,�9��Z1�����*�KH�B����H�jm1E`V�T*��;�V?o^}f����S�L�$���h����Dz��L+fJH �q��ST�:}i�����*fJH�5/	�{����u���!���PKU�n!�9Rx���o���ug��V��*fCH(�o��l8������~eZ0[B*@�
�6���g��}�9�`��T�B{��W��&	�lYw~Z0B*@���;q@%���V?� �����iUk���	�8)U���
P[[[Z�W��Tx��K��e�h}�$GH(�������S�;_�"U���
PH��>n���T��6E���T�B�l�Jd���R��K+�bzux�4=e�GK��X�L�����p�KS�}��RZ���?>����/��7�w�~9�����������G�y%�	�����?!�%}�3���o��C��#/��m�c�x�y�i��W�UZP?B*@K����J�R>������cU���;�/�+!�e�`����;����#35�z��Y\���
�����O=����@3��kW���mmmU�����#~n�&;�\�@1�@���--Z�|�������1���`��I�l��1�����'��s1X�?1�2����m���>	��R�^�|2�-~��������k2��8�������Ub�%/��'>/>��T��������~��T�Q��x�9�_`��*�������PiJH����W:^i:A���l�%�����m�@��
��n��Si5?n���i������j\��
6�#��n�Z:&����������;{��N^�8�T����y��z���``.�SN�����Iv��DSO���*e�x6�b�
��T�����h8�������#����e���i5n��mi5�l�e���iU-;
e��JY6c�
��T� ����
�u<u����_O/�����*D2qJK�����������j�b���O�B*@�z��+i5�*3'���%��{�!;����+�&�u����jB*@�:��ei5��:}iZ����)?������1�)-m%@��Mk��������
���'�EO4�p�|Lv��	�Mki���y�i����;P�����V�q���������`Z�-tRy��;.@S{�V�������B�NQ���n�3!�4��x��iU�]������)T�ST�m��V0��T������?����*iKg��K�y����T@s����������7������0��jM���o>~mo��)����\`Q;p�@-�W�^�,i�L�_�|(�����U��ar��3}1��{���T��T���j���i��5k����{=���F}_��*q�����T�������Y������7U��g��Or�����k��-"�-`�wW�wE�=�w�]Y���~M�����H�+D
h=B*PE{��n},���:�����w��_�w����\�*X��T�8�T�+���B�B�}a6���g��C*^x�>	0'�5��^k�bMk�Of*�4�'i:g?���u��
-a�����sgZ�	�=��ws����$(��LV���g����;'����gJq��_��+Th&�@q��R_��h���Kq�5�R�3;�`KOOOL���Y����?#}��{�
###���W^���Kk(����/��W\qE���(���\P-�g��k�����Gj^!j�@�R��(����}1����#<����|����O}���	*�L�T�8�T��Qa�F}_�)�������[S5��s�rHE��<x�xH���SH��;t���7�W�Z��xx�����L-���.6�T��p��~{��J��{.���o
?��KC�E���������<ZE^@ehhhF�����R��p�HHh�^-T~��t$�������/)m��s=��?��o	������m�(R��X�����y�
&
�l��!U��y�������P�����#�4�?�/�{��T��Y~����"U',�XV�}R)�?�ZI=*Qv�JZ�LWWWZPt���B�G�	{��t����������R�Q��J��;w����o��V����@sR����{���z$U���jy���W�
�#N8�T�������K�P���|���+�Y*��T�E��v<�f��T����S������%m�C�����j\��R/�-6n�8aP%���d�*�wr���'�;-��r��7��7m���������att��^�zuX��y3����h�s(U��Y�,�?�|M���S�(�V�������?�BX�fMZ1[y������Kk�ST*C*���a������l �,�=�8!���N��{���Yl�I�������Hi������Kk(�C������z��U������"s]@5�������Gj�!$�z4_�8Z�M�����_���_��,�X�����X��@��@qhz���
�"or�\L�����S���dbXf`` U��>	�R�j���Z���_3���Z�����g��ST��_+� N�d����[�GHXT��4|���S5��?yUx��W�
�'n�����=q�H��I)Y����������R��)�����������?��+���q~������z=�+~�8)%�����
�����
�(<��������KG����/
?|�E��Y�
�������y!�����0\�sY�h&B*@����7<��|�}�y�W~xU�h6B*@C���:�n2U������~fM�hFB*@�����/����S�o�����_�*���
�����_�|8U�.:��p��W�SOjOGhVB*�����������S���S;J��g������T���;�
��_H��n�������R@�R����q��S5��[�z�������G_7���06�L�_�hg�����
�V!���'�}�Py���t$�/|���{�uA�h%B*��:��h���}���_NG���{.?��KR@�R��?|w���GS��{����~���	����c���|��T���fE����T���T�y�??yo��mGR������\u�heB*@�}d���o�IU��/8�Pi_������T�����G�M��*������[�:}i:@�R�f����7�R�oi��R@�����#��
P{�=����O��n�nmx���S@Q�s�������;U����*��
+R@��s��/�*/IG����_��;?U��
0kG_.T�<},�����4����R@	��v�M����_HU������z.KE%���o��q��T�{�[�
����R@�	�3�;u �r�������g���3kS@�	�3��������NU�5��>p���!`�����T�����K�SOnOG@H���=G���}��w�)������+NJG`��
0�/��T����'U���.9=Up��
0�;=n��o�jb��[����T@5!`B�=�b���}at,����hg���sS��T�\O>�J�����3���������<|��.H�R�jkkK+�|����twx����H��tQ���]�*���O�p�T�1�r�}��*��u��_��+S�R����p���JU�w�aE����*U05!�����4�4~��]�v�����y*����\4�����S����v$U���ry�������RaJ1�188����a��7����t��x�x<?f�h�s/���������>���GR�������_�v�+���I�;LL*TbHc.�����G����Lki�s,6�������R�o��J��fJH�I�5��7�ehh�t�J�����>3.>:�I�������x���>��|�m��J���!�B����d�l�%=6l���q[�n-�4�	&�zn���������������:\s��T��	�P���Ov��Le���=���l�c��$�zn�����7�tw�&�k?qUx�V�
fGH�*1`1�-~��SE&
z���www�j��$�zn����'^�i_x��H:���}��a���S�'�BI�����V��SB*��W���/�&�y����7��Y�
�X}q8|��������#�~�;/	?���T���P����)cccSN������j\WWWZMn���i5�f=7�b*~!U��������uy�`��T�����>fv��sgZM�Y�
�1���{�MU�������~�3UPB*�)s��Ri.!���j�s4��|�`���O�*��������M���
%�
��
������)k�s4���������X��]u������N���
�������c�(U�.Xqr���W��NnOG���T�������G{_���zr{���k�+NJG���TXt6o��V��������{%��_H����������c����jQ`===app�������d�������~MY���MU��c�������/�U�\rIZ�htt4x�������02�N�������'�
h6�z/[�D�ZY���f���b�*_��-�k�F}_�Ve��=�yOZA�<x0���������������C����pi�j�����QZC��.������J�f"y��l(������h��#O
�����ST~b��m��������9��>����:hB*,:;w�L��k�sL�s/��*��8~��D�o�ia��MP`���*5se����Bo�S���zn���s7Z��k��6��V���h���o���}.���/����H��������8b��?��8�V���C���i��j��j�����j�.���~�����f^���|i�s/&�
,��{����3e@e�;���������h�G���{xx��Z�N��r��tT>��H����~�/�	_���T�{��W�_���RKH�y�y�����]�v�����4����|���c��T�{�����_�*XxB*���[��������������:�Y�
0����O��H��]���������~��8��bA���3�&�}����a����X��`�>��G��<��|g��4�p��a��e�4��
����/�B��'+~>>������������?~���*_�k������	W\pj:�#����n��q��	�x�|����i�s��W�~&��G�N������o\�<U�XB*���T�����O�x�#���G�����i�s����7~x_�&�3�No��?,m�=���'�����zzz�og���J���������h�s7�����
�=�yOZ���'_��w��OKG����N��tJi�z���d�L*����������m���i��5k���jk���j�i��j��@����������3�����PT�
������U�BGGGi
E���j�5S����b3�bp#;�d"�y3	z4��f�����?�o���O�����o9-U�x�� �n�Z�;,�9����x|hh����j�s��o��y��T�����/|������v?P��B��������|"U�6�����g�������p�Cq[_���I���~��mM������L�v?@�&(4�����)*o[s���
3�O�p�T`����?���KU��.9=�p�����KH�O��H���JU�V�n�nm8��wX��T`����H��O���|���^
�\x���,nB*��|����o��=����������S���
,w��\����S5����k�����*hB*Pmmmi,F�~1|������h:��_��������*fC��1�T���:�J)�?N��������T@sR�+T�$�������y�����#�
t�M����>��|�����������$�
�[qO��]O�*�;_v���W�
���
4�~���c��T�{�g���:U���T`�}�s����H��]z�)��_�-������/X@���������|g���P9g��t���
,�/~����?q0U�������:\q����!X_��L��vw�&v��k��V-O�!�g�<�|��M�R5���V�w_sN�����<z�����7�/�<������wE�Y�2U�z�T`����H)���S���|?��K�����T��������V�8[�v�
�����T>�9�q���
����i_���R��{�uA����<U�#�KS5;1��q��������������
R�y����������*��o>7���v�
`~��J��E���N��7�i-�.!�����C���x"U��z�Y�����
`��5��7�ehh(�����������7Q�,!��?x |�K��*���O�^@�_1\R��&��K�l��!U��n�Z:^i��W(!��O��H���>��|��uR������S:���+o����2S�i(y��x<T1M�JB*P;n;��S��*�)���~��p�9'�#��!s���,;
e��JY�|www�LS���
���w=~�c��jbq���KOO@}��)mmm5��8��282]�|�����jr�7oN�q��P&�sp����?|w�&��zMXw��������L�r��Dv���V�����jr[�nM+�&������J�W�G��|���V��o;/U�/NOHU}�6��s������T`�>�j�������{%�w]�e�6\�*���)s��Ri.���l/@�R�+T�{��t$�m�0���KS0��N)L���N*�@�	�������KU��|�y��pU�!����/�	��|*U���ug���Z�* R�i����/�9��|����p��W�
��6o��Vp��
L�G?�P��NU�K�?�P9i�_�����cl|y��X�!���|�����M�6���x���w�������w��K�o��5��OMG���att��^�zuX�DhZ���c���i��5�����C�i������'�����a``���L���t�����?����j���W�I.��b}xM��E�����*����'���+ ���������3�y��A�GHf��o<�������dI[��_zCxS���Hcy�����!�R_y�������B�T*�_��~�+�$^x�>	0'�5��^k�b�����)*��[�h*�X��J�m����c�
��==�������F��|������+S�8����z��0I��5��Q���LR����5�$��7���k����022����+C{{{i
Eu�}��������+��5�����l�~M���Z�H�+D
h=������=��@���z�Py��c�H�_��+�����T-����p�Cq����
E4��J4�������\�>	�:x����Jgg��
�w����o��Z������T���Z^/D*�tl$l���ST~b�%�2�P/�7oN����kWZ���yh=B*P����w?p4U���]��=��
�5m��5��������tuu�E'��o~d����S���7����v�
�8v���V���}{Z���aCZPtB*P�9����_
_���������
7\�6U����/�B���'+~>>����}��R��nx |�K��*_����~vM��!�����'������l�@%!
��oy$|���*�yg-7��������@qd��� J���)��T�����JB*�go;��O���|'/[>p�����NIG�%NS���N��������Vz�pJ�+��g����
�t�����'U���������*�b���2����|�R�p�u�������jb�������;;U�'�����B(��*Q<>44d�
R�P<�R���}����t$����U�}o;/U�-N6�������B�<_��o��!=j	�P�<�j)���s��#����4���S���
�02:V
������H��xa��-��
�!
��7�wz.U������/���T�$�@����<�t�S����ug��SkR���
-�����0��p�����3���:U�|R�e}���������w�y����:����D�O���%������*���-
7\�6�w��t�/B*��[��D���LU����R@e����#�|R��|��g�?|w�&�����7w��*`�	��2>���*�����xN��� �@K8���p�Mw��/�#�~�{���+S,!��K�F�o�;<��K�H��������8U�BR���-~�=p4U������=��
XhB*4�������}O�*��7����u�
h!��������k��*��;��m��I�(B*4�����h������p��W�%mm��(B*4��z$|d����w���J�3O[���$�@S��W�����*�IK��\u���S����Th_�������S5�Py��g�
X�Th
���h�����jb�?�&����!���T
�{u4���?xe�����*`1R`Q{��WK�'�}%���[.
?���T���
����X�������������
���/K�	��h�����KU������/~hU���JH�E�������;�JU�uW���O�I��	��������r8U���������N��	������
���*����>p��p�2���Yxw����h8�������������/?��?���|�O[Z
��w�I���T�7���h��g����������3�o�����>�:��������;?��2_[[7\�6����thB*��}����������{(|��t�Z<?�w�Mw�������[V��*���PWw��\iZ�G?�`:2]c�=���9�����tN���
/��Bx��'Kc
���T��L���G����td�bP�����=W��~��T���O�o~���3��L�������O}*�>�:�����oB*�E������U~�=����T�u����;v����+<����h�x<~>>/>`>	�P����j�jv�i=��2���]�j`:�����;��3�����u�����
s'��~��$��ST+�X���t���g?�����O�#�������~��_�~���R`N�}�����=��9��_�;��D������FikO�f?Fy��xU�zR`Nn��Si5?n���iL�k_�ZZ�PF��D��;�\�0'_����w�oZA�|���g�����Y�BH�Y{ux4|��T��x����TYO<�D���;S��}�LT~}<o��/@q	�0kO}5�����}�=���i5n:[�L&�������
����W�j~-T��#�<�Vs���GH�!f��3����:���i��&/��Rx����c�=�����o����o~3�z����_�b����J�)���<�������NH�Y;���iUg�7�V�!�@�{��W���?�x�������;��3�v�m�K_�R���?����>|������������|�3a���a����k_�Z������C�J�S�|����/�����I*��/���|�P���5]��jl���-{�{��V0;_�����]��[����o����}\�E��?��oI���,���z���d�L*�*�{c��M1�q������u���=><<����tww�s�9'U4�����*�5k����7uh!z!���@-}�u���022RZwvv������*� P~�j�����QZC��.��~���z!B*P�/��-�|"|r�c��C��#����i����R�tx����^��R�T>��(�����r���'{�����	�/?3U4��G}	��$PKH�y3j�.��~���T��4_���|�p���
��R`>��@���~�W�����T1����p�OO9P�
��u�xRy��h)�����u���{C����F���/!@�j	�@5o�C-�T�����
��352:V����C����:��N$��$������w^~��LQ�)oZCq�z����`I��:�u|07��	=[�:.~C:B#hz���
�O��T��7�������LMH
����i����!@����O}��R8��gr7y�WJ�o����S�fNH�������I6lR~��!������w���w>ykX���>,9�$�F����F�E�}�Z�$PKH�y3j�.��~���^k�#5�
h=�/L���^�z����������-��;����
W.O3!���������N�[�T����t�������#/���/�e�/���:~\�/���.{/���U����>���Thk_�����%���@-!���x����j�5SR��|a"�y����g�r8����� ���/_������<��f���i�~�[���~�!(������M�u��W_}5}e�c#a��x��x���������W_��`J���^26��v:�9oc8x�w�j��k���'v>��pu��p�/,�Q4=��Qa�F}_��>	�R�j���Z���_3���Z���
��Y�z6|j�c��o>����I�������.��uR:�]�>�����}MG�/n��K��	*s$��1��=6�+�%�c�vR>�\{�Pk��������IZ��q|�������;/���^�o���:<{�����T�|������<�����S����(���Q_�
�4����I���
T�f<�r]@5�������G�T�4_(��[O������g���Yq��R0�6\N;y�����*�����Z��'�0wB*Py��+��r|[�l�$D1������R��4�$M7)O��;������x����&~=�-;5��zVXr��'>V���S�>�y����_�>�����2�e\6x�g��|��g��pX�O?:.�&�Q4=�+�g^H�E�j	�@5o�C-�T���Z�k�xDH
@���_{�������������N����i���i*�~��p�]O��������y�i��oX����KST�!h-�i'��I�<�����@�����������K[��N-�LR%m�S��'��[��gV�K�R�$>Be�$}l[zr���{���#�t��.m�����?��A�����c=����*m�s��Ny��HGi$M�����_�^H��/PK�j	�@5o�C-�T���Z�k�xDH
���o>~mk����~������_LGff�����pQx�;�KGf�������W��G_	g��,�8ciX�!<1�T`���N*'�T�O������Zmc���&����&'���OJ�����C{����/=,��KJO9�4���Xh[��?�G�����,MT�w�{f��O����#_,MP��S�M��R�I���
T�f<�r]@5���	���~�7~#����z��}�C
?�s?��j�}�{����t����+��"U�&j\������O�~y�?_���3�n�/W^ye��5���L��������:>9�H����?s,���^uVir���Y���,�T`��Fn^�d��i'�:F���Ifk�DNA�4�dY����tjXr���	&����I��N^���^}������T��D�8Ye����8A����'a�eoI���Q_�zmP��$��	�@-!���x����j�5S�{��]+���s��}������7����V�w����[�����KoP
-N;y�����#G�<Pz�q�w��{��������}.����]�������������d()���/����a��}����>�~�����/&��66N~>,?v$���}��g��x�����[�o��C��	��a�r�����@�r����\��������d�Rlc[V�e��d@�l��&	,r '�@^���c{��#����I8	6�� ��$�y�������m��d=�������W�v����U�]�_��Q������jU���������������>��>���k��K��/�zz����.~�#���>��{����c_���$;/�4����i�e����/�}��7��o��������������?��������������/���~9��?�T:�=�<�����\�#i�U�����mi���)�z�����^4�	*!J������7~�g�K�`:��w��x���[T�	*��E�J�%;��a���J?wR���W-~����/�V<��K�����u���9%��bs'��E~G�r<�x�Ow��~�N���I�h�}'���G��9Q����OM`��-w4���I����'����+��r���{�O�������3�����H��aG�(�x������9�����U�`��$�h�I���
l����q[���V������"&�e���?�n��?I����JP�����zAz����?�k/��,�����C���?�����>��g�vr�}�={����_�K�����g�vr��w?{��x����|�k_[�����S�d���>��w;y2�v����7]��;�k{w;����O�x��?�g����_�o���O����n'H/{$�v����'���<:0Aem��i�y/L�^�����_�����i���F:��?������3�������O������?zo:��L���_Kg����~��Ig���|�?J��������W�����v_�
i����"AeS$��v����YL�	)��?��>����y�J����t%��}�����E�����K��~��z?:�q'�~�v'�aWC-j}��O=�~��/��?��bJ;g����~���mozA:���b*���T��6)���o����~�f�w;��tR���������������M�N'�{w;�}|s<�o�e����={w������������w>Y����,�%��r�����}��t���,�����/O{^~��c�^xy1�y������#��
�wR�~��[�c�s\�V�5����bJ_4D��E�4x I��$���������1�#�D1����������������J��i�<�H�������/��{��z�����.���t�E���?����?~����A��)��_2��H*�w�p�d��(�H>)�Qz�6�����lgm���I&����jO���X�OO��I��z4�8�9i���im�d�E!�1]u}I*�Z$�@?I*�����������S��!$,���I*�$���(��~�����R���=ULi����%���7>���2������h���>V����/O�~���Wug�\RM0���2��i;O�������&�`R$�I'���'�8���O.)�l}om��'�y$�1]u}�����{�~�T��$������q[���V���)}��2$l�j���k����*u_9��l�t��{����#G���\v�����^�����b
�`��T~����}(=�����������������V��)�vR&���N��'�8�"�d��uN'�l&��l{����x���[Kbx���tj��trc��%�L;�<����AX&��5�d�Y}/�o��$0�$������q[���&I��"I��U��kLtw�Yz����]S��>�?5�#��+�Io�����W�[La��r�J�9%�����/�����U��*�,��.�������n'�"���lR<F'�L"�$��{�N%�$��2x����{��;�T����/�L��v�����On��I�:=���P���4���IR�~�T`+?�C?�l%^3Z],$��ECHX�~\��|�_}�h��{7�S�������k^s~��>�_Z�c&�aU��;�����z�jS�{��a�<��-�2wwT����w4�D�j�Iu�~����M6��>Z��J<����}g=�dR&��&�l��Q���/>9I*�:=�kV�"��^��8	���[�1�9.`+����b!1�/"@�"���}�����z�KO���+�r1��o�����S���(���V�G�;��s�G��������W��d�a����v�o����d���N6�h�&[��R�������v��K.I��M6��~�N9->3�T`uzL�$@��IR������x�h�TX*�}�{��~�~���1J��sO1&�2/>�����~�K��?RLig�������������+��j�h���G�L[<�'��Ft����2�dX
���]N"�d��u�F���{;O->5����&���[/Q<ZgIR��!�1]�Tq�'I��c<�s\�V�5�IR�&�2��'N��:�;t,�{pO:���^��8~�������X1������������������[��y����|�x����k�4�>��r�g<�T�'���(e'��v�:�yG��]M"��p/�d��'�[�{&��h��8��������a`����k��7�O�
�A������@?I*��������M�
�0���������Z��K�������^r��
��������+_|��:�=y$���_L�P1����/���{wN����B����h�������x�j���m�o��#�G��~�N/��x���1�w�:V|�������$���-	'���sS����$b�$�����.I*�8	���[�1�9.`+���$��
|i�}����������h��_�n����W[��o��X���L1��������S��s�)0���h�+�����7�m�����vr�p�;e�I�h��{k���Xnm���%��s���A	(k�F'�1�$X��%I'�~�T`+?�C?�l%^3�$Xa�|��{.����o���������+.>�~����W]zV:v�T/1%�����s���/9;�����_��b
4��?ZG����c�G������x����}���M�G�l������O��U[�hRI2�G�l�V��d����lI*�:=�K�
 I�IR������x�h�T`��r����+U���sJ�AeR�y�Y��_|&zf����_u^/9����b�r8y�hz��/���?���sA:p��i�����L�<�h�&	&������c���_����E�c�d�s���I�dr��:{Nl}����n'����30��2��o<fg���2���C�c�$��T��$������q[���&IV�$�ze}wP���G��Yy�7\������/>��������I������OO�<��z�Y'�L�?�;��e�����
�T&5���%�<���������������4�����d�"�d_/�ds�z�x�V=�h�jrI�����&[�����"IV���tIR$�@?I*��������M�
K��o,����}�+�(IR�W�w������������z���7��w��=w1e9|��"}��}���^q�����~��I��:M�Nz����o�>~"=��ZqL;������{I/��T�1;�tr�����D��;���"�d3�$�V�����p�1���:+O�
�A���U�����'I�IR������x�hu����
 a���>�O�J��W�?��������Ul���;{�)1<������K�?}����yv1��sN>������.���� �P��`R��+_N'�I'����G��H�O�J'N����V=��>ut3����M��:�&�l�������S��N��:�$��
M<��-I*�:=�������Y}/�O�
���[�1�9.`+����b!���
���v��}��$�z<�d�_��G�W�;��=��)ozA��w�:��o?�������iqL��!�w�W<�����+^-��q��`�l�I<>��C�w69w6���)'�����
���4<�pz��?���<}'�,���G�����i��6���C�c�����������$�@?I*���������.S��!$,�a?p����{����S�k����K����;�;��5����+?������?+^
�{�xz��'��{�N�;#}�����z2� ?o/1�����;������W�F%��A�\��K��*��������mO���<����(�7�9~*;��������v�S���/��w�����L��������z$�A�
�A���k+lG?rV�����$��V~��~��J�f��XHL���B��}��<_������Z��K�������^r��
��������+_|�����7�X���{��~wx����eg?���;�wn1��}GK��8�x����C�S���{G�{�|iz�k/(�,�_���?�����{�x����&�y��t��'����,�lrd��c���'��S);���%��G���=R��������?����{���l��M�x������~�����(a��<to������I*�:=�K�
 I�IR������x�h�TX*�T��v�e�;����>�~�7>_�Jig:��[{2��v(=�~0}m��t2�n���o~Q���..^MG�������������O�.h�Xr����O�u���~��7�];��G���O�O<��x5=�8�Hz�[��x5�h0��h��9�X:����3O�#�ct��ct�(���������t|c�d2;�7�����&�#���o%��7ms�������i��=i�����������i�������6���)���kO:���M���L�~��o_�?����<�;����<���)��|���C�c�$��T��$������q[���&I��"I��2���~2]}�e��S��g>'�<?��l�����O��}I%�\x��;�|��'������'>w(�l�C��;?�1|<]��K���3���a�������s'^�>t�U���S�.I���`���^�^u�YEi�=s�D��g�L�����x��/<U����z0�����#��+�4��������9�������g������[�Z>_��������.^m���Q���/M�}��{��d��6�>O>��>���p(=�L/���x��'�����S��&G��t����������3��%�6Lzw;��w�H{w��%���f/�$K6L6�+	([��=��������'���K����NO���Z��w��/�7~�g�Y����^^L��"IV���t��W��9���IR�~�T`+?�C?�l%^3Z],$�HRa!U�	9�����O}���Y��q�����M�u�H���g�8�����+�K;/|U���?���W�9�}^z��^����������uo����p��}w>�;�D9������k��h��/I��s�����M�:��W���>�������O�:���������U�(�R�����O�m���~�������?\��r����������������Y�Z>����!��3��W�������>�����E+k����n&�;��:�v����(���t"���3���;���7��8�w����H.�;����$�����8~��������#����Z1e���5��
��O��������.������C�c�����&�����'I�IR������x�hu���"I�X�p������� ��;�X�w�+����M�w��8��#��k���$���#�������L��=�5�?�E����X����O���''�\�g����Q�MRJU�A��/����$?n�S�������u�y�.��TN?��z,��^`����w�i�������������#�;��.�v�����{m=�����8�/���/���/�>p������Y������?�7�'��>u�����b�������GG��/{�����O����L�
�A���kD��k��^��$�'I��c<�s\�V�5���Bb�$XB�?��t��������&��?�V�M��t�R5��M�zM���?�^�����?�����f�Gr��7^��{��O\���%��~uz������<�:)%����]����3H<�����7����1��5_?�8�L:z��t������O�c�<��nL;q�H:v�H:z�X��y���t�����k����t,�L'v�I'�4<�)�`R�����k���]ki��i���i��H6���8#�.����"��`J���?�����:��C����c�/���9������i���)���$IV���t�H�#2���IR�~�T`+?�C?�l%^3Z],D�
,����?�/���c�H;-��J^�>~���]�?�eL��A������kg�2=���b��6���}���]�?�gR'~8����������}��'{	&�m��JG�y*�%�MG�I��O'N�H�N���Lv��LN�ud��[v;�O��f���I�.'��+�Mv��;���={����z	'{������yW���t��&q��q���?ZG�w�
Q���cS�/!�����7�L�
�A���k�nG,dV�����$��V~��~��J�f��XHL��K$�$������M�K�HPN���5��?��|����K�/�l��0h���;�����W�}�����W�����_w��&�4L�����K����W�{����L�=�D:���t2�l������������O�'����*����t"�L��vm{����4�{d��x����'����g�z��s-���+��h|���7���?��F�w���������8�'�t�1��W��}V�G��NR'��c�D���������Sv�S�I��t��/�]/�<������
C�
�A���k�G[�k��^��$�'I��c<�s\�V�5���Bb�$����K�������)�����t�u���n���2>�c7���W���%a�M��d�A��y�������&wi���>�~��_,�����b�������v?����i9p�P:��d:vj��$���T��\��K�L���c�T�������~<�Y;�v�������3���;���'��w �9��l���sz�&;zw7����Ei�������*qg�R�A���3���a������!�~S�;��T`�8�auzLW][3��]����4�~������Jw�yg1e�m�������t�U����D�
���[�1�9.`+����b!1E�
��<9�Nb�!Y%~����#3}�O��|��W��������zq1���$�_z��NO�>�7>��&3h��u�X�}�H�w5I'{w6��#���#�dO�S$��=pF��Yi��g�=E�I�;��\���O�����o�c��7���'����|�����_�v���zwQ�E�x��!�1]�J����4�E<��zk_rJ..����[�>YE�
���[�1�9.`+����b!�TXX�^zi1���,��w�
7��TE��;�(^m��������������������k:�8��������/|���������h���T��s=��h���#���#�m4������b�~?�3?S�1��<y(]�MW�}g��v8;���_�� ~���~�xZ?�H:���i���Ik�Ok;w��br�������>IW}��Y}/L*T��z���M�{��s��"I�IR������x�hu�Ql�C=4p�T_���D����)U1�yeV>����l�J�:���iu���D��ITB���\��i�+�HO_~Mz���/��-����%=��k�g.|s��s�T�9����R�O��'��'�J{O.&n��>���v�3_|Y�u��%�0�HH�q�zwL�%�@3q���P'�n�1��V�6��� Ij�������>1��������#_-���%�4M�fR���u�������W������H�N�L{NNg�<��9�D:������'��w=�^���������?Gz��Hoz�����_�nx����o�*�������wOz�������;]����H��>.z���1�[\�S����N�3�iy��,/�`>�/�}�w�C2L���'��#�RM6u{����q��H`�N��_�������b�i�-�mU7�N����w�wc�������n�v��~"�:u,�I'���Si����a�7���{c��v�������v�;�1����qV�{��i��s�������_�����&O���+�<�^���/^�����p���p����k�G[�k��^�Du�m���������{������cM������.S�z�$,���4�>�����Y`�|���/��o��Wl�a����o�6��M�T�MO���}`���i����+�H{zI&)���4�w�L{w�N{��I���I{{	&��3�L��L{�<��d�c�t�3M���?��yv���|�8��������W��GkX�wX��U�/��~�����_�����y���	I*�O�
l��x������kF������h�	��n�.���H'h3M����M�}�O�W[�?���iu���`�O>����/K�L{�8;�>�����yim��5�������}�x���>4h�k_}Iz��W�h����:��:=���������{a\�^�������KR�~�T`+?�C?�l%^3Z],D*����bl��W6��|�W}���b�_��_7�N����VPj���Y���e���Y;���&���oHg���i�9�]��	%�<x�x����b��x�}���n?�r$��V5A%��T\�u$����G�A��\u�U�X;w�uW1�=�}���~u���6����Jzt����k�����y��?�������^��K���������.��MoJ�]w]��o������=����L�~�K�Oo�t��_��Wc�������P�Q=Ue�J�}m��\|>�`~Dz\q�Z�T�b���6WM���w^1��.`P�@��|���<sp���������������]-��/�#��oO�]���Mo~K����1��W��w��������~:��s��z��{����^q������I=Q��.{C�ju�P��{��v���s��0{�$�4�3-�G�
T�{�\����w���v�h�j�Mzw���/��i�������k���}w�h�z���E��,����xiz�G�Y'�,�n������%+������D�������Kb���wS�Ki�,2���I��������bl2W\�_N�u/������U��W�>��m������`��w���������������g`�HR�%q���(���%�4M�FR��e�����/�<�����������C)1�����W�����������Q�nQ�G����{o:��K{�+�o�V�$���������]~q:����U}�D�d����R��1�2Q�t������~u�j�{��g�Pu�w|_�����=�'������O|.>��������1�J�
���8����������	u�!M�E�H*9�@�]3�|O9O��
��M�+������C�g)�4����A�^����+�4~���
��Ew��y����;���x5�����n�����������C�O?v��_,W�����i2��y��<G�M�*�����cG:u�T������������c}rw�}w1�o���O'�|$Z����}n1��3�?��K;>'�:��]l�e;������)���������{ay���{�mu����~�m���n��x5����C�f��X�;����u�]��l������3^\��xFEP7-��!����W�S��={z�F������w6�e���+#����Q�v8y�����W��^��t���^x����'{�����KP`;���?=�9�)^�������_�\rI:��s�W��iO�
,��~���K/�2=u|m���$���"M��K*�d�.��u�I��������t��y����7^�v:p�����HR�A����ey��6T���jPbDw�����w��o����g��G?�_�K;�7^&�T�M��t����_�^��W��=����_�Box�������{�����.J^xa/�e\]�+����#lG�`V�����\}����v���?;����{u����
 at�x0I�Iuy�&�t�C�|���<�?�p:s��bjs��>��m���/zY:p�s;I*9u�T:|�p:r�H��o_��X;vL�fO=�P1�/��V�$m�D��o�=�|����v	.,I*,�.�T� J�r�
���N�/?r(=��S���������_{$��������E�y���������g�����7�����.IR�f��l�X���m��%I��r���c�����b��q0����%I��b�I��������+Q
�r�]wc��z����&	*����tF�y�wQ�����1��}��)���
������U~w��"�:�<��8dfC�
d��I<�gP�JL�>�'\y����b�/��G�R}�O�G�@�$���
%Q�9��HN��y�J|��~�e�_�S&�T/��I������b6�m�������M��S\&�|�OkkQ��S�����#lG�`V������(��{�yV\],��T`�H8���2H�'AXF�lI'M��HP`wRaa����/����]�*��#n[{�]w��Y%�S���J��9�N*@�;����#G��}��W,��������n���b��HN����������HRaaI<�)�
P%Ie���������=���p:��s�������x���=�yOz��_�~�G4}�#)��.�V�.�qg��c��}�r'���;�TS^����w����5�yM1Xtu����
��-�@�M]z���X�|�V�$����TJ�U`�HRa�HR`�TfkP�JI�
,I*,I*�C��l�JR)IV��&I����������w����$��V�$��dXL�T��'Ie��&��$��b���<I*�5n�JI�
,I*����2[�&��$��|����{���K�'�'���������s�I?�#?�~���7�I*�J���X.�U`�HRV�$��!Y��$���7�X��{���W��$�Ut����_��_K�{���)�v���R�x`�J�
����kW�������?����/}i1�n�TX*�ThJ�
�����E�
KE�
MIRX^�S`>IRa�HR��o��������VE]r;�����ys�W��|�#����s�9��xGo��o��b*0/$��T$�0I*�5i���Xu��������������;���
7�����8&�cs\]��ho�q3���x���F#�q�������
]�����H?��?�~��\�
���`S���q�t�eQ���q�C�Lb.��q�E�c���Xa��'���+�Kv�����������v[��]7�|���{�����_}1��.�wu	��t�u�I����x����X����M��y+{����v��������5��b�p�9�������>���hG�[���MXQ���t�����f�T�Jm�CM����1�,5�'�CW���u���1���������;���x5�F�������]"�����.^�w,vy��K����nj�s�������`�w�m����X�.���M\����X���w�4}����0
�>�Ww>j�o,�y���,��t��P�
���o.^�s�m���n��x�g�c������q?�d�R�+��0�Yq����W��R0�4���<��%�N]�&:u�ML������q����2�1R=f��;�����[��MQ�"�
���>L�������i3�������f������m�u�]��f[+?w�������}�.�������u}��=�s�h&R��������J�xg����l�8�66}�`Lk#��4��|~u	�������N>��f#?F�y�p����������?Oe/�����v�����A���X�M�������?,�����>�a��:��C������Tm�
�c��1���F��m��|�U?W��X����7(F5$��oY0�y�{����������.�v�c����w�;�w��q;��	��c�:_��$_�a��M��ET]��\�Y}����T$����~���MyVA���M�%}h�I���M��U�����wy���<�3��u��~������4���
����l��q�m:��g�hz�W�6_�*�%0H��GU�s����<14�x�nT��A�l�i���$(�4��e����n��f����LR��B��}P���o���E�����q��	}h�M�w��2�[����6��Y��m�����?�������+��V;6�,����6q�u�c�<�/�����t��7S6m4~��g�D����&w�UWc�s��|���<V�caR�gx��n��,�o��F���aO=�Tz����~��~/����x��o����u����;����g`�,R<��
7������-��8������_�m�f\����r��$���^:pXEyC��+�,��k�&
��h�G�����.�wu	�?��q=N�k�;����c��������$l�I����a����^���S`��}�/���i�"��`;�����Q�v�w�Q�O51���L���r��$�C=4p`����J#`�����uy��K`�8��a����q����^��42�U���O�.�������c�=�}�m��q��I��`�-K,w�IR�%1I�%����i5��<��%�}�<�~<q��b�[)W
��t�@G]��e��J����{#F�K\]�E����X����`�����4���wg)��q���H~n�����`��}w�c��F�
,�qoc��o�Ln�Y�]���>����~q�������g�n�:��`��euY6��R��h�6��_R��[)�11��Q�"��`;Dt�w���8���1=�;q������u,w�HR�^<�|���w���2�����+��~������%0L��?~4�$>��!�$��/<H���,Q�E!�;}�T����w�k��-:5y��-��R��hX`-�\���H�-�R1-��7~PtGF��G�G���8���\�y$��
I*��g�����]]��uj���E����Cy�XW�3���W�H����k�������|�/	�����
y`+}h��]w�U�m��8�����LL��N�)�y�,��y$I��U����yS����y�qS�?Rw����?�D���q0�+�~`��J�c���P��I����w�cn�����" ���Gb�������yC�O���E$��?��z�������G�F��R��,�x!�%�y�i�+>�_�q���c0�r�'I��%���<��%PoV��;���*��ag�W�M��\�e�+u���n'��>4lu��Wc�#��h�,[,wIR����S�W������+�6i�0��n^������>�T`I�W4p���^;p�E����.���g�������M����!�J,7��ItY6��R�0m���m�c��F�
,��Y��������{���<��%0����������n����U{�wX�n�����|�Q?�vY6��R��t9.Xu��0]�fm�b��@�
���8�����M����`�ty��K`r�:5��L�p����}?��������I�S?������.��W�>���i�a`��CC=����r��$���^:pXEy6����^���J�]����4;5���%��������`C�B�t��7c�����������$���}�oi#?�$o����a�8���6�*��#y�YY�X����Kj�-�K��zk1�I'O������v�����G�<���I��I�/���;M{]�
0��}��j�U�o&?v�	`����Yuy����������a�c��N�
,�j'1*�Q�<���	����]]�EV|�8���S�P8�a>������7�Q]YyP.P������l�y��������wS��w�`��C�iy�6.����mQc��D�
,�<x2�k1=����������.�v�8���Y���x�������Pw��r�-�X�<�1������Q��.��W�>8-o��:&B�q1�o`Q�C�V�����#Cb?\�X�2Y��7GO�L!�w���AVy����r��BY)F��]6��y�����~�\rI1�Q
�D���;���7����.����%��qo
?��q�����	q<D���&����8����>���g"hW��l�.6/�I���M]r;�sV��L��E%�!���T��u��i�fe^�q0+y����o�Gu��=�`�z�l�cP��\�U],$����t�Xu;ti������00;�t�C����F���6���G�c&4=n��W��,{�HR�������~4i�q�2�������C�l��R�:?>[w��(�fa���0���.��X�������������J,�����4`quy��K`�8�a~�9fB��&����(1O��a�e�+ulW�6=&B�?�8.�4}h8-�m��qr\���+�s'Xr�n[^~�5`�Mz�FU������{� M���w�qN��5?f�}�[��U���3Q�<���IV���}�+��A��U�(�8�.��%�M��G�[��e��.��X�$`�HR�^],��~��$:'IXzq�A�C�
����o�����7	������b������b6��2��VO]a;���^�Y���HRaaI<�)�
P%I�{u����s�T�\�[����n5�"�����K��~>�`1�
���^],D�
K�
������X����9wRaa����-������.�`+wR�^],D�
�R$�t�.�q?tN�
���`��s�=�X�k���V���t�."IXzu�_I}�G�
@��b!�@��I��u��7c�����c�N*�V�����X�$�����U�T�W��:'I����U��+�V�,�p��o��������V���t�."I��%I�qHR�^],��~��;�������b��5�\S��V�����X�$`�HR�^],��~��$:�q?��{�{�[��{���]����~��q?,�HB�<y���j����k�-��������31K����k?�=��S����K/��L�X�~�y�{lI*tN�
+��Q�����b*�j����������^L��U�F��b}��^1����+�b�����}��.���X��u�A�e6���T���&I���~)����
�����c������b,�+����w�]wc)]�������Ht�o���-�^�iW]uU��I���`T�,�%���X�t����$�K�mo{[1�-���i�����+�Kt������]��+�w]�jR]�
�k�Kb-�*I*��F]���[o���*��:0��
�.tY6L�X�X�4����$�����Vv��*��n��[l$Y__Ow�q��`b-b-���m����E�X,q�D���n��Iy5E5�P]�����.���#����v��f���x��n����m���<�����
,�aW�4�
�Mb-�!I�@~[�a��H�����1Jb-���K�z�N�V��+����$��
I*� �
�xv_�P�b'���)�����z�{�r�}�����n��]1���\�����^Q�$����(��T}?���2J�Qn��;b����3��u���(��N�U��{?��x]U}/W}���QeS����k�����2��������6��r=�e���e0Z]���Z�Z��~��]����_�]����W�~�Q}?^WU��U���a��/V�eT���_�=m��U����l�k=��s�m�����=L�����j������gi��Q�$��+���r�|mD���ue
�|O���P�6����/_��{����������6������6�&�_j�1Lz,��zs;��{�zm�Y��4���v�3��+���r�|m���W�&�S���.����r��m�/c��QCn��u��/V��m��m��6�C�<4WW���
0uq�A\9t��7SF����3qu���ek�N1_�u)�W�*���|O�����}� �bl�m����o����f�+�W��2���Z�;��������[|b-b-���vE���	-�<��O$����N�m��V���v�"!� �t�M�����c�����v��`���q���M��������d}�6�����5_���R5h{��"�������{o�����W[��R��1_�-Gm�|;���W���"u�eWu�/VE`b���[g��_����VQ]����Y}/PO�E�e�w�a}�a���~P�o�����LRv�.���b-�F��
���l�e�F���sx�S=��m2�P��r���������C���|�i�����-�8�R7���M��
��A�R�����?7J�}���:_�LR�����������c1���3���
[�0���j�z2��0���������B��>�??��w�����!������(�A�6����?�oS�l��#�7�����@��Q��k�l���?�7�a�2��/����A�{�>3H>���	m�����3���%�_�SU��"��`�w��N���lb9�����^1e��� fm��Z����\l��j�(��&��#\���v{�2��G��,�r_����&�'�%W#��i��nUm�A�k����Zn���N��y!�"�0-�T`�
�%i��q�;U��w0�s��s^������R����%�Fu��wf���t{]y����x�u������QA�R�e�o;=,8Z����^�`4����,��6����Z�Z�E�
,�<�T��i����+�.O):xU�vEB����Su���X�r�&���>&1�}�����#M4]��?�N��O���_�k�^�k���?O�Z�Z�I�
,�j���NX�����_��w<��;�]/sm��I::������|4�����Hf�/Nr�U���n9n������
`Y��l�e��Lz��y'��4IR��4�?�d���i��eZ���+5AWA������3�f�/vqM�})n�����leQ��y#�2��9���w�k`�$���v�������hL+00�w��F��M����A���|��W�:��l�e������nM\Qb��R�X��Y�X��kk�&I*� �3��PUf��
�^):Zu����D����PH� �@�j���{r�@J�C�(���,7��k�I*3���	���UPt#nmW�4
��2����Fk�I*�\i<Xq��u�Nr��v[oX__�
�nK�t���XPb(��Q��X���%���X����$�����������|�;Uu���$���������0���NoU]g��[V$1�t�M����}_,��D�@JO��`+���kY��/��Z��z>��z���,S��{�|�b��E��e������-e�0H���U�n1��u��K�~���??���R�l�����|1��������3H��m�.��-�8�`Y�ud�aV�4S=G��U��]������7�&}�a�)��t�A�~������(M���}��<������?�X�x��3wR�%W�r�]wc�������M���s���h��u��c��+-6:����q��`��/��9��,nW�T\I��s4'��=���!�"�0M�T`������U�Nc�����N���}���n������y���vl�>u�2,�5��@N|/��/��9��<���G���`���l?���!�RO�`<�T`��U!MM�����%:|M;���zk1vZ���V����%����V��F�+=�����e_��m�2k3/���l���!�"�0-�T`5���
h����b�����z�	���[k��������G�����u���[����:�����8��I���
�\We/���_��6j?�����yD����kk�Nb-�'IP�C����y�+��{o1vZ\Y���E��bZtfs��u����M�%�G]��nZ�]����Q~l��3<��L�0M�,{Y������'�z��x/�O�5	0/�Zf�.2h]�Z�k�'�0{�������������v���nY�6C���?7m�-�����l2�vf���S��u��7H��|�����uYv�z_�?�F�?
[���^uhzL����r;��{����������ou��
�����O�����	r�l�X��e�S�'������I�!m�����}1�L��4l;����C�c
��z=wR��M�s�4�U�����S������:t^��5]�X�QWS��1_���m������h|�����������,;,���r�m�����c
`����XK=�������$X`m:��xFjt�����e�i�^t�b�Q��y������8�Rv��su��{�����	��#n�:����(;��:�������K��/F���������5�X�t�:������4���|1|.>�+�'�����K�w�����X�l�mqK�-��XeqU"J���+�����������{��X���X�;��9I*tN�
���@�$��9I*tN�
���@��6��������&,����l���Y}/�,��B�I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IR�s�T��$:'I��IRa%���������;)ho���d���zC���:s���}���e]�u�����w����;N����3�ga�|k3���,��y�����_�}�k�Z�U��M�m���R]{3����:]��Ww<6�m��6�v~���������:�C�}_;�������jQb�]�����v��9fYHRa�MZ�u�]��r�����2���;{��7�<W���W���W_������OW]uUo|;�>�u|�����������;z��g5��J�o��;/��6�r��e�g��[n��;b?�������m����cF��7��OV�^�d�i���Y���.c/�����7��������u��8]�:,lwl|�q�"b��!I��v���c��kh.�hD'�UI��0�@I4��-���?mg�$����D�sM���P�8�0����f(�&Z>�7-��� ���[��v����bl�2�2i�d���"�di�3m�l�����|��~�d���g���3m��i�.��"6>�������i��}�X2O<}<}��O�G�8��v�x:���t��{��/>3�s��b��'�A����D9�$�,�Pu�E�[f�8�?��������S�����/Hk��K_W��<�c�����D�5F����m��*_�Y,C����x�M7��K4�	]m���lg]��_����@u=��gM���}E����|�+�������#�m�������=��c��m�����x5Z];kV��Y����t���j`��m]��Y�k�l���}e����P=�K�ql��{��?����'���/��~�Y{�s�����%gs,���ei�z���	�R��C?���R�%h�O�v�����_����Q��$�|�&w����R&�Ob?iJl|���a���e���R;m�Cu_Y�s�4���bJ�V�!���~8��{_M����)�����������+.(�,����4n�[��,-�14���9��O�#��T:���L���(�n����������_�7��������&m��A�

��lWC��=�������}w���6H�<1�O<�x����/|!;v�����={�E]�.���t�������m�������y87,s��7�j[�k�5(@�vgu�Y4��T��ckV�������I?���Zz������:�+}�e��w^{Qz��S[~����T?�e���C?��V5����C;�f����S��;��������Y���m����<���>7��On^����Hh���:�,��~P�������I�����	*�>�Xo����QQT�{����f��o�Lz�_�3�� 	����yb����x�HU����qU����lW#<:��T���}w��W�d��v���}��H��+��|�������<1o|�M����n�~�����y
 Lk������x�G���_L?�O>�>��_�����'���,��y|�����y$�2�|�0�v~��Q��������h��&3>�q�0���(�\��e��OF��
������������bJ�?{)��������6��6��kt��Nl�S�~6���Kz���?�Yi�T��y�3��(c�E#)?�=��IT�1�gw-V4y�g~U@�}�zN�t6:t(�s�=�C�P1���L|6��~��D,G�����s��E�>�������=|$��� ��/=�u�����?�1�����l��L���8?tT����'�����h�3M�l�W���n�#l~>k��k�&3b�L��b��D�9������$�,�������������L1���5�C���G9�$��6��'�6�\�.�|������t�ON���8��C�x�K��F'(i�c>4���3S<�s�@.;�]~���~O>�d�j�/~���q��a�n����FQ���������yi�G_�ZN���j��U��=�������������)U��zw����vA|6�X�D��y��y�f^���&�����g�f����w����������L��8�2���0��_E�:i�Bl||�T�������
�sr5����[�Yye��AW�h�+���J:2�b��qU����M�Vf�7��x���*��Z����M���<QV�����)s���8�'��6ml�����{�y*���w���m��W���}3��X��)��v�C���Z�(����2�R�=�rGm�.�u�2�j������������8?���:���	�T�����U\�s������Q��������+�Q�1������U����m�0h9F-Ce��*w������NZf�]U����<�2�Q]�%�4T�,y��F�	������6M���Q����g�W����l���k���G����<T��1���G���*���~`��h�������No[�2��>?l�/��\u�������NZf�]U������!�/��{��?�j����q|��1���s��b����bY')��Y����/�#�������G��{��}.=���TZ�1��('�[&�J�M��Z����_V���Q!�ibZ�'����|c�ae�{e�]U�M���z����l^n��?�s����W�L������-�
��/��>W�~��������e�c��2��uYvU^nS��5�e��^��u�2���?,#;OiS?�I��d����&����v_5�,���;�������c�z`�m�q�u����.C��Y�_]��j�M��Q��2�n�a�:n���^�?������mT�qT�_u�c�&����r��_@;���_����F��X>o�e.�<���N����\���8����<��^�����0��x/�������j�M�+W�
��2�Z�y%�2b���+���]�1c�/�����.C�vX�?��\-��18jYCYf��5lY�-s������m~c���Jf����z ���� �uO�?n���a���2D��f�z�,�*/��a��2�n�Q�:N�������x,Su�L��Ab���������������U�����[>�E��"��V��>�'Nu'�ab�a'�X�8��)7*�6'�&�.G�^M�}���j��;��O�g:��&���WU��v���|[����}Sq,5m��R�e�:��q]���Y����q������E��6
�<h�����8�����d�2�&��a�<��c=�x�(3�^e�q=l��:�I=P���j�M�������������Zn�Q������� W]�Q���Nm��!���]��V�i���7)h��_~&��_>}��1ob�S�l�e/��pM��y���}��[[���m��!��&����.G,C�v����4+��Kw�}L;_;����v�(��h��v�&1�n��o%6>�y���:�b�i�e���#IeI���_(����rg!���V��
�mc4N>��G�>*�������+�����f~�(7���X��3UQf��|��'���������X�q��R-�\�i�T���~��(f��D�-cc�H������,��}>�����!�W������y���
��*�[B�����/�j�ue���k�5]��������:hX�m:m�i ��^l"�m�,����|��v�D���2y�����czR�2�e���N����i���m�AuR�,��6�-��8��|
���,k��X�Qm�Au��rc�|�i����v|^�6�+T����P��z����.T�o`z"N�Q�=k��Ml����2�)S���i]_�{���(u��(#���y(�������qQn��Qf^n�2��WU���v����Q-7�\����_�����\�Ae��H;������#�5�`P�Q��e�m��,�`���K��O�[Wf��1�����6��:X���z��e��R������ ����^�F�;���������NmT4����mf��y*E�Q��h[!WO.�	m��0��k|>N�u�at�bz�@�5��e��w����V��(�#_�X����t�t�J��r�u[5�?���X�����e���J���R~,��}��/����u��4����-���[�X��QX��u�_L���A���z�M��:y]V��M���6
�R~UB�:1�u�^�(��
gu��eUU��U���J�e��8��6���x�lk����=u��uv���Ci��E�1=?&��e�����.���
*�rU5�+T�y�g�����449����_+������[^�7i�V���uZW������(3/�M�m��y�_L��;��U�(;��Z������_�O;�A�e��J����v~.�/e&���o��v�&1��l������#6>_��|�w�l���V�&��8�P4�?�������g�S�t1e�����.:#}�[^�����S���`���CTN��,*�AY�VY���,*�P����&��������
�&�F�e����Q�]��a��U�6�������c��t�����_���SN�8��:c�^��_}c�s��S���Q�s)��h���P=����c�|�&��v=��ih�������&����gY�m]W4�'�&4�gFw_lZ��q�������������yn>�����r�������yu����W��U���/.��f����):�u���}���Vh���0N���y�a��5�'�Y���
�d�\��6��(������M���|����<MT�{�:~U�Mi;�]���e41���N�o9��&�&3��lS�����8ir~�����o[��e��F�%��u��6���ir������?N�]�>m�/����i������������I7���|[��M����i���2�����
������*�5~(����w������l�61���u)�S����t^f��kRO����������������3���/�������|u��d�&��nz�X���IeJ���#������G*;����-��������m�JM"�hFe�WO2m~����5��6Q�����<1*�Q�r�
>���Q'�u,��M�jh�����<��7�O�$L��\����El����>��bJs�����!��k���7����#�/���W0��5������h�5i@��Q,|fT���������q������z0��M���r��d=��������>8Jq\D�zRM�G����.�����c���wl��b����AC��u�At�����8�����ei��Q����=uW�t���n�X�&ua��{��'����u��Ou��Z�P����X���`6M7Y-�?������������:��y��l��u�4�k���I�}�2�M��t���3��NB�e{����z1�������u�v�p����e;��(3b��������l������F��`���/�KM�3�Z��|����q�AF�WHR��������y�Ac�du���6q��������q����8�wT~�!(�]�F�vW~�6��B�u�OW�
�y�U,c�b$�:|���������4�/���l�T5m���J�������aT#?�;��=�/�78��s�E��U�}0:*��/�8��(?%mL�����?��b��4��3���!�+���x�N�����.��a����
���M��6uY���cy��+�A�m�R���$��QAX$������-a��:U�;{��`��[���8����������W���'�e�*7�����_fc�];�4��v���?H���!������[���U;�����5��-�����:�G�$6^����j�8j��T�7����"��2%s���NT�N�����v�+�A
�j��U�]�e�
4�EX�������2�����6���n��
�Y5xJ�rOw���)`�]�=:��a�f��<�x��05
�6@�hzL�jXO���x���M�aTph\�.O��?�A�����8��o�������z�9��~��J�A�wW�v]5�D���z��r��V�1�U����VM�����m8I�%�N�$�3N_!�M�9��C��y���~�J��c.l��~����T�M��]�3��bh�_���8F;0��2��.�U�Y�C�_fc��{���������v� ��3^��c���������U-3�����XU�t���������b�.O�����P�y��,m����b�[v�T��e/<�[��u��"O*�8u����*�81���qC���m��T�FQVQn�|{�w���dh���"��;6�2�����8�=�M�8~b�b��6&��vyL��xy����&?�����!����v|��y�;�p�3i��jPGb��;��bl��Y%�\��b�������^���$��i��������5N_�.��au����d�}�����T���l
�k�b1����hL���k�.����DQV���5m�6+����h�*���g�k����f���W�8���I��������qb0uup� 6�=V�
7�i����,��q��IlWl|�IR��w^��bl�L�������:}�[�����*��,����`Dq�.O��]e�iP��DT����l�]Q9�wW����_}c16[���sxo���wc�E�q4�G
q�L�H�c�l,V���P��I��e1���,�>YUWW�>V����!��t���I\v�e���U�zU1�N^&���hk5�w��\���f��6}��pU��]�����������ygZ�Y�4�s
�=�#G�w'=�v�_�6���<�#�+�;�%����2��q�/���6h�/�Uo�O"�]~.�(3O�`fc����j�z�������2%����������\U2����N��P����g��US������S&�w,��A5�m�J>*�8Y�:I�W 4I<)EC"%��Q��y4J��s���3����=���N����3��� �����?6���S�G;e`d��xnrlB���3��:�����u�.�����7�i��J��z%�4���vy�WM����>��lk�������<�\���
�}8����XE������4��EY�q�6��ih���G���k���Y|fT["��h�����^�	��ta�v�����tMf{�s�����M_����
�xJ����x]}?�N��WX�=IDAT'r��j�y���{����[�h�jz�����������fQ�������vu�8YV������Q�V|u�D�_~��e)�^�2��4){��<#(��N��2�l�|���1I�m./_�t�����t�#��7����K��=���t�_z�����������]6����c�������f=����ct���A�u���I}2�I��~vZ����:����:1?���.��;�������>���Mu{��Z���^����7�����C���;���j����q�{�;O�9��5I���Nk�����������T���w��EU����c���{�����>�>��_-^M����������C�:wX�T}��Nm��
u�Z�&��
�^��`��d���zM�<Y5�����_�u�/V��G�����������������vw�Y&b0[uy�����q�������9�e
��]�����I�uX�����~�4��.���������.�����y�E������+w��'�P����+E�:�j�!�/*���b�F���2�������2��g)N��*�E�-���w�����7>����el�����Z/������]��
���j�9��:k�UU������>/�g�����w;Lr.Y���N�CT-�-uG�E[k�yni������:|������{����Km���6V�l���1�����\����kQv�=�L�;�8����~�l����v~;���L���XH�]��v�F���`fiVm�a���G^��+T��y 6��$�%u������/*^�����z����(wY�
��B�6��}�O�I{P�67�V4�{��P����v>�%��
�x5=Qf��`y���[ni�����6�����Uv��5n#����F��ZFy��wX�>>S~w�e���E�oQ�}�����}m�jz��(�����5�X��n��g�L�"�:�@}��B�����F���P�V��e��q���|����hg#c�~6�|�F���I{w�������-uvu]���`�����v�|����Z���Y�|�I�|�Hfv�����m���{m�j��
y�]��O�y��?I��$�,����t�*����l�_~"����Y^��'�j%��4�4���)O�M;:��N�I���(�Dc`Y:j�������|����Mj��]�+�v�����*t�Nt��z�����8�[��;F�T]��o�/o�����Q�fPGa����H]4���/���^��3���$�����X�]ps]��&){��Km��T�W���g�y�[}o\����p��.Lo������6!�|�����^��lT{w�WFNRwVE[9�yq��s`S��!os������b�a�*������u�v�p�:�i��g�(�GY�v��|�V[kRM�Ee(������l��[����cz�!��r�������6$�,������9}S�Q����l[�Yb������,{q�%����m��4i/5Y�P������Q[F�o��im�����^�sXPt��<e��P�b���8�1:H�8���[o���>u��4(Z�WY������%]�=J���!��z����������24�_�}���>H���*���+��[��q���������2��*����Y����&��������u����nR�w������
�R����vZ	��T���q~���^�v�<}�o���e�(������W�+�3���vu��b��nmr��3�y��~���,�|��6&i��-C�m��j�>���z;_f1�>����F]��	��n��y�����x{�T���^~N�'����gO�O�y�������|��
� H�2��b��mR�G������@l��������-K~�yG-G�I���������������)�����+�W�q5�Y������yq1�a���Q��������W��8*��m�!��z<������/kl�Q��|}-o��QuP|g����Nl��S=����QA��!��^����F�I���8����������)�W�
��WEQ�u��
M�D!�bYF�uy��]m�.�py�����+��=�Q=O4�?��ub�����`�~L�/�������]P��m��zs��]wc�2^x��b�r���y[r�>J^'O���T-��9>?o:_��v��b8�����?��v&m�W�\��m����6	���V���������j����=����\���,���'Ie|�K�N?��Ho}��)�<ieS�������r���������F�$�P��
;HM�u����������j���hz������xy"����1=o��'h��������W�T�u�wu�����3�����-4�'�E'8ol��J���F4n�c�N�MyP%��ny��e;t���X�A���.T7�Qf��������
���V��i��8m�A��M�$T?�N��\s�5c]��y���,8�B�m�:QFy���R����[����Z�m�&�!^G����5�o7J�)��3�C�t�'U����,���.��V�������;f�:>s��|e��UQ�WM�-�e-/;>;������|1��e����_��v�����������z��<��u�8OI;����O�m�&�!�����y�]�Ul|�U�q���*������u����~�����7���7�����[&��{�+����<����3�~u�:��-�4���s�:7N�1��-�!��u��20�����������������G��+j�x���yb�e��+u��4��}�!����A�~�3�0���S�:O��^'?��m�q�5�&�6e��6)��j�1>���i�������v[��3�m��������?�����3?�3�C�������_Lr,41����d�m�&
�.O�q�C�:d��gYch�7m[v�en���������r���������6�a;��{�~}�����O���-������<n��,�����6�L^:W���C���������P�����7h�r���_Bu_t�LK~�7b������y�U�.O���������8�C��i���.s[M��M��O����c�a�m���\�����q������c�������� �.O��<M�Cy2l����14���);��I�MU���q���4�3���
2�2�]u�T���,��[���]������O�s�?����?���b��R��GU�ye��Q^QRW�������[L���
MO���M��c��m�_��v�}��������?s�O����Z?�1��Q��:V���1���S����p�q4�8�S�:��c��/����tYc���&e�<�:��w��o�q���8e��4��m�uI]�������|p��X��?���xL[V���<���{���~�?3�t
�'�Y�:�c�e���,k��7���������g�������2�Q� m�v�t�����}�a;��{�������������w������������x����j}W����a�[]�uC�W.g��7_�Q����u��4]���9Hu~��vV)�����ceZ��P���g�����a��8�S'�K��.����fYchs�7-;���������h�����I������q�ue��U������:o�������S����`X�2�8�S�:o�c,_�Q�����0jysM��yBuZ��%����S�����N����n���?�Mu�.+�a���0J^�\TX�g�uu'��|13���?J^��e
S.G���,G^��+�4&��[����/��|9����u�sy�14Y�r����-O��3MD���5��0hYm���sQF^v^f��q�k��w7��u��8I9����I��d[�jy�,#���o���b_.������=W�/�A����M��\���69f��6U�M�3[���;H|��Le��2���t���u�~�������vg;l�2.������4������?������z��.5��h�^�!^�{���]�A����������&���s��s��gG�;�����~�b?����u������0H|6���z��T��&�P�������r�a��ty��V��,�Zf���.sS�n��W�}�5�����������n�r_�����|9����u�sy�14Y�r����-O��3MD���5��0hYm���sQF^v^f��q�k��w7��u��8I9��zW�c��P�|��������1�����+��Y>{y�Q�����������V����������x�si���x�t�<�e>M��/��#2��Xf���El���X���_����n*�R/�
�et���c)�v�m��\�z�t���c��\�k��v>�|gI*Y� I�����b`>�B�$�V*��J�������8�HR�HI��A�j�eQ
��E�z�T��eYT$��`Uh���q�����[n���|v�2�H�W-���A�����*�<@�B;`���3�$�U�$�t��j��z��"�I����e�$�;$��|��"6� �T:V�
�2I�3Q
���!�h��*�@\� I���jpV�v>����$�mPfTG���i����	�AU���W_}u��E�8��A`�i���qrk����i��}����Z�C���X���`�b!��@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
���@�$��9I*tN�
�[��7GV��z�������.wR�s�T��$:'I�������O�+��]IEND�B`�
figure-2.pngimage/png; name=figure-2.pngDownload
�PNG


IHDR�w����sRGB���gAMA���a	pHYs���+��IDATx^���u���g����\��t�(J��$ 6D~bAA���- ���-��	�S�������P���������m�����ds�2{�{�{�~����uvvn��y��x���m� �6�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m\��6
���.�%K���w�}��7��d��y�����e����v9�����N��U���������z����������;*]�����K���A0���������/�1G����rY����$�F�0<���h�����L/^,g�u�5V��Q��1}��2J�h��e�����\%}�	�\��G����#���A�w�u�5P��:
�kzRG=�����Fc������L�s��0z�aI�9
@)5�?����RzlU��h��}s|��o-��-�o-�W����)��!�2�����hH�����#�AP����4@��$���`���n$��;�Rc�S
�IG���
P����x�������.�n�)]@���R�P���7�;�6���I`���=�m����b6��V���|�b%;�7��},;p�<��������"�
�f�RV���|iwT����F;�Phm,z��6��6����}#���k�K�>e��R�c���~��S�F�nLG@��
����nm�����rF� ��x�d�r���L
?�/��
���h�����q��S�a�|���M�}}��q�R���c�wB��6z���vP�����h��%�-c��wPb����u�][
:��^
�1<����M_���K��F�����QZ	�J
(_���V�j6(Uc��-\����5�����
()v�L����4����(0~��H94�j#�6�;���7w�\kNf��.k��s�FW�������g�S�J���zQC�@@�k��TB6J���'�]���
��d����'�9��R5��#up��	�#�vflo���)z�Q�m�s������������|���>�=����_�j���q9V����~(�R
��2U��2'(Gm@E���z��	�|�LHg��y�����AN���1]��
�>F��vB�+a��8�[�+XP��D�_�X�W���T����
�HN�6��#5{�l�V�rRaB���J��E�;��
����o�r�`A5��V��R�h(S���@�!h*�����=`��F�\]F�!���a���	B�@i��E�-�>��d
�Q����LW��-
�e
����	AP����[0��R�&���6��X
(=���IU��l����a��mT��T���6`< h*��@%���\�"d��v#���+�/Sc�m��e������M1����
0N���
���l�a�j�AP�r�p*�����qn��FqRC_Um��S�_�f�R�a�����XU��&�o;Um@� h*^�+-K����lz%�=���t�=_�/��(}N��#�s��w)=�r}��R.�?�s��~��3Or}����Hd{�B=�p������q������<+���dz?t���$_��d��`�P��I� ���R���+�w��~��w��
��l����f��{���.��5
C?�|�(Z��B6*�y���,�F�����z�iX�t�������,X`-�Y������I��>�t�����U����=�$���i��_��;���C>�Q��?}�����z��c��<Z�i�t�e$C6����boo�����F����l�[�A_�h=�����X�'#y�l�X}N��K�0V��t����H��=�u���{{(4}�����A��B��>���~���s��W�)�l�W��X���=���g3��{!�w��(�\�k�A��B�1v�n�>�t���X|�=�j�	�*���We���.�:Q�\s���z_���^�8�W:��������}����~����=I�N`l�g���p>��m-��n/���=��;��u������������F���`��1��0���:��>��{2���M�#������/;a�sU3�>��%K�X��U��3GK)��m���]�� �/6UlJ�w<=�Hg$�a���
�x������X����h48����{4N���.�rx���~qRV���
d��e�>G��t������S��Y����I��z���]��S�������Re�Rh�a6��}��_�����~�F�Q����k�!��Q�}_��\
]���r�Nf;�����NU��!��*�����4��t������}(gmJ���+F�b&z��'��$q1^K�O���.�rx�����^kcF!?K�/�>R���g[H��V��V��Y����v��_�������
%�vV��t��Hs[)e����}���~��4��k�(V�#��s�J
��T�md�M��*$�q���)�*6��kT�o:(mr�+���g�����Y�8�Z����b�W����^�h<�\UP�-�n�n�~�����>������c__h��~�*���PrkK5 �����>G5Z�}}�b���=���m�-V�-�Fe���l��hV�U
�F��q8�^�lRe�wS��2�6���b#���zRJOR���x<����u��Dv�O��:Il��u�9�B��+��](����6@d����Y�gS�F����<t�%�:�0��no�g����9a�(����=��8N��n���J����R�g��:�<���T9�~+�s�~��_!�m`��K7d�n�����r@ �w����*���	},}�B��@j�a������
�������Wz�Q������u%
��E����~�m������s������@IK�Q
0j/^�C�L���K�%GO���:8�n={��5-X� ����>G����}�N�7u����>'���*����>�!���\_�r��u9'�y��C>�w�e{�N��t���={{s���~v����N8y������k���_����kmN�k�y?�n':W����|��h��^�d�Ql��m��u�u������_�����p��!�����d8����;t���A����7T���C>����+��a��z��^�k;*�}b�b|_�����nWN��:��t��T�R�^�����C���d����� *���V��rG"���j�����T���s)������;'�
=]v��+��}Uf�>�B)����Jc}���;��+�<__>������n���6�����
x'�C��������}��S����A{������;^j�I1�z��{�1ru������=)���r�=��X�����=�s�A��s\��zG�i�BW)���Uj�Q�]�\����~v���G[����m_���1K>�yT��X��?���tP������1o4�*��'XGzbZ����Z�r��~��8jsr��X����yJ���\���xN"�r�(�����k�6�p���|��#��g'�C#���z��}�b7��#WZ#�~+]7WCi��'��p��I���B|Or��r=��p�����.D�+�U�PA����.��S����=����b��GC��.}�*��T�l�)a0P������D�Z�p�uktdkp��O�j\���u��Fzb=������\�w������|!��X|?�4��/���-��?����OE�\�C#	��t�\a�b6��#WC�~/G��V���;^*�I1TB�h<�F���:^,��$�o�>�b��#���Z�P!�l��r�f���cF2�o�=8y_�m��\A�b�{�k�R�;D�D�:��X#C�*�6��h���n!�d�h�T���X��O�F�B7���z���yV���\��\����!�\�t{s����GRi�P����B���d{oJ��-WCm!��*�
�l��^��BO�)tW���*�~�P��d�oVB5�|�gR�V������P99�qb4��Hd;7Q*��C�E6z����5�]�&[�I1B?�}Ef�x����^�h~O
��s#���lg��4�-�����\
���
���:	�S�F�B5������+����v[���l
��+d}��r��H��Bx��m����O�x��Jm�X���r���rD��[z�_O���
�Z����P�h��E����:h�����b����W����XR��?�w������T����
�hU�m-[��b�JT��x�����Y�}�K�=��Ue�C�ZIU��^��q��Q6�Bx�8f/g�����C���>..��p�Xr����JAPR��Lj��#���D��4��I��*m�'�t��`�yh���rh&�lt�u��
>�����^�F�b|O�]���*���k;+�F�\����w�+��c,�G*�v7�+Q)��B��n������w��6WU��4�f�������A�/������X^J!�J�;H�6
JA�!J�!�\�k�C�7�����F����Jz�s5+T��~�*T2�
���s���(��l�b����H���
�P�`A��d��9K�n�z!�XwU�M>g�-T�����H�W������E#���3��h_��I���b72d���
��j�.v����\A�b~Or�w�V)�V%g��R�xR��x���6�TR (5�@)"h���l�ES%4�kc�6���	n[�6�Vzcn1_?�/h���X�6�Ru�B���e%�&��1l1�1B��	�T�DCH���H�HA�?=�sz�W�a�R��j�*��������'���]w�eM_RC5:�\.S�]�����r=�[�'�K���j�(f#
�7Z�q)4��uC^� �X�J��[�T��P��j�\�������
���6j[���!#�{J�����Q�a�R��'*Q)�dC�T=qi_��'���@?�0��@��P
'�P����Fx�&�g���b��.������U/�Y9/pL��t��B�T�PA!���vZ�p�u�����E�����0>������=���)����R5v���!Po�^������g
���2c��ls����I�f��E����+��g APR�dlj��#���v��\�5zb���j��T�X�QY����J�FK��o,���%W8��>9[�@`f��g�*�t���-*�}I) �6%L6Z��`
(�bv�Sl�����P��>*��x���G��?�����0:����6%HO"��s���In�n�@��U���+/�j��������x���
�.gr}/��@�!hPb�d�V�);TckR���)�U�N�|�"[U�\��l�.�]�������
@	n�&5P�t�R���5zb���P���X
s����	�3'�s�v����E%������x��e��
��mTa���S���#W��cTPj���!�����;P�	(�Gc�g�nJ�|���\��5���+�l>T)v����!����{22���3�{�6j��r�u0A�q�GX�2������BC����+�����\��R���U�I��e��tU9r� A����DC6�,�Oyv��\
��'���B��X~����m���
.��T����f�
B.�����N�Qt;�]�����A���+�B�l�����A�*)��k�\�G9���	�XG�o�_���O�u�j6��Y����@n�����tUX���c]Q�0P����RD���j�)F#$����dk��;\N*(���XP�\r5��{�+�0���8i�.��?�XG�u
�h��\����5.\h��r}V�9�O�Q�������m��������(�(M��,��\�������	�!Wx���
,��X:�e5�\U��=��p:��M��\Um4�X��#[�,[���+����R�FQ�]��W�����@Q����6FS�����0��V��[)r�����-�o%�'�RD��d;�T�2��h�FS��E�Y�
�I�.0����
���t��
�:��E��XU�)f8�\8	��)'�1���q�6�;y��t�Y����
D��*�c��_��7tp���
=���>��a��D�o�r����1�l���5�Tm*�����ek���B62:���,)F�y��z�F�b��p�U4
��{\	rl#��i�N
n8y�b4�2�U���\��b�n�>)�bT^,�l����������k����
�Jx�06r���
��<�(q�NLW��\�:�������[��`���W�sm�����F�\
����_,]���5����+����_��9h�}?��bt[�
K���z��f�r}����$W����\a�b��-���k?Y��w,�������K��.�O%�@�!hP
�hR���hqRya���~��[��J��`I!����;���F�\��B���������p�B�Vf�]j��p�K���7�����9W!*���+�{7��'��\�����Z��1�����s�>:	�jPT+��t@9"hPr�`.��'m��jL��b�6
�"��O�}�n��h0w��/����>r��j66
�8}���t�
e:�8�Y�S�&]$a�\��.G��+�1�� @!BiN~���K��?��&���n;�pz<U
�?N
w�`�J)����
@	(vc�"d��$Wc�n���H>����k�&���Ils5T�69�p��}�>�R���$|4���I�o)��-���������9F���+��k�!�d}�V����}�U���T�����bV*��l?�,*&�^t�}�nw��8�����~���}�������
@	(fc�}���v�'��q����l
�:]D+�;�.�`7�a�8��G?�|~tY����������"�g���'���(���~��T���yd��e�{�3q�HU��y'W��s���i4~O��
��{�ty'�I)$����O5�*��A�B����4\c�m�+e����(<������
@�p������n��������q"�N�N�:���6��l
�N������'�>D�=��1��P�~~�o��������uY'����&RlN������(��@!8�.S��1t_k�oR��}���d(�s/����k����A���K�[:�=���%�����>����Jw<t��o�����K�nC��L��s�>��p\JA��'u�4��������pe:�n��F��5�q"� ��M�J�(�'���=������'�P�~v�O��'��T�*q�����c,��]fb�oR�B���c�B���y����:��\��X�%���>���'��Vn����6���s�r+�m(���1PX��-�nm��!U�B"hPB��]��pe���c����HF9�����rhJg��C���5��B%��ngR��X��$��dC��hhd��*�����{=���r�m�����x{�cE�%��^f�>�c&�������<0����b7��
$vcb�FEN$�\�v]��N�4�3��J��P�F�r
��w�����w9�cE������J�I�G�6#�nS��l4�-}��C��)���l�|>����W�}���\����<�^(�\����RF������B7f��=�H�5�d��(5��I�������������F�C?�b5��}�6^n��~�wQ�����~�{�,�g�*���i���ns�EYS�g��B��;��V��P���x�k���	���V��>�m_������(g�6
�3�6%�P�Y��#�Nn�*�L�Q(7���a���m���N����FD���t���go���a�R:����8���~/�1pT*R�Q��,�������~<'���].��7�X���p�s���B~6�g2���r���s4��bo/:��X�1`c��I��t��+�GO��
��a�j/�<��JJ�JO
�I���(���i��LJ��)�!(�~�n�e�Q��L�>����n��k�����l��e�r�X%���R�WT>!�\��T��?���>�rr�U�a����r�Ui��l���7c�����^:l��e����(�@�#h�j���)}��v<����G�1Fcoe��mp�L���B�e���
d@�J���-Z���p���j��w���3������m����-��
���+��j�^��J��4~�)��t������v� ?��d��:@�"h�l���Z�_�t����N��T�F�y��Y�cE7z��6�i�	������e������i5�b��Y���2
���P6F��x�����[��y�b���!�t������i���W��P���P����u'�8	@���;O5���=���.�����@9!h`\�V�|8�>�l����
fP��uEc/R�����/W0��2����l�	Ae#�	X=i[��6�E@6\�@��pD�j$�i�����[[����l�p���
��1{�l�Vz�-�n
��l�u�
p��<i�&[8"S��m���T�F���nmm����-����'X����\.�Vfz�VD���$��D����}�������[��w^�w�u�5l+�q(������M��
�]P��(+�N���]M�Up��c�+��l'�(]�#�C��d�|���T<2���a��Ae'W�N�@���6��
��p*�q(����2��	��rF�@Y��
�o�']L��<�Ah�]����m�eE��A���1h|`�Z�p�u�M���p�Pg�}�u�M�i�h�Q���
���W
�I�t�#���WYre2�#�c=����
a�����"�-2Ub��AeMO�jc�6�������
���{Oc/�+]%$��4S��\t�E��-t���`<p%���#L��6p,[���^�d�i�H��y��y��`�������>��($
�2J�3��t�/NW��*b`� h8@�Q�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m�� h8@�p��
�A�Wb�'o�P��+��N8��R|�]w��z�����mM@!Q�p`sE��S���i��&��v�M����K__��V
mjkke������b13
��Z�JV�^m��m��w_y����&�t��W�����9�R
A���z�������N2���~��'O<��5F�Q�#m�� h8@�p��
�A��6�m�� h8@�p��
�A��6�m��p%����w�}��������W_-��~�tww�������+��N0�������SO=u�s�����.�LN:�$3
�������Yj����%P�*�@���1k������"�m�P���q�{�)r�GYK�������!��= ��_�PoGb{�I`�ti�io���i�� ki ���~���ke��M�v���+---�}�i*N+���
����������q�g�����~��O<a�Q� /�pT�\�)n�H<���h<>���xbZ�!��wr93Q��?$�;��w
������}I�8��}��������?���w��'\(-�h-	d��xd����FM�F���Q�:�2���fP���m���yd�i$�[}�$	'og��n�r	������	�f�L7,���~�n�N�&L�&'�N��~�{��M�Fi����]�����m���V��y:���d�������F� O��~�5�Q���MLb�A���2����r��HM�'�Om����cQ��RB]��)�s��>}�p��D��P($k����7JOO��A���2�E���i����J���'MMMf=�tm�T[����7I4�x,"��.��s��m�����%���\�������3���}�r���?;I��q�<�����s��)��~�t���Z�M����=�n���� �v#�a���WKGG��fc�.�v�i'�c�=�)�6��[�tHm(�Bjm��#��$��v��2]B
���hdPKG$�h%���&	�u$f�M�I�5r���%�m��7�KW�%�]�}�|�a	��������{[�<�wy�����w��L�������?�n��dWv���Eq�r��G[S��
�qC�����noo�M�6I4���PX^�[��1]�[�����DB����B�=�$<�c*���:d�k�v��X<��'�d��:�����	
��;�X��<y��������KL�'gl�%��.y��+���|�����m>�`9��e��v�	&�c(
�hSS�L����ON:�$�s�=��6�6�0��F>�����	�X�������������\{7H8�o����q�e���o/����Y*��)o�|��W\n���x�Ok�Of5�eF�_�����)1#1=J�K��%���'�K�Y�f�j5��r��q����\��������e�?�	��E�`�v��,W.��|���e`0,�4�GtR$��	���S�S?���`�X$$��O�<�q{����5Uye�Ii��������l�����qK��6���\#���I���V�Y�j��|��r�UW�e�]&��v�,^�X~�������+������TmF`Rs��}�l��yG��{�������M4��#x�d��c�����1*� �p�&Y�����(
hy=.ik�JU��T��hC�G�����v�����@.�HD���>���k��^�
6����.7W�\)�>��\}����s��y�-��P����{����}���g�#K���\�'����$W]p���1{JkK��4�^,<(}k���#^�K&�x����>j(�	x�9�5������~=9�`ppP���?��e�����T��;��[n���Pm
@���thj�.�Om��q��r�u������m���!��e\�d����E8�GyDV�X�y���������e��)2a����1�m�V�y��W��g�5��6���/�K/�T.��y��$�YsF���3��~������A�r,���z����������/�|��P>������d��wb�M/1�e�w�$&�B�3��vA��c���~ikk3!
�ill���'��I��r���6��Tww�Y*A�
u�y��Yg�%g�y�I���O<!���/L�u��92w�\9��3���p���JUU��!^�h��C7�������Vf��m�;���y�s�9G���g~���P(d�P������DbQ3���PL�4f4����=?��/��i�`m7{��g6w������p�P��WWW'���&�c�m�~�ik	�lm*��?�i��b!����W��<PN;�4�h4j���A����}�{&s���n.9���x�s?��S���?�c�����	�������q�����I3DL���@(�1g���	$�i�Q&dS��]r&0����Y��l� Muu�5w[��^�nw#���]������M����M7�d�(#u�����M>�{��;L�,Y�8�	��:H���:��c�����M��V�(6��J��v�G%�K{D���a-v�����dG����cr&0�^�a���A/X�%u9m�[�n��
���M9��cL�F�#��ln���m,\p���?�1��t���^r�%r�GXKla�m��t~�����9��?�|S�F�>�X��������ZK$�s�
7�x����k�����h8h�3�X\���%MV�����=������+1#����Q��|�Z�B����g���uA��@������[cP��T
��|��	�<��Sr��g��mp��}��&�2o�<���&��?�������;��GyD|>��F��m4�����k�I}���v�	�z�����h�R?��6)����T�%�����=����GK,4��M�ym�����Hw�v�@DVt�������1�xL|��2��/Q�iiwQ����/~��������L��l�GZZZ�1�\m*@!C6��G�&�r����n��pM*������_|��mB0Z�&[��s��*�s�y��������~��g'XS�T�Y�x�V���0Qv��ih�&�h�L�����'$ow�d���	��&�x\�n�4�w�1�D�,��^?i�$��!���*u9m���m��'�|R�>����l�_R�1Z������N�6��������L�O��h�P��:�,S1'
����?�j������?	P�j��({/���|p��l��v�	��v����������i5���6S�F4===�
.SU]���_����2���6��V��={�6!��)5��/�6jh��<0��<��C���M��d
�h�'�
��������N��J
�<���K���@����e��G���Q\���\n���n/3�<I�;�k*�������^�����l���i�6���Mgg����Zg�=�����F�f��*6Z]�_���	�����.����?�t���Gyd���`�p���R�6�<�����z��k;����Z��K/��(P�B=����?�
O�-��.�'�g���2��Yq�u���k��@f��1c���;ws�f�v�Z����5�Duuu��
���i[��-������f=�tm��e�������V����������X��)
���_�V�qb$�A�����|R��G�R7��]y��ce�3�������r���7]./^u�5�L����3G�M��Ue���n�Y�z�ttt�j66m��i���f)��3����{��G?���+��s��7�.����/�P:�����r�y��G��RJ��,]>�>�>w�6�T�{;��[~.���q�M��%��|2��/3���J�4	�$���U��I'Y������oz���D*ms���#�8B�>�hk*@�g�GO�$Z�EK�}���7��FZ�����.J���s�9��M�A��{l���2Y�d�VA�C=4��9����6���(�X$,��_V/����~���*��<1 S�}�T��	5^����&T������-��Y~�5�p���|�����\�iK���N�*�{����^�T����8�?v����������C0NhPE��|C+�<���u�Y�{+���M��\���g�}�����k�xJ�V�Y�����xM%��%S��n�F�N�6Ty��N+����a�]�_�@fz����K���o7�F
m7�J7k�����_����[S6�U6����w_y����&��V��H��p������o�7o^QB9Ci�U��J�hs��w�����y|M����Xc"��������������s�w����:N!i
P<�����X�n���������O7'���=z�W�	'�`�G�u�]'��z���P__/�]v��t�If�z�����z����N�����\N
lX.O��$��!��o��:��d��&j[����B�5wb�	�,�����������n��+W:��]���m���c��5*�~��'O<��5F��"�U���g�5�=���[SD���c�����������l\p��~3���{?��1;��i�&��q�9�3�*=���o.��w��I���E�(
m��������G���^|n�Lo�KS�#�A�hb������	�+��i����Nk.�5�d��C��>����>^����
N�8�`P����<��A�����?���u��
��uF��l����U�F�{��������9!�����Y�Q���&<dW�����@����2��Z�f\��
x��%�$������b�����&[��I+V��x`s{������V�8q�	�k����I&O�l.Z��4l���%K�lua;T2�6(:;d�Z�F�v�m�`���t
U����6M���Tv�h�I`��c��'(o�������Fr�=R����u�������l�#�yq{~�����I�e�5�"
��O?���M��ZZZ��:aW�inn6W���s=��S�P�tOj�:�uT����������M�����3]AiR6��]G�u�]���|������q���=v1��&�5��%�Y�*��=�������(�4�u��Ee�3���_��Wm������H�����5�^�����v�i���5�m
�_.O\�i��no��kr�W��}�y����/����+$]q�=2a����Mr �^~�5���M�L[�����l�]���U���7������o/�w�5*�����T��
�h%�9s��>S��=�<�>�l�m���n9���F�9���e���c��>}�y
U�0�����
���fc�\~�c��������E��g�5(1yB������c����\'/��4����"�Ty\�cK��;4��Y�`D��Ib�OU�L;�s���gZK[���^z�i��6�f�/d��<
�tvv�q]^���J34hS�&@��l��M���fr�l��I��#�H�*�8�����X!d�������&�=��x=��l�N����;����?"��]����k�����h(��b�������m(Uo0*�z#fz<om���9��l�mnk����.��VUUe
��t9���i�*A��I�"����^E������
�8��w�p��n��_��IY���:���#U��R��j�@�$�V�� �.201���}%y'@
��/?x�	���A����f^�8 k{"����9���B�fgPB�Xb�����Q��E����uO�Z����u�E���
����r6�T:�6(�+���j��ln��VY�d����*tWV�Bv���/(���Y��;��yL��?� �fJU�d�U7��_3A��IM�q{����7v����|A�}�uo������'N��	S%��i�X\���������2a��k��+M;Hf.��YH�����I�L;��`���Y�r�n7y�ds*A�g>��/c�5%IC6Gy�V����u�p*�<��3�$Bms���On��tv�/��&g�qq{�RU?��;���_u�T��H<��m\��5 �'����������2q�C%IV��������S]/��[��7~iM������6������^�����h�___�Y�^@��!�o�q��q���n3!��[9����{��L���z�0����������]��x�W�$n������-���DOS����}����$�j��4����,�`��6{����������=c�F6���f��n�^������\�lm0bv�fh��ds�G�z�}����c�����n��vk��6:�\�m��hA��#^o mb��Y��XNo�#1Y��G����{�2�_.O]x����^q{��n�R+�h���@��}���o�cM����f��!p���3�j����q�FS�F����e����a������~���7O����zP��`D��l4����%Gu��B6�����	��{�I���Ur�FB+<�|�[�MA����hFC�F���]�po��u������������wr�O�k���F�4���/�|4<(/>$���sr��f���#S�N��~�]Ii�F�5k��1Un���M:;���l A[���=��#

Z�r�2s�:����
�\z������������6���VaP�����b1�x,*���	Ed���$�������)k%���Pl�����d���\����+;O��z�4U{eB�Wf6�e�	U��hI%��{:d�=��J8@6555��/~���i��V�����_
����,�O}�Sf: ��
�%[������2r|�V��{��w��l���*��d��s���X]�_v��,�X\����6I<��f
��$��@���hE��O��I��NY�����xM%��%S��n�]��
Ui���6�-Y2l���ZK�UUU�^ N8��u�]M�P����5e��o���+_������f`�6���W^)7�|�6!�S���:�����������K.�d�*5�<���r�gH(����}��R]]m�0|M
�{�)GM%�X$(������T�I���%2�#����|,���*��}S�e��XxP�V�..�G��m���+5>w�^�t3�x�)������u'���Y�f��G-'�|�iG����{��'�^'&N�h-
HE�yy���M�%L����u�]RWWg���y�����;O/^�9U;��l8������$����=	�HiP����*s��!�p�Tc
tK�r	�n��`��{$<�!����k�D�a�S����&{��f������
�E�n�	x��llZ�&`�8.��C2��k��������n��L�>�\��t��yy������4����V.���a
�^z�V�q����[����V]>-\�P�:�(����z��g����������e��n��&ihh���m
�������j�D����	��K�Z�Z#�$<�k���l��-s��.�;��f`���2��jsE$���3�hz�y.{~b�X8(����	@~R/|d�{K���������?�71��������5&�����e�$S����Ki�UC6���|�JM8���&����ow-�n�����
�h('����U�V�X��yj*��]m���W_-��~�������+����_-�]w��z����C}}�\v�er�I'�q������v3��#�������E\Pho��$���1���w%P����Z��Fc2��Z���=���f�����5Yv�A��m��%3�����m��t���z#��',�XDj��$Zt�5�~��'O<��5FE�A���e���n�`�G6���d��%r��GZS��D�Q��t��}�R	�����5���"?��!���S��>`���Db�v�d��r���+��!d���j�m���`(j��O�\M	%�3�����K��Y���h�T 
���r�=��C[U�ms����o�].��"�?�55�s�=W���.B6�QQ_�����I~��#���F������?���{����?!�q�aF��4���W%um;�����ecD#��am��F�s ��uk��h���1aW�A�Q���?����/���w�+~���g����<�8�������s���rT�W����]G]z��������iZ��������]K�
�����������^�:
@��j#%t�e 	��e���:]���f�i��������6�XN�z���',�H,�bT����Zj��d������T��m��d�"��'=4�������5����F�(
m�.��Q^��{���;L��u�db�O������V��I�@���x<&�����Sd����i�p�m�:��I���)
�]�P�pB6j���j��	�D����I��)�&�4�X\�������,����7.�G���!F	A���N{��}��d��hh�j��{0������e��}Cv���t*m�S�2]�w�%��������O�Vb��	�Z�I�����N�X�����5�h h� _]��\���;������/�[����e��#�}��2q�C�%����b�n�:k�
A�D��qy���M����z�3nM}�5k�����*����d���r�����_,���������.kI@*~}����`�������r��W���?o*�hE�
�d����#���/�����F�*@0����^~�aq�\���t�w�������;���n3�Im`��D"��������kM������'���S���Yjjj6�n���+��b*����8����+W�.�<��������U&M�$


��������igppP�,Y"===f=�tm�2�X#��kpN+�<���&8��n����Huu�O������nb������ h��XhPV��y�_�eg$��{�<������\^������#��@v�hT��YcB4vwQ�ET&�2�����nt}A����6�K��-�^�=i�A	nZ'�hD��~�[���~����oN������Y$������UUU���R����� h���
+����
O�%.�[$�xb�=H<�����H�u�������S������kMpF��T�Kj G�:����T.�6@��J6o���_��&"L�������'���2��/���4$���K���nH�0�v5y�d�Q�	�B��7��r6����k*A���a�x��-����A4Vye���R���j���xef�O�o������-��Y~��������I�&����`���Y�rvX@�(	��NY����r{M%��%m
^	x��<�ni�KV����B������%��iP����T��Moo�f�j���/}}}f�'��%��JB,��U�����KL��Z���n�r'�k��)����������L`�h��^{I$1��hT���3�m4`���i�i0�����{�i���F�(��.��\����5o���M�6�qI,����Xs��iE��3g��9s6�m�[��k����M��P($����~�z3�r�����,&L0�@�#h��x,*W�X�EL���N�0��%���������`�jk�5
���;W�L�b*�(�JJ�5�Y�z�ttt�j6JC6Z�f�w�����L���vU�������gB1M�������|
Ex�h�jM�U[[+����������i�
����,X �s��6@�(���������q��4]����e0�������H(K��������3�3�,���e��y��/}Iv�einn�p8l��h����M��g��W�B%H��
��#Q���w��K�#�|�r���,���Mr����������=%k6�ZK��}UR���H<*�x\��"2��
��CO0*��0����]p@��l���r��G��'�,g�u��p�	r��g����/�&M2]G�F�`�����~���}����W��u���L���/(���^~����Ew��n�l�W�,SfR��A�����t�%��v��w0*�z#fz<_M�L��)k.���h<�	������3����q�H��
�0���S��������1I����#[���Z��W.������&��miE��{*m�Bb���j�`D^�8 �z"����9�]!y�3(�H,����[� �=^j����' T�g����>��_��k���+��-U����51�I�~�x�jL�m������-7���Y��8Iv��iR��&�h�L����'$�l
����t�G�tM��\ni�y_�u�W��@Q��PWW���_���.�L~�����~�3��_�"�����(�_|Q.��b��/~!�\r��{����=}!���WL%��c*A�����y�T�M_MSbhM�T7L���)��x�����'��y��
��[�����}��d��K,<h���H���Ao�b�����>�u��7�4���#h��M�6��w�)�{�455�q�'��s��q�r��g�	'� S�L�<P�z�)k������?���r�Yg����j�6o���5*Cg��<��*q�]&����%P�*no���v����������Q�j[L8BC9����������8Cv=�'2i�#�S]omMI.�W�'���G�$����T0�e`��%��}H>����J6*
I8�<�x<��~X��g���>g�9����+?��e���f\/�H,3�P)�������]��xM��g�M����V|�
�.�Cf} ��5o�+��+�]"��^S���Edp�
Y��?��7�\�Q�+)Ptm��!�������^{�Z���5'=;��]I}�k_���N3^�=����o���2i�p��n��4]A�\�z[!�7�y���P8&���J0�}������s�������Nl7��7���6J��H��[���7���_Jd�@a�J����y��7�)"&L��|�;&x388h��<��#����3gZK%����E�Yc#300 ��s�������L��Su�'1;�`z�roI?d�X.���X<nB;@�Pw��u���5��B�n���{e��*�����j�	��x4,�+^��o�er(*�6@�� �����B6������}O.��y�{�#UUU��ze����k��Vn��F9��C������?�u�]g�
�v���@.��"k
T����45T�)"������I�L�%�xL"���y���R+���*:�'����?w���>q'6�I�^�eR@Z���X�����l����j�n��["=����K���X����
P�zzz��+��[o��������?�o}�[��-��wI��{�-���7�������k���O?m�
�O<A�Q����';Lk6Umb���6��"�9��6q���%<������@b����< I3���/������{�2��'�k����j���������p_�l|�>�(�6@	Z�j�������Hss���G?J��������>�Kz��G�0ZQG+��el��P-{�6E��hb�%�HH{�K<N� 6��q�{$���t;�����*��}S���hhPz��$����-�R�1�FeH,4!���mb�u�)P\m�t�}��w�a����3G>��O[c�i�R�'O��D���D"Zi!?�e�UW]%7�t��J9'�|�s�1f*Uc]�|��]e��%��n�B]���\B�$2��z$<�)]���j�F���)�v�����>�M��
H�X-��M&��N5�d�&��7�L<���u&����
Pb���W^���������)�d��/�P~��_����+]t�V�I9�]F�����D��w_9��c%[S�r���(����&8����X4,�����61������M�����v��=�����L6Kl&�����8R�e�I�m�����
PbV�\)����5&��{�g>�k����69���k_��|��r�a���;�h�ufh�Q�����3�����~X�q`<�}��r���{L����	��5��2�x85�E�������l�
�p�$0a�x��vKl>���|,���V����n�5LOUur&�����_o��!h��w�}W^z�%kLd����.�Xc��\.�z�fp����x��N�����~���]F555�I'�$x����4"��f6���"?�����{����J�����aZ����������|�����A�T&wU��N{��F�h,.��1��$�KG2����n����k����o�]���S���k��K/�T���z��������Z���
Pb����ml3g�4jRuvv��O>iG.��r�(���[sG��n��������~��g����T������w��[.�������/�����/��/��|�����3'XK�y�����H,2������Mv�h���_�����	i��.�<U5f] �X,&>��	�<��s&p��
u�����#�<b����Im��W�������4�s�1�HKK��.��OZ�>�l9��s��C�������o~S}�Qk�����r�-�Xc[���:u�5������#��Lk���wj��:�T%���n���[]/���4��!�G�+2��=aye��l�K_0&]�QY���6�'1���x�j��C��{j���V&����$=���P���v�����������km��C:Spn�}���\o(=pY�b�������Kmm�5P����j9���7�Wohh�+��BN8�3>���:9��S7?���z����Lw?(]]]r�y��/�K3���,���|E~�����*����e��{�5k��r�)f��'Zss����G�x�b3�]F}���7�����_��|�;r��w�q}���>�X�s/���f������<?�BK�-�PMr�8�Nb��z�Qy��/�����~�*�����[V\����N�Ev���S]���9@*=.
���=X� �v)ZWW'UUU���L{�V����7��>,���j���h�K<K�_MZ���'������
0*���F��24h���*��s�����������#{���;N~��_����E��sl����k��v�j6���������N�o�nWb�#�?��]�2��Nb��q<O�K����Hp�k�u��\�|2p3Tbcr�R����0����m4��t��p��
r�}�m>6�`��i0Y����:����M�L�F��M�����,�%�wM�0z]r
��uPB��c���_�	�(m���?h�]/���H(�e������%�9s����
7� �-2�s�n�n��fkl�te_�����P�!���h,./��)?��Kr�/���]�L���i9�'���{VnZ�\�zC�I������A�<^�L�*���`�U���{�����a��''�z_���l�XOo+=��C6���������G�4��\�M
�0000��0��%d�������[c"o���<��3�X���-��"_���e�]w5%��j�����T5�*F�'O��N����$���������(��J�[i��VC�+��Y���4�;�?���\x�����e��>	EbN�}ay��.�������}N�~�C~O��b`�A\.�v���:K�xL\���"����b�~�~�/�q�O��lu?���2Mgg��nt�0wMM������V����������?�hcM�����(���:
�7��B�h������k��~���f��aMI����|���u��YSD�9��inn��lM�����MK�M�6���E�t�
Dz���f�'��
��������������kt��(��%��O�$t7�P���y�|�3{I_�LlZ�&��)��u���_����f�IlB-�>�N��&6��P\6
�Mwe����Y��
�r��%H�`�c��������j6��B��#���G�]^���:�%
��m��:J��=	��E��7�6(�LA�#�<R�����]C�r�%��n�lZ��'?��Va��%K���O6���o~��5k����6z%vkk�x�^k	(�M=�r�?��?���T�<�)����#�@��}��v{$L��%��MS3���>,�`z��K,��O�)/^yjb�2
y�U^����*��Emg��������1��������]-���rQy��H�����?�����L�bng���vS�v�Z3�
��U��@%���(�h��i�FiXf��y�X��D����_�����!�2������)G�y�#�����G�])��������W#5��%��&���V�IU]�T7N���I&h�q�d��.����Ho?U!�5�f������\)������+�^�	���V�ix���+�����u����v��s��i
]k�F�qa(rt�����J8P��%D>tH��{�q�g�9�
'z��m��
����[c[�����.��KV����>e�2Y��+w>����.q%��n�O��'��M����������Y�5M�}|��$V�����%C��-J����6c����J�����b���'
UiJ,��u����&gC��~�8q��c��M&�����4d3i�$s*A��L�6Mv�qGk,I�=�U�������2M�����.�l�e���ckL�#��\{���9d�mlI5t���������T�q%����F��5d�.!V��Q|�2���;�3K��K�/�V�
�������.��"!X��5���iw�v����G�V����}}��]2����%���Yv�eklm��tR�}��Gi��w����(���-w�u������8\y����?�A�z�-k-���*y������2����?����/�b�
k	z�C���.�x\�ry��
Xs2��}��i�v�`X�ZI�[�cQ���S�&�u���Yb�+e~,4(���1`�h��^{I8���N�	5�=4lc���Mgg��cH
�����fT:�6@��veq>���=m������/���e������u9���3��v��j��W�tM�f���o~#�����2��z�\~������ZK��
GM5���h�!q���gD$����vb�����>3���i��n�nN��&i�hz:�����������`�h3s�L�={�D"3M��Z�v�	����������L�����7�i�FC:|���������J���o/S�L����c���I+����k�X�6��b�mn�t\\��%����J��u��t����u:�G~�GfMi�h4�/��C<&�h8{ "!n/�K�#�^�<�����H`�4�Q���@8�q�2�^��6�e4d���I3�3�4�
���s���ms�F�������e������aB7J���8t�v�}���L�J��`������W^�������AmTimm5�u���T1�}�0�5��eF[�Dc���I<W���v��Xb9U���)��6`s����m��uT"������#��[�F^{�Q�45��>��L���2������?^���������=���v5�|9��c������7�M�&_��W�&�"�M�'�L�=����3���w�mNV+�Z��#��=����������r�]wm�,X {���G��'�|R�y�3���g�$���)
��p�
���|��3�<�T�Q��R]]-����r�!r�A9=�P�e�]����������{�i�����Ji�O��&L�`-1:����:h`I��h�R�� ��`D�}m��^�#nW�t���W�����"�
vI�S��K�k�r�>���;'��r{}�m%&���S<�*S�(sI��#��O�j��M�`T��FL G�����N�>K���%��|>���5K��n;S�F���������l=�;����=�y�9��J�����T���^��1��w_y����&�"���+V���
�>}���R�����ZN?�ts���,���
9����h������SO��������.��N:����\z��r�gXc"S�N������kJf�P���P.��bk��G?�Q����6wIe_���*������{N�>�l�R�����g�6v�Q� �WA�v�E_O{{��9h�F+��s�B
Gbr�������_��S\������I���Yk��
��}Sx�K�}7��u����-��=B&5�$�,�M���?�/���T�Q>�K&���:�s��t�������L�5ou��:�d�����4�	�xP��c8�>J�����>�#d"����<���]G%I+�x�������n����A�l��?��k,IM����F��k�3�J��Nmt���<{9]�*2�3��-�5S�9lFS�	�����2������%��!��e�g�	�D�1ik��/���l�VU�d�����d�G�a�p4.k{B�������N+d#�x�w�-M;�C�y����M/�j�ZS�u:!H��t����i�-�o����&����?S�&�|PN>�dy����)"����.��+���$�'��?��nj��Q3-
K��C��VK�
�� ���~;�����C��N���L������}�_���H4�/q�Z�V���\�OlkZ�f�Q�������&���u���;��k��F.��"S!S+b��O�x@zzz�%��%������:�K�������t�Z�:����+t������dbz3f���
�������;R�������L��t�A��Xb� ���j���3O���6�Y����}_�Dv��s�q������hh@���T5���}Lv��b����Ryy��6�.]j6�>���]��t���������e����^z�Z`#h�������,'�x�5%���_�_���&����h]��K.1���2


����T����
r�W��g��p��4UK�`X��d�����$_����>*G���
��7L�YG~U����2��d�Ew��?�G���n����e�^��%g������6A�h4jB7:(S)I���4��x�����w�i���%L��Z�h���?���63M@�����~���6����i���Z�9����)�b����������}����c��_^�������/�9�#r��{��)
���s.�O���xk�_��&�q�6���XK�E"S����2��v%$�����)S�Hss����lnk0G������f@�(y��q��?��O9���e�����miu�o����SO����A^�
h�|�+���y�.	��R���Iu����JM�OU^�y=B�>�W,��K�.������w�G�;B9w�<��c���,�M�=n-	d��o�V�����O<�d@����P���M%���jijj���'�i���q�;�{���T��J�}�����/W]u���������3����?_�=�\��/)w�q�<��r����;�,nwqN������g��s�1��w����
����������������%2�q�D������/����"/��tY��zk
 �P($�<���J5z���tZ�F�U��J��yZ�FC7v�Z�6O?���
���
P&��#����<P~������{���|��_5a�|���>.���������J�k_�������\0R�W��?����v�x,���h>".���:���P�y�����k�����j��W������3!�Lt��T���5k���P��eHC7UUU&t����/v������������@�	�w�������|��k�F�'��dV�_�7��1���&o3"�����Y���fy 
��_������G�bo*]��u�6T:��J@<�����n��'�mxe���2��'M5^i����&�l���-�rK��C���w�{)`���h�����T6���H$"����T.�6@	�n��K�i*�h%��%S��R�u���=x�i��J,�X21��H�}�_z7�V�2�v�����P(d��\t9��OKK�5���
P��A�]������DZj�R�wK��#����*�4U{��X8(=�u�t����'�����E�����l&M�dn@�#h��H��_.�������/{S�;1[���7�HH��m���A�����A����Z����_����2��� h��x,*WZcb*����kB:�y������v����-����{����M4����m�6�m
�l����k0G�6�>��
0�������L�Fi~&�$�MO����qqy��0��l�mf��%�g��p8l�i�P���3�
�h���[6l�`]NC6>�O:� iii1�@�#h�OU�&N��D36�X���N�'�	Ft����>��<39H���J���+mmm�D�4�lc�kV�^mB7���&`���a�d�}�5�m����UIm�"��Dcqi��H0���}6�%��`T6
DM�f��@���r�����>�!S�&����`k�A8�]������c���(�6@	��5K��?)���Ec��'"�����uOQ}������	�XL��u2e����@f���2o�<���(�}�{����t�Un4`3y�d�{����O4�l��)����Ui&�y��~��E{���kdmoD����`DVt����A�m��c����i�|Aj����' ;��/;���}��r��'��g�i�7g�u��p�	�`�immW�rJP��%���Uv�����8Y������emOH����M!��K\g���r{�q��e���f������3����:�n��$�q��K�2 h�������g_/��I�[(��mb�`B6���<^i����i�5S�� X��J�`�jy���$��..��k4h�y�qqI<��W��+�_h��G�(��ny�����/k��q�D&��dj�W����1������������������`������[c�l�% 
K����n��'��OC�+;�T���L����:��l�����j��[B����]����+�w8�����M7�$�������.�k������?�A���KGG��$ A��{7����en�].��]�V��j���n���ILox���+�X�LmZ/k�%��	��r�=�����gy���e��u�000 +W��'�|�n�x�	k-���
P��A�[������D&�x����$j(�NJ�6M���|���A�]��5HO�4�]w�	�hwQ�XL���$���t�������On��Fk.@�J@��[��/q{��rI���)�������M$$�k�����D"�K��k�n�ihh���3L�0AjkkM�F�B!y��7��G1��6�����2�q�5��XS�u'fX�I�s����e�c�5l��k�}�Yy��g��	����j�:u�	�h�F���F�4i���������e��<��c��;��������vU3y�	�(���c��1���<{~<..�W�Z�-������k�+�h�F6UUU&L�:�<�j��m���N4��%�SU-���Db1���@8�1gc�'�	F������>��<S��������M�F�3���&d��.�o���L8�^@�(	n_���n/�I4����#qq�I�h��'�MQ��g�m���RiP���SzzzL�������m6m�d*�@�#h�_]�����D��f<�������"n��S����dmb�N��b�
�%�=:9H����o�26�lcw#���k�+�@�#h��J3q���u��$�� 6
������7"�A�`��]ay�c0�mT<&��z�6��R7c����ie���Z��!n��D�S��v7USSc����JDUs����o��a��"a��#�h\�������,�����tM���k�i/���St
���������A���.�D"Y�6����6����%�n�n��wo�	��#��@b��#Db��!�x,*n@f~�d��[W���L|>����N&\�������K86A��Ai�������`�v5���%�v����W.��}�oucb���#.�OjZ��YG�$;~�tq��� =��+{�������&d�����>Y�z�	����Hoo��`�q�FY�f��n�t�=���� hPp�HLV������Ez��/����/�����Y���e`�
Yu�_e�7XS��jkk��#����z�Q��v#���
6�*7�� �m��)2�|k@���}m��������o���I���f����*g\�o���/Hw_�:�K�}�<������n�~{�`��t���P�Fy��+�_h�rijj�N8Av�m7S�&��A�5���v����|���5�ImF�o ,�_��|m��r��o���l���Pb���>Y����7<���<��jk- �H��}�/�g��"����%.���:��l���F�4������|��������i��\�������{����v��J7�PH���D"3�}�{�|�_�C9D�~��&@��U�{��?�Kn��E�FcI��F#$1$���4�J���r�/�ko�ore`�x4,�/<(�����=^���P�{Z�ej�O��}�R��Y�>�~B�x������Pw�,���2����' �@ `�4���g��SN��N;M���/����.'�t�|����3g�[�1�V�3CwoP�z����+k%�`Mbp��_�$��6�i�.Uu-��W'�$H���'�-}C���
��n���hn�].��]�V��_���=����WZ���m/n�7�O��/�.���h�Z��n���V�������*��|��J�j����I��y����������?����i�Z�_�(��Z���(�:�n�i�v{����M��<m��REC����Uq�=�IlS-5^����6�6�����4V{M�R�pPzV�b��G��!h���}r��o�& ��(��'U�����I�u%��X���^��4�A�Z��%+�v����)"�����]IlP�Nl'_��<���:��	�DB2��mk(�6y����;��q���u�xM�&=
��
�K,���f�zb}��Ee`�J�RZ�����<���R�kE���%������?$o��$�K\.������L�&l��L�������r�5�0�Gj&�2����pL�4f4=�p�Olc�&L�&�b h��P8*o����\nq{��+�$��yt9��z���*�����@�T�XL4c3�e�����e��������z��
���
@�>��lk�H4�L;�c����Cfq{��X<.�W&O�5��r����m�MEcqi��H07U���xMO(*��������XA�<5�U���F��M<W��Mb�m"A3V]��YS�m���k�)��D��f<�����D�"��&�[����d]Ob�����
���9G'g��!h���j�l7�Qb��E$��%�����P�����	�I<������d���*��=���~Db�`b[��kd]oD��Z�&"+���V��j'�myk�e�!����Z����
@�Z'���� }��K����l�h$(.�6�X�
�����P_���]N57V��wL.��jn�����&J,�xbZ(��=!y�3(�7��#���t��Ih�q/���S�mP\m���y���f���v��`�"�^�\)�����V�	�u�@����^b��D�qim��?�A�/�N�������E&�6G��}�k(����r������>�u������2s5%PPm��mb�|�c����	�"fZ,����Z-�+$��A����9q�����z��}f�G��,dR;u'����e�O�)
;�a���L���a��^j�";}��=>k-Plm�i�&�/�=B��Q�{�&L��$�2�����4�d�W��Y'�����n�����Mu�9�,��������2����'�LZ�����4�0������6�w�2G.=�p���S��! }aS�F��y�v-���P�<�(S��M?p(
��Go���>K���q��U��s���<�����?/��7������
�5���9;�g-��~�Y���������;Q��������';No"d�"��������a�SwK���%��#��v�z�YY���&x����5�h hP���A.�n������+?�U_�L>s�?��_�'���������v����9Y����F$�J<.��u�.��a	v����y�����5@������\�����Ew��K�����(��A�������������$�?���N���V��[���W��������D!M����&�Lk�IC�#��.��p�l��d��7��@q����{�[����~QB���#1�����2�G$�I(������u�\w���|`kZ�����d�}��+.�K�����j�Z���j�L����_�k���-�rK�{��{�oe�}�uO�X��V���=/�*5�h�4��].��i�@C��6O������sb�*���>�����I��
�*��)k�g�V\��
��vI[�Wj|��6�������Z��XL]"��u�v�M�~@�����y~�������u'��j�n�.��6�W7���N��&J�q��ju��-�������i���*�������Ob�j��J�����1wb~c����vR,��/[s@������>���7$SM���IU�d��k���9&\���W7��+$,_�%w/{��l��n�_��nP�:R���)O{��6A��"!X��5A�<���������������!������/��/��Fp�������@B<���+��(C+�T{����J�s���e�c�5���
@�zB���M�q������[m��$n�6.o �|�t��6�.�f�L��"f\�3��&i�hzV�$#���<^	L�jM�@� �HT�\�i�%������WIp%���rz;1t�eCg��'P#U-�D�$���`8�1gc�'�	F�;����>��4C��""h���#3Z%�%�����a+��Y�^.!�K���I��fPn_���no��h,.��	F��J�mi��'�MQ�������`���!h����*��0�h�����&m�4h	���*����`n6_]�L��'$�7��hL��D$q'6!��4t���c��'1�lZQ�je��O��@���SM�'��6JL�6����%�!�t�GT<&�`�D�}�����Mm�fIn_@&�y�L��H����mE�s ,���������5��ay�sP5����<��2���J����{�B��������.�?��>��o6�H���U�*����^������^�X�S�tD�(���!�t�)�w����y~3�l��l ����vN������s��y��7W���gh(e��.d�X�?f���r��o������v���Tc}Bo�;��
0B��C3N��"��*zy����y�l�������f���������~��������`�"h�a����	�y�HH��3U'�z�2�o�bQ���R�+�\�O����M�(��Z��5*�������u��{iBsM���
���������oU�Pp�
E�?|�����@(������O�f�������
�8���������~T�~�h=���������_?Y�\�E�-�W�l���F���&��*�-��R�g��R��y%{V*3�N�����(�PT8��M��{��M��S���3�
t+��6�6����>{����W�~`�#h0����w��z�;�i���R���+�n���=��������j����Zt�7����>`Co����|�����g�o0#�+�P(�X�G�s�6����P����!��.�
�X>9��7�T�KwUj,XH-�u��5�u��\������Z7�fu�s���A�q'�e7�RO��<
,R�\F��;�����h�PT1����[y�%���3�Z���?i��}�?������������Y��9W�f�i����E_|��d��0
k��������+�eGJm,�Y�	M����&�6Lm�j������*��^�n���]���l7m���gP��W)�����pM@U���k���������B
��~A=O������_��P�{������r�L}�co���~�n��������[.z�.����������������{�����*".��QVu$���TF������&�*��Q�����
;A�q�K
j�5jh�bB!��hBM��5�&��xH������\��:fwEG��o�R���r������A�T���kc�eR��{�D����)�Ol`��lZ�+�V R�?^�����Z���������K��s
�|��l/m��5��d�7m��[��j�	+
��}v��=��&����[���*���k���J8`K�^���'��Z��?xB
�G6)�:G%\'�B>�T���~��#�+�qUm,RS��*��fN��D�ma���M0����(�(����X��Z�R�b�~���I�I����T��#�{����$�vE�`Z�H�������"A��{slmU��\�����`+x^Q���74�����j��b!��-?�+X������U��
���7u�����
�8��]��P�;�R����Jo��(��nD gp�BW�l��E���_�������og_���x����+��o������������U�5O�
Y�&�+l2g����d�7G�h�bK�vD�`��5�)M�X�4��W�xS'kee;�.zyU�MU0-oR��~����n�
�Z�'�SOZ}i�\3��[QZ���4��R[������+���}�����J������k��zR����T5��lWm�����
�����R��<;����M�_���37O�����4�V�^7�s�y�~�Jg�������J�a����yO�l^�,���~z�.������8�nP��������WP�@^�!��);n,tc��LAk��L��)����Np�����
�8R�1]�x���2��=�D<��=;i���]Q��N�TM���R�PF���z����{�p��_�U�+^������V7+M�[���v���w.����6�H�h\������M�\����I���+���y
d<��<��������9�U�	'j5��ST;m��3����
�82�M�+Z��Z@���5�9u������]Q�i+K�z ��6��K�m��o����
���{l�.��qE�A��	%&)^;A�D�"��kZ���m�`(�%����W�S2��>6b�^f��qEj�T�g]��L����,��jyoF]���CP.�����{k��g����E�`�5uh�q��?N������V�g��'��C9%��g<w������C���x��:y�Z�9��
`�[�=�������r�|B�jZ�V�����[W\�&�hU��X��lu��~�b7
l�v����3W�y�#T���2�x��kn�k�O(Q�]�~��<�����e?v>q�����T���G\;(;1�����?��]-���R�V�����i%Z���N�er�I��Y-X��p����K�)�t��@0�H�^�h�U�M�sZ����E�yr��w���li T��Q�h��lom��]��%�v��(R�(/�R���`wD*���K�{���[5��Q��9���`*�%+{�'�k6��ov�E
�rzneOy��Lw��������1q�,�Y�����i��SZz�O��w_-��;�6�P ���O���]��o�O�Ly����k�O�N��^��D�?�T=i��� ���xE)$ck�G-K�Y��ve�C��*�%.<s�����1�9�w���D4�>��u��C�f)����y7���k�<����Sv�]���4��?����T�|�^������o����&�y�O���w(Z�\��h$�����{�R��XP���S7�Vt����E��a�6n����0���]Zu��
��
���5�%������"j��hjCT�4�%d��k�M�P�{u����B�`<�U)R��h]���p��'�R��I��:����`����&����"�)z���l���m�n.kjG��*r�=Z}�_J����	4�.��H@����q#��4$�j�	���v�e�W������l7m0,`wA�-��G���zr(<eS����R
b4��r�A��C�P���P^	�x��W<�*��C��*��hh��d�i��T���r
�����+)c��\�c���T��[�&3�N�|V��]~)n\���\�_��n��ZN5�����g�������.��!�c'���<�g*��8w��:�������
��"!��T�p��J��R��4J��t��|f��or�^��V+5�F�B^�WT[S�N?~/u����
��G�R�V��R�~�U�I�����4]`�z/�R�{uylm����Z�v�^��^�t6��YU��P�R}�+��B����f]�+
u��Su������
kU�:U�B�X��L�?f*��Fe�*��c,
)��Q^��6�h�Ym��g������P���(�F�+?��yj�����o��?��������*��'J��,c��6��q��m�����#J�N��`;"h�����4����XW(��T����9S�����|�"�E(Su�t�Xp���ye�E�8*o0�cYO=)�]���V��p����79�� ������%�>����<��@��U�I��y�S?��~������6�i��OP>=���^A�y�=)�RvTY���2��W��zR8^�	�����C�`�^?�s�u����Ie��
���Xne�I�1�/���]��O��e�?V��
F�j��h��w�
��k���iaWZk���x�KyZ���s=i�s�)(��Q���Q��7��l7m���PV��Sz�����{��#���U�J����a����
E��W�f��]{�B�{�T��X�i�f��qEj�T������Z��������e����������g�����q�����
�Y���_�K�{L�p�lB��
����H�A�X�b5-J�OT����A-]����y�k)��n�^��S�S��U�B�-+�������$�d�PHM{�9�],��r����O\�1Z�3���^$�+(`�lB�jZ�V[Y��<�u��FE
����6�V���{��6��Y�g.����=
����p�U��G7p!���i��S�lwm�h0���e]
���MH�D������#�$��&���R9-X�]Z	��O
h�
?S�s���R����R������Q��J����������k�<�����P2��V�*()���lJ��.�o�P PtA��V���%E/��'�����`(,��Rm,�Y�	M����&�vLm�jZcLak*��N�o���������6c��{Z����3�:���������](-�I�C��*���5���w�E���b �P0�	uaUG�'�a�����DXm5���6����j7
��6c	�4��Ny�PJ�*x�r�f�����tQ�HX���n7�����5��iBa�����*��hh��o��!7���W�e4����Z�������&���+]���edm~6��Y�&�����u��i�"�Pj�2���JD��=�BA�XrA��
��R�K�k��B�`���M��wm{��O6�'/���P�-6R,(�T>3�b1����.��+;D<���p���U�I�������.0b��I�R`�!h0Fm��:��J����l2��\U�@�.�X����M.���P����S��q{�L7�@0�D���b��,I�fGW>��bQ�PH�����=��X$�������U�t���bM�w���3���i���J��Vj`�
��<����j�v�^��ZS~6�$�V�i���E��I�
������6�|�M���A�m0��V���&��*���%EW�&3��T�j%{V(=�V����X��H$�����w�k�I�����'L����<�x�J�����v$��7YO=i�]��}���}�m���S�lA�m������O�]�5��P�X,�X�J��v!T'":��=���<�ml,R��	�|z��g�c�s �[R0P�U~��f�_Q�\��p�J:��l������[n���_�/�P�\r�~����������%�������l���A]x��Z��W�P��K����4l����n�g����	��X0W����m��������;�������5�������?��z�J�JJ�5�8�=��e��[p�}����/��y��l�2
hpp�U�Y�p����.��i���l��������t���T(H����	(��W��U��VE����T�l��{HW���y���,����5���+R����wa�t����9-��jy_V]��<�p�c.��s4��sJOlF.�����'�y��J��n�`o`#x��l�?���t�
7��[o-��6cT(]���kS8t�|B�����kW��Q��F�j���xu��Xe�%�z��?�S*�/?����5�S�S��U�r���y�qg��7�����}��n��<���z��g]����l������I���jhhP"����Rp���<���Z�`A�Ym�hm��n�{��^��l��b5�
���y��0��:�I4�{d�H�>������=��l�-�����(�c��5���)���!-����M������u�����K��M8V[[�ZZZT__���*566/���m744���$������F�`��Y-X�U�fR,Q�B6�F\K�p4��??��������������x�����J�k��^suX�a���U��J�����������<0�`��#�lP����Y��������B���u�m*m�4�|7
�A�1J�\�`0�@0�`8^^�)E���.(���s+{������W��wi���S0�t�6����&�E�VQ{mDS���S8��2}�����+��Y~&`C�|^�V��X�(�`�)���m,pc���I.�i��nWi���X�!�Y�f���T��+��h}�X���`�:������H0�	�aUE�'�a�����DHm5aW����t��������<Ok����b1��9��mgl��N�\`��A$���:��B)iS,������M+V�s�E�"a�4n���/������������jb���
Y��>r��6�\F���*�^`�h,$Sie�x<���������S�����6cT_���:y����������:���\"���:7
T�SJ�Y&Y;�@@�Hp�GU((�#��M>�T����V����y8hc�l�����*l������_m���.�L_|�~��������`}��y�7.��R�����o��c������}���Z�T�h���������l�O^>cW��[l�XP.=�|fH�b@	�i�+��q�^�r�����J�������n��>/�T�guyx�U����_62��Vml;ca���7
�A�q������=�\�s�9,/�v��4k�,~��n����>��������9�M�2E�}�{�[�4������N�v��8�������u�����c��^���~y������u��34���y�d���"��]~���(_�����M�Z�X������r�7�@0�D�d��b������B�3�XT R����x�e�����6v}0�No6l�L&544���� h3.�}��Z�n������K�h�"�u�Y.��x�b�]�������a�+W��UW]��'���.�=6�Z��G�����y��Y���Z�j�{�	&���*�
Fcw9���z����/�M_�n�s�z�[��������-o	F�EC:l��z�a�S*�w!�X��]���zW���O+��U��RkT�����Z�����������gJB�j��:d��,c��6��q��m�y�i��@8�D�[
�H4��{�=\�&�������[����<�5�����`�mc���;R�6K���RY����>������*/��l6���W��c��E]T^�u,X���X{��y-�i!{�����/����<J��}��i��>�������Y��)��V����]��V�}Ui�_�+/�r��Lj�������m5Jg�
IQW��K���5��B�����\�+
u�>Su���/=	0B0Su�t�x)���+�W�+V
#m���f=��=w����������2��O�����T*���Y��*�����w-�n��k{&��C��
��G���l^j��o��Z�|�p��X��w����{�9-]�T+V���>�������6O���
��v+��Z����U�����9�����kKzzzt�y��������`�����VO����b��������*���hwMe���������K��U��@������O�EG8]��oi�����4����yJe������<X��qpyo`C���x���!7�������\I�@�ST�q(S��A�8+Z.�S8^�	���F��u�a����u8lc�m�r�k��y�X����*�l�M���:�m h3n��!�?�������A��ls����Ov�X���<y��\}����J,!��o~��g�����{��A�lc���w�[3f�p�5i�$�Z�^{my��/i�Jz>��w]�����\Q����^�.MQM����*��;{R1�U�Swi���+��/=	p,����f}�?���{����C��1
e4������)Mz�[��O>�oz��*	��6�����[����*�2.D��G���Z;��`�S_�������d��y�^�5�8�d���g���Mhhh����>�����z��\%Xy��9��#��w�s������P	������*�X������r���zE"������*��/
�\s�5���~4�A�)V������z����k��'9R"�������m���:�Z�t.���Nj�5jh�"�vR/�	5Mk�kBmT
����"�\�����c���@P�PX���Ku?y�+]6��X��:��m~��w���G}��t�ON�O?�o:�����.-
��4���7O��?�pu�
��������iioV����J����Q7}��N����klG}�N=�T��5K555��uke�����5g��v�i:�����{��vb.t���;dcf�|�H_���]�fS�r�;���F���D�M7��R��b�lF{��������kY��_���^kK�vvk�I���SEyE��*����b�����+;l�ll�6RGmD��[�b>����Q����
l$��6���*�:�I�����j5�'T����7����9G�|�
5�u�k�m
�����<��U���J6��v����O��R��������7���N��g��s�=�U����?��O?]oy�[4e���W�1�6;)�bs����(dce���]�������9�����[b%�����lPZ��/���KdZ�K�V�}��[�Z�^k�s�|���}-����O)�����]��R�ft�iL���6���?C�(�R��f�Q����[:�������bQ�PD���oP6��Z��*�����kp���nx������M h��y��'����^���o�z�je2���R���-�[��\]]]���c�9f��;Va����`����g�V���[6~-+c���e���^��Q��,�z���A���hP���l�����s'�!
��.���ep�=��S���A�aw���DY%6m!/���[/��?�HiG`�:�����+��B?�������N?��Ou�UW��{�U2�,o	���N�_���k52`3{�l]z������������R�x�C=t��/�J���,���Q����R���c��������7�G��L�:�{������\�(��;i���;��)w�����^>VMx��?Tz�
rY����m5aMk�jJ}T��P)p���'���������g�����w�1~�a-]�T���Pww�,X��9���E���T���X�fd����x��z�{�������m��#��P(���}��GUUU����q/G��~�����{oW�n,���Hm�Y��U�h���(��'�T�$����v�t��+�:Y���W����B�������ov�_����C��5�	�5&����mf6�T
H��������[�H�����M�W^y����NW����z���^9Wh7�g�Y�_�^�_�n��6�PB�f'c�$�s�=��������&N����E�d��F�_6�N�5,���>�o�-���A(�^k,�s��s6~���{��T���P�B�E�sy���B5�T@����}�<�[�(Z�TZ	���f���U���ZGCAM��**U���8Vx8�����4�_W������O �f�y���k����n������4a�w�~"�p�[788��{�u��������Z�x���������+�yyX�u����x�<��;���]����
<f��3�Z�����ZMY)��_���j���U
������?<�s��jc��k!����\����a���
'j�[��T�����	Ba�RS"�hh���v>�*TC"���}�=YZ	l����^�Z��~���knv#|[[�Z[[U__��XX��������8CCC�f�T*�����>��g�����x�&�f��b�Xynt�!9���B9Gu�VU�iiiQWWWyN��W������V�������\a-��9��
�
���n������/���������F�S��Zi{��;�����f��.�c_f���]"x��!����I�]��~r�W-P u'j&�D\�a�������L����Y��<�<?��Y���:;E�X���x��]��q��Q_�_�����'>���~7o�G/��B�q�n~G����u����jkk���}Og�y���;�b��l��Z;ewgS��Z���}z��w+�v�m�5�Tn�4L�_�5��������;fj��\K�o��U�����]E��g��,Pc�lFc���wE�;������:���0~p�z���sm�����x�/~���������6��=��#7�7l���l,l�R�6�����7�y��l5#�6�^G�P0����U�?/Ua�����M}<��XP�HH�BQ�lA=)O����\Z���U�1V�y����p�W7��e�a�.�-���r!h�:��f��z�.���{��sA���1W�fS�;Y��;��������&L�~_�Fa�:xA2��%�\������E��n��k5�v�Zwm���5k����w����A�/)hc�V� ����k����m����C4���7t�y�m���������k�q�5��"m.�����c��Tm*�-�����s���(��-R�sa�H��M�}=��4Ew��s��Z�t����!�kB%hc�R����� h�:���
=�P��mVnX�PP�"j�
������X=�����;�S7c����K+��Z��6�����^SS���b��6����������[[�s�9�M�x�q��rxI����!�N��6�N���{��}i�Q��`'~�����S�uG�����_R�Z�lJ���]5��m���K�)�����_Vd�����?/��`0��a��^
������U�1��e�h���&�����m�*��P$�����:`$�������]o��n[��i���~����<��}��wP*��/gE�mm��qE��|�+���>���6���t�1����6�hc���
U4�c����hc_\,)l���jxE�*zY��_�u�_��E+�j�����D�&��c�����v?D��������~w�������6����{��
��*����k.���p�F�`@�5a��DJ����?a���U�Y�&=��HM�^����m���S������}��km��������6��~[��*c���w�0��:
/k����n�G���6_��\fsA�[o�UGy�K�X���_��+��4i����W�]t�?�����\Z�|N�+�U��M��f���5
�c��|��&;���6��A�k]r�b=q��4��)B���D$�������bQ�Y�U���5�BQ�HT����N������r���/��7��V__���f��iS�z��u����2e�>������A�(�$�|�<Ub��>����6��r��T��<�+�m;{�W��;��`0�+:�����
W�*V��x�D5�y�j��Q��	��6������1�s0��*�kQ�}�����*��**��zz�?��z2Z������r���+yw.g�I�(?�bvm�����glXx&�No�����o�������6�������!�H���|��5/�k��%	6r�//@��z��;�������Gj���)RU��6�+�/�������0W{�
E���v�h����9s\c7�[;�l6���U����
����sl�@�c��Zm\���k��l��VW��Kc_��K�������|�&���?��_�N�~����O���������F�����~�|j@��OZ��E\�ZL-��W��M������]���u9�tvv���U���Z�~���]�B8&k���joow�0����l����z��'^�n�Moz��k���ZO>����MY�4zr���U�V���u�1�h����9s��M��.�|��_t�D�������?��~��:��3����u��&O���O>Y�_~�y������2��.��a����t��j���Z�~�U�Y��[���T^�����7��k�>�ebS�����x��=��Y5��-�au�F�^Q]<�B���r�~�}��}�7l�u�8������:|��nP�����u��z�juuu���]��|����|����r�!�T����Y��4�t����k��l�kml��x������t�QGi�}��7��M�~��Z�b��{�9-_�\��s������������.=���n��`,4�����0���������w���������}���j}������f�����SO=��%l`�Y�5���{�~w����*����������#R.�i(��c��K?�S���	]i`���W��wk���I���TiVs\��"j�	kBmDS����S����������LO)�lJCC�������t��x��f�vnd���	���:��S����6��+��p�
c���?�������/�fc��F~ho�k���zGR���������wu�i�������Y����
��TX�+������U��@��`���o��.d���k�u2�������===n���u�e��m�'������i���Jer�B�R{�h�N��vU5LR��I�h���Xq�����w<�G��m��r��Z}�_T��,��SM�
�:T��/
)
��*�����}�>w�Jw�R��W����^y�����y��s��m�a�������'�m0&g�}vy��>hc�Xyn��q������F�l�Z&�����}��|-����
x-�@����-}�_��RpUU�N:�$}�����>��
��W�.����������*�lsR��{���:��1v���O~-kQ�1kWe��/
����O��%�=�P��E�J�OT�n��U
��j�i)-�nV0h����[����y��~���RX����B�GwSUX5���k��c�>rm��vR!�V��mo���g�r�U=�\?��l���jllTSS��xc�!*��d2�nl_�p���������A�����.���n���
�/vh��6v��������V8.���Y������\��T�2t��?�����7�����_s�5������&�m,X����sm�#�<��/�|�}�������U��~������{��t�������mk-Z��|�3������`����N���+��\�F�j[�����A7�?�1�*Z��H����`�m����r�s����|r@�����r��D�?���FcU��r�&�Uj���`C�����w�G�}W�kfmmmjmmU}}����\�����-h�e��*8[�>	����
�q��_n�����\y�����/��X)�MU����n,�ewj�����J,�CE�Z��C��+�����Hd7#�����B�]���u��}����e,�220����N���7��h�.]������fc���V��{����#_k����\�ZM����s@�����@2�g�v)��!�.*�)�}�@0�oS�o�p���TN����������c�?`�\	���?�7�_7�b��N*��Y�^`��2M6�u�mX���w�o��kq����7V�����=�@����T���17�|�~��_��6�N�|�cs�������M�r�#_����.������-[�L��{��^q��g�/`��=�����=��~�����g�q�v���Cq�a���7�e������s�N=�T�O��v��������,���R���oua�x<^^�b�HDg�q��;�<7_9��|�rW�vfC��������
C
�.�9�	����eQ�T���*�8�7OR�P��o�����ws������?��s4N(/^` V�X��4���6�2jSl;��g��nV�\��`�#h�1��+p@y���l����m,d��7�����+_��;!�)�>�MozSyN��������)�9���]I�����/��/(dRz���S�:_|�����u����#�9]���<V�;��_��M�n���n��\��b��I���Hv�����]t�9���\U��e���S	��w�}���ryO�Wv��=�1��P����]���v>��o0���I7�P�F��K5�26�\a�9���&��D Q�u�-^����]�v8hc��F��>��rm��_��n���6������>`����;���~P�����2c'h���?��#�twa���_�R�����M����R��r�u������v��O��{�����`NG������L��������E�?�}�o\�������������������@�l�����1v��b���z�;�Q��2;w��{�6��R����V��n��v��"��,�m�]v����Hv�vV�pH[��w_��������T��I��y,VKC�Z`B������c�+���+��]�3<�YO�)�]�V�6b�h:;;������X?������nh]+����s0~��6������g�?��������*���9S�f�r��|�#���H�y���7�������}n���5��\�f�k�u�YZ�dIy��|�p'����!���K���>��E��O�����5,B���z��rC}Z��_����t������������o�9����g�.�m�9s���[nq7$�p�
��(�l����s�=�9�s(c=�b�����\����������&���P(�X,��e�l��������x,����4P�i��7�]���������2]A���W7�u�LAk=wOU��)�R�A'�����z�]��\k��M.����k�]�m���T�����
���g�
2#�p�d2���u�k��+k�q��ZLY�&�H��l�QG�+��r��������VQ��E]�����cz-vv}���]������ZV�8	��&�i�QM����*����6!y�����S��q��I�J�,X�y�����i����Qca��'L�����c[[���H��sO�z�����U����v76��]t���`gRkZG�@X�!��S!�)� FUP.3�F����h����:�$��u�c����*�2�}Tw*�E]i��k0��?�i�@N��f��y�^����8�����W����X(�X���]�����mg��^KK�����_��P:�a��|�T�r��.�O<Q�^{��,s��������~����9dc��N8�]w�u���>������>�^�*�TUQv�
/����/���
C��_{MD�4��QUc<����&�G�����l�G(��7�R=O�mg������6�<�pVb7s�1�����������o�Y�>�����.�vj,��]��*�#���3jo�����`2���|F����j����t�F�Bv.=��P�fQk9U_���p��H����q����wa�X���z ��=Y-���k(���pl����n�l�x�9�g^�*Z����b���)

�m,hc���K���'�����n�}���d���qdwF�����1�\s��.s�y�
?�G?�Q]|����7��
����������%e+�e'o�M����p���7�����Z���>t��V-t'a���RV[uD�pPA�������?�b!M��(��Ay��:��V�����`�Y�b��/_^����hy����k_s7��mosUk�N�����=���;����e�������]�{�x<^^�uV�^�'�x�<Wju��7��<;�X4������:K�t����0M�w�2C�����O+��S��?���r�(���Jx����F�(��~����S��i��U��Z�x������M�?������}X{���~_F����Z�R��n����r$��`%pc�6R���$Q	��6�?������/}����q��/U]]�+A<}��
����\������jhhx�I�ma��J��/~�����k^��`g3��)��v�%��hPMU�r�fC��cm,��DH!�[c0��r�dy-��k����.����Z�����7��������K/��U�Y�l������)���Z?��I�\���]�^,��_�R.,/��L��������V�3N�����3V=��*�d���{^C=+�\�lz�/.���]�>l��:�����8(V��/!w�f�Or���.�+�x��z�s���3fh�����ha���N�����|``@���w-�c����������`���*�s��Tx���XB�NxY�g;�5y�d���i�w�P��R1VKu�������1�*���Z��S&_��Ij�/�U
��������������B�vS��^�3�8���VU��s��7��*����t��g�y�6��������~����y�����X{����`��C��5�h�:��c7*YKn;���cUy�?���.�3��u�����v,;�bwg���U<��
�����}��w�]���\��n�w�����u��z
�������>��|�l�����ND\�x��Z�����9�
����?��Xq����1�O8^��]�n�u������;lFoo����J���GfdE��4r�����t���>��`G�8�������J������
�}���2}����T��'\�fJ}L�Ua��[���Z��Q*[������&��
R��
�����"���?�����s:���\���n���N�w���a=���.�{��n�](�\�u�]���:���������^��G���h����5�1?��t������X#�6��F#/����^�-w}oJ�,��O������|���D#AMn����5:d�vM�X��N�`4A��H����i���E��*��*�x(��[�9O���;w���P�V���������I��6��6O=���Hha{��]����w*�eU
��D"��~	����&W�uG!h�B�^
��{��������6����;Q�)��z0�iE_V�|Q�����+���a�-@A�\�}�Y}������_�������g�������q�y���P�n�N>;�`-������l�*C>��������T����y���x����XL����\k�W��A���#/����g�ok�}��e�����t��V�O�"��!��T�=g5���:T���d=��0
��;����7���_�c$�[
���j"��
�6�����9�U*kQ�8l����}B����x��6��;Z���K�������N����]��uss��>j��x��ja�M6v�6�+��
����_Jk�^���>rUm�a�S�`#!������:����
W�i��.Q�.{��m�ry��'������������g>��,���a�o�?�|]u�Un�J���_��������m�HR�w��MMM����r���0Z����l�PP�lA���B�<�Zy����_.W������M(���j�r�.�=�Q���[�?�
���{�w�^6_
E5�96j�o��O{z�;#�O�H\���]uG����-����f���a.�~������Z�
t��>	�����B�x���,��"�����
���7S��j*��������zE-���;�
���g������U5azy+PA�/�Mm�<�U������2�=��>����O�Sy�4w�\��g?��{�Y^26��������>��z�-�\|9���u�e��;�_I#�6�o�p���m��H��	���!}������.er�7v��K�=���ms��4�)���|��x���u�	��J�Y�'�}�
��B���d�Q�h������+��f\�������7~��S���-�����G�L��	����>��x;
A�B��j�^�B�p��V-P S4PGMD����;�'W�8��z ���������h�Q�W(�(=!F�/�Mm�����^Z���?������NLGG����o���Ow�c�����t��/�����-%m���/&Lp-�X��d�1��.��1������5��t0�H�F�p����e����gS���*j��F}�����]�yoBY��]���k��V0^���Lk��&f�J[m�8��rZ��U�XPu�,���k�D9�;��!�a���+W�;\�u����5�K^^9����Yw�SW2�T������{/���l�LR����}�%d��YP��H�����u�{��������W��c�=V��z�
��W[�f4�o����L3��1
�{��O��%�>^
���B����'*^7A��E�u���(a�������+{���Q:[�B�`��-Em�9���2vc�pq�M(UQ��L���e"����:��`���+��
�83����y������(��=�������z�Z3�S*gU�$/�R�i����)�6��,�^&N����K3g�s��I�&���}y�d��%���-�m;������(dc�b�:���z��l`{Z���Mw/R&�wak��mU8V��w�����`(�hu�"�z�����xE�n������?^�����B3�q��l�&Y�5��`��q0�pm�B���J�]��v}����~Q��:2I<Oy���WT�`���U��Q���h���P���)�
�����[���l>����NKK�v�m��\����f���-���������;L���+/����>����������S^
����zfI�"a����)�)�}�@0�h���&�*��r���K+��P�ZUfX�;w��;�m���G9�&Up��HL5g�u`�!h0��M{�f��+M~�����:WZ�K
*7��p�N����7~������Q�����`yolO


/�hc����-���e�+!�O~�����nge![���~�=�z��8dA���z];(��$J��M*�������������[�1����j5��`Wa�X����9W��UN��U�������
���#15�>��l��V>���_��T0Q��9����5����������[x��:�+�h��~��C��XSGy�(W��V�`,,cUm��B6�������|��S	�D"���.dC��x��{Z��G�b�����`8���l��]���Y~�w0���I7�pU�&�^�����
{.L����u%�J�
�xz~ �E���������qW�������	����n�~���������'��������\��_u���+�*���0�x��Q��v%Z��e�Q�zM�y�����N(�xS�LQsssyn�ZG���%K���J���5a�����*!��~���!��3u�T]|�����@���	���R��0;��
^�Tnds*���������P���
k5�����K��I��m���V����;��=Y��)��*��6�k�;?^~`�x�]r�%z����x�b���6k���3�<����.xc��1�Y�k���O��7����9�����5�\S��:+W����sRmm���c����6�9�������r��?��j��kbk�k�ca���Q`�I[�o�og����L�s��H�`P�s����]�HU�;��n�]T��]%�@ ����>����4l�}�����t��wjppP�lv�
��h3����p�
���;�:@	A�U�*���5�<Wbw����z�j=��S�9��4���{y��6������_�R����l`��xX�&�������~yy�}�|���
�e�(���2����P~�O�n�������d��
6���-jS,hp�3Zq�%�����x�����O?��WV��UWW����U?���W<w���`�}�Q-Z��m h���������VC�`���?�;������c�l~��_i����%�~���c�=�<��t:��~����l�y��k�������j���Lf�|!�Vvp���u��R��7���rC�n�ZN���t�A3�<0��r!����d�}��Q�\������Q�jJ&���������nr��h����o��v7o���=i[[�Z[[]����e����8��U����[]kRA�U��#�������i��a~��_��o��]D���Kt�e���H�Db�m������������;w��dC�F��u�~���Cg)�����lf@��U��W>;�B>�B��N�����QTKc����=4���Q�P�����{���K�`�}���B�������j�j������6�� ���l�Z-��"e�G`4�\�}��J6v\�hjjRMM��TB5�h���:��|?���s�mm�W�	&����r-����Y�d�>��O�*3��������?�a}�k_s��@����v}���t�[�l�~���VI���f��]^���:����4��Z�L�U�re���[�d�
��*���W��
:d�)����P~��*������G�����L�
�&�O����	����:,�}M(*�~���w�{`c�&t�����^ccSl���ZUUU�y����6�������~���"G�SO=��6�f��i�����������~�������o~�l��������_�jy�[�x�x���\���}v�}w���nc��M�������������k����:r�i�H��P���X�������\���DD�<� ��A��1^$�,Sr����T��\�P}KU Q( 5%�������!�4$B��TQ�iu?s�2�]�>�����J��)?vv�Y�f�p�&���7����bn���l@��������Px��]f���/2v�k����M
^[���'>�	����y+�~����3�p�;��_�s�=w��`���}�{:��3�<^�~��_�c��R�TyII4u%��\�����]�x�����?�����%/f-���qz�q��c�����d��s/��a���U
�F�H	/�5]C�������%Z��O���.fGC�1�Q{��U�<K�NkR4�{�x�O�k��~�������R`as�uT���l��TUS"�B����x�v�y������F�����E��"�
����������������{����������C�H�����8���V5�Xu�~��jllt�0^p�z���sT�^�,s�=����N*/)��5����������7��(��~�~���m6dc����������.�l�.i���7W�
�������O�B�U�4�����8S,x���Sz����0S�Xp&Vf7i��>�j�Z�b��ut=��P_yo���2��c,pc*!����f�U���l��
��g��9s��W�������������w�������^{����n��6WQi�������
8�;�_n����9�����;���/��N=��j��M*tes�,��o~J�}�������0.�B>+/�r!�-�\f8hc=+e��,����b���{-�<��v~�*�����llTZ�n�ul0�innv�0�������u�}�:
vn�����L&�������E;�`a�X,�D"�.�l����k��P��l������� ��������#=�n@���Z��[�L�_�������e'����j�p��u�Is�r������W��m�31)��VK����LJ�PPj#j���:�����beV�I;��������
�ti����P�ZM��?V�bg�N�u�UW�Rv��w4��d�!���h*m�l�mo�ok
������3��
����A����
��w ��x@W�����`��@0�h�V�`�m��3)�M*�IZ���*jr{��}�:r�]��`gW��JW�6#)�~����G5��	�ID���S$pa�����Z��u�B��:>I���+*x/���������;��O?�����.�m����XX��!��������+�X ���Ns��`��8hC�(�1J�s������	
�U��a�b���V7�G���mS(voV����>�E+z����]0Q0�����X}�&x���!�_&_������������.���
z�N�sUi&|��o��m�?����3��~�)�+��b��5k\�fhh�-�J����wU����j6t!(#h0F�})���
��z��#�	G�\`b����V5�a������5������'����Z�=V�s��B.�ZFu�rZ����!O�O�iO����'�T���*(���C�]u3hI�M�
6�v��bS	����#�p��������q����;w��@�`��R9-X��H�Z����W0/�}�@0�p��5�"�P:�g�����[�h��S��n8l���z �e=Y-��j�P^9E��[��jw�S3����3�����SO=Us��Q:��x��|>�m����:���u��'��8���h����I���U�	G��k6��o��K��l���5�[O `#���G�?�[�q?�S*zyw�dE����������������?V0+�
l^KK��;�8���oU]]��B4�hT:���t�����jK�o0���p8����?���P�e��/�X�l�S����j���v��oj���V��7�c�7��Q U�nj��Y3��IE��n��z��'t�}�)�L���HV��ZH�u�]Z�lYy)���
�5��UW���k�S�gdmV6�_W,�+d�\8Tkc����FS�1C���������@��I�vQ��f�:�3�x���Qm[�B4��_t�whpp�����_��T�1��i������������7�������u��sm|���zU,Zu�M�m��ri�R��DJ�xD�'R�[N�(�2I��6W���!E�Z�����#�<���zJ�L����`Muu�������z��q����}}}�?�/^\~A�1���i�n������h2��]�&�6Q���g���rA	�+�:���w�u�����������������u����y���a�������l*�[�����8��U����[�N���0����������i��&(���+e�=J�����e�O��_��5J��V>�V�(�VG���^��s���
�/ke�i,,c�����Q(���p,xc������^�?��
�6�mz��:y_�c�r���*=�N��N��W�c�r�>y\����d��w�S~��<���+�5V�����v�I$n�7+W�t�0���F�5Y�?�X�~}��3�{wA��5.\����t�S,��{�}�C�)�
������Y3�����qs���mgl���N7
����Q(��{N���:\��q�^?�Y�hX�C
&���MGK��v�������8a�jc��`��j4����2��*\[���~��f���S�����
�K4��^��FW������������E�����/���p���$�f
v$�b����A�&��m�������)���y`�#h�2H�#jo�v��}�0A��;M��6���F�uq�[��
��U�ijjrUllXu��	�d2�ha���f7
�A�� �2�W�P(�����������l.l�J�444��-hc���N-�j��9�j�%Tww��lc!�J��mX������r*�g����`�#h���I����ea����k�}�U.�s�,L�f��I&�.|388���.�[���pL8�A���7�A����e����c����u����_�^g|�Z��/����\�t6_�v���*~��jnnva�Vc����.X�z�j����w�[_(4e���;��,�6/A.���}D��������}�����{���_O.^����X?��}����yS��;TSS�N9��F�*�x��B5�h����
�X0���NR(*?��
�6Z����p�.��q��M*���
���j�)���?���O��W.�����)�^	���:�����w�������v�kea���F���z�{��ZFY%��l��TNW������r�	
��&j�iSU}�bU�
G�
��:�s+{�������6�J�����{��O<Qg�q�>������O�9�������:���4c�E����
�6cd���|^_��B����E����P�v��������iQ�7�M
�n��+���+�)����;������u��&N��7��
��MCC��r�����
�Y����Z�d:Wj+Z��p��M��Q�`H�pL��&E���E
���]���%�'�W����
��e�������
B�&�������/�"�T�+IX����z����������+O6��
�%SY-Y��ZF�A���M��R#q���������:��zzz���CW^y�.��"��w���~�+]{��z���N��[F"h0����+{T(K��@�=T���lF�W�
���9�w ����������e�]�5.���k������W�����?��O]~��z����{*��A$R{S��^���XP��[�����+�v6Y,*�?OsC�����<]}����Moo�2��
�����e�@ �|>�T*�B77�x��^@�`����h�uUm�.@�Q`�I[�o�og����L�s��#X���G�O<��B�B!����Z���jooW}}����Z���8�����
�@�`���aM�X/+jS,x���U��V����
�g���X(�������:��,8�����n���[�&���U---.`S	�����&W���������9�6c��T�7�i
&�n��O+3�NE/�@ X�������������T}M\�4����-��i���.,c�����u�m*�{�D"���s���c!���=����g����
��ca��.z��3������\z@��U�u)�I���(��Sz�S��5���?�jnH��o}��N�/?l_��i������^SSSS^�b����D��[�f��n�;�6�`��:}���jk��x���C���{^���J�U.����������9S��cv/?l�Y�f�p�&�������mglk= h����6A����z�A3��ynY�Z�r*xY��+p
R[c�>u�A��i��`G�j4����X,��������f����wm�Q0���7����T�f�U��T�(=F�a����������6�,��MCC�A�|>���6������s0~��F�z��������+
�eV%�4
�������/�W��i� ;L"�PSS����U����M&�q��innv�0���C���r�3���eLfKukE����*Q��XU�B����R�+h��n]u�3zr�:��#�B!���
m]�fsa�t:���!7mA�@�`��B��O>���}T�`�U�	E����P�v��������iQ�~�bUM
�n��+���?�S&���
��h4���gW���r���v�m,DS	����T*����^WYn����������\U�`P
����(�u���-��!�1E��I��{U(�`Y�n�I��`;�0��3��>�����0��5k\�&�L*���J7]]]Z�n�p(���x�����p�0�������zn��a�R4Q�H����Tb�`(�HU�����a�v���*~��jjjra�Vc����.X����B6���.tc����L��C9��,�6c�Le�dU�k�$���f���]Q�tNKV���������SN9����*6���P�=Z���
�z��:���\U@	A�1��Z�����r��@�=T���lF�W,�+���=iu���<�(mmm:�����w�K�������])��hll�{��SO=Us��u�/ h0�pP�M5�{���bQE//m����l;�����Cj�O�y��jkk��^{��O�������:K'�|��>�l��}��q���3g*���T�����:Zj]U�b�S��(������@N����aM�P����`�����\;�I�&�����*�X������8E�`���aM�X'+jS,x���U��*��MQ���r�!�%����u�������������*��������7��u�]��zH�t��%`$�6c��T�����d���ie�������@i��O(;��O]������9p�[;��?��.�L����}�Yuvvj���Z�j��|�I�y�����+�d����
�6c��u���t��3���U,�K�+��J�d����k'�M�)=�F)x^�E57$t�qo��*���<��5�\������Q&�Q�PP0X�ll�����R����y�p�
������J�l�����w�V[c�p������R��y%{V��M.��b!/�Pt�n�=E�y���g��5������?�d2�P(��WUU���Ammm���W<w�g��0��y��l#��F{�6A����:l����O+�/���U�ld�c>�)���*���w��?�����cXp��C�v�mn�*���a����aA���566����&�����A�z��J��n_��l�`0�9�M�g��`}���^���:Q�`F�������Ml���}����������Xyo�1r��}�QW��*���P��k���jl�=F"����aUp,������{��l0��x�&���]G�A_�������C��D}��c����K~�X�������EK�Z`G�<O��-s��TWW���M������U"�p��Y�|������
�� ���F�'5�P��N����.��X����`������k�+����{�&B6������f�7
�A�� �2�W�U�����e�lb��k	�%�]E&�Qoooy�/���N�����������M>��bEc�U���<0����X"�Pcc����f�[�����u����j�����X(R[[�p�fpp����\p&�Jihh�M[������Z4����]���r9uww��5��n����lz{{��M�2�@�vj��9s���{o�1�Y�v�zzz�L&���]����.�[�n8�c�p<�@M�8���xG�vrUUU:�������B5�B�*�X����������l���)S�h���n@�������}�������B��k,t�y���VQV�f��w�{����a�-��
�����{]�k'e�rM�z��i��X�B>��������g}�Q7�zM%Tc-�����W<�vcm�,h�d�7 h;5��Y�F��v����P�HD���nT�6���n����U���,ls���*����
�7�6���r�?����\x���jjjj�]��2,�SWW�F��Mww��� h;5���|�r�B!����v!��
7��jkk�H$��nlA�I2�t'q`<*���K����e�h�uT�T<w��ca��T���2�>�V��������^����k����7���K�������������O|���sW��n��_�^_��?u����([�v�Fc!��7D�b�Q+�l����s?����9��x�V�X����/������Oh��eZ�v�:;;�x�b��������������P*�^��>{��������+���.-Z����t��{����|X�������U��`��*6���.pS����X���v��=�wm�$���sUl�{�9�.�r��N�����r�����]���+���u��{�k[g�����-��-O��CJgr�����!)��R.��w �yOw����G�]�xyo�1���y�lv��6���s<MMM[�����f����r��J��h4���:544�QSS3���U�����500��������~s�|���*�2yEBw:��W����Xu������JE-���_nFw�_Qz�B�������6�������3�tz�2�����ml����s�=�E��l�6vg��t��J����Y���������'�t�m���Y���.��w,P8T�� �RU�$�k��nR��I��%�:�iu��B��V���o��������
��H$���g���������b��U7�h�B6v���n�����6��U���M�N(�\c}�����<��4�Ds8��[ya;�c����E����������/��[�t6�`@
�����)�Q V �K#���7�R��B��M��O�>���l�}����3gj��9.dcR��;�c��J;p�tc[^	���oz��4q�D7�A��N��	��]��B5�����bn�J��M�Ix-Je���.E�!��9�D���Dym�w�H�	�j�Q��9C������Z�����G�*[%;gc����Z�~�:;;������~���V�f���:��C�� h`��DK�����h!��Y ���r�v�������Uk��'�����*!�#U�5�R,�m�vEes�V���Wn�;BKK������6P����x�����y��Q�q�6'�x�V������
�1����p��t�*5�e�3����e'q������\���6��ZD�R�fC�]�ti�~e�����4a���-oq���v��N������mN8�~��n���&Vn�N��Xp�rBfS*���SWW��}����.��������
^v�o�u�v^�T4
��1�D,��`GZ�p�y��|�����MOO�z�!�Z���PA��6ijjr���D����p����6���EU��444��������$���Q/�PT��+�������
�����������bM��P^;��h���:�~��.Lc�vS����hm�,ds�������.�
0ml���'��6���}}}�.(��VN�N��z�;Y3q�J���IDAT�DJx�j��k���J����K);���}������M��P@^6�����Q���������/�v~��G�c�=�n��J�vN����
Qmmm����Xe�MWW��l�t�R7 h`M�>]{���;c'_,<�n�:�_��U�I&�n���k������u��|>������>���/?��4�'t��_��vmS6g7 ��Q�w���y�!$�O(3�F������*������;S��=��l�}���5k����ns��1��6---jmmuA���566�y{���������[��d���A������Y���;Ic!���w��nltww��Me����;w���k������w��h$�L9l��&�X�T�?V��k�M��X���E�?��m�������`������*�Xx���-�U������M$q7GYu{���>v��*��x	���[��~����b��M�j��a'e��T{{�N:�$���o,?����}��o���Z5������� /[��M._P,�i���/����EB�g�����,_�����@Muu��`c��6Q#�v�I$n����-[��`�#h�%��Qt��X+)���N���i��;���G�N8A�����S
v�PP��;M_=�p�|���ND�� ����b��v������!:�����X������X��J��x<�7��6���� h�e�F]�c�=V�}�{u��g���O�i���~��n��CQGG�O��kM2��Mw/�=��T&�W�P�Q��P>_��5����4����:�1���d,,Sa��G�d�1��"������<�A/�XIa�^3i�$�j�]wu������;�`g��{H���-���Oj}oR^��B��b��.b��W((����g:��_���o|��7lV�����n*,t�57CYK�
��*�xG��vQ*�;����~s�|���J��y������D��5-�W�(V��p4�@9x�lu����i�=��vk����X]u��	�d�Y�h�y����T>�|�Q*����Z�k�X�p(�.B��U�j��xm��UM�V7)V��D]��5�
�B
Z��_���Q-}���l�}��O[[�p�fppp�a�t:���!7m�q�?��
��u������)���
�Ca�����(��#4<����D�R��BQZ��W�=���l�}E"���^.\cr��������h*�{�a!�����vS������TV�.�R$R R$Q�`$Q^[,?� +�U8V��9C���Y����/�h3s�L��=��kL*���5k����d2�|>�*�Xg��u�������I��<�wm0,;�]���j����$eE �������M)��Mi���9O+:����E��V]]�#�8B����f����=Z��������S]]]���wa[om&O��C9��,�6�R���b>�5�_�W���������v����\=w��\���l((�/�����Q����m(]������7��������Ovm��=��y���5��G���f���:��\�)@	A����Hh`��z�g��?~]����z��[���R��G�n�����K���K�n���^��,(i����*R��],��ep��M����B���BA�4$��q���������;N�{�jkk]k({���L�0A�x�;\������C�`\)���������o?����T�w��lJ�`H�PX
E]I�|j@�u�������/�S����TE5��^^����W6��b��@m:l����������b�2���v�E���GU*��^Samz{{5o�<�Z���PA�`\)U�Yu��X�����)	��:�)
1u�����X�
���'��������$/�,?�[}m\�wmW*�w�vKvh��S�s���F�����rC���RU��D<��w�����B4�_�n��6uww�y����6#3��V�\�n�A��sOyo�!h0�rY-���\����;��Z�T���9VkM�n�6D���{���-����}�>�Y6������>����M{��M��'�f��Q�w���y�!w�J>=���Z�j�si���5����;K��3��l��Y����s�l���\�(�TUU���A����MT<w�������Ci��en@�`\Y��-X����'��oJ��^Qu$�H0 ��
n��_g��H�_)����{�Q��R�|���(	)S���I��)�������e��*r�"w�+h�n�n���v��g��Y�*��{Q0tA���7,hS[[���F7o�����-�������+��=�|j@
U	��:�X8�j���SS����\)��	G]H'7�[^�*����f�1�U}����y��^>��?��x���:������<T�h�����r9W�fpp��g���X�&�/�pM4U}}��nSi'��������+C��K���h�*6QK�l��XW
*l�n�!
=���J�����o���_������Y�]������9������u��3�����|��nv$��\�'�X������ll��&j$���'	7o������i�����n����g����\����~���v�Y�F��^N����?��Qe�iM:�����O�_}�������o}�(]��'���=N���A:|�i��������6�:�R�&�������mgl���N7
�A�q"Z��`8"
������n��[{x;�X�khU0�?���h�u��
�[\K��������k��FW�&�nd����X���2�Xl��}�]E&�Qo/m���
�8o��`��B)*�+�`�����*����W�g����E�[K+�fY[)x�Y���:������*��������;���#Uf(�V�X�@������C����~gg��zR�����fS5a��1���%UUUjlltUllXu��	��v��:��=�x�;!�8���q�T7���\�f�`N}iOV$��W�Ok�}�����:���\�N/5����V����xm�Bjkk�*��n6l�N�544��-`���yA0m������t���k�
ye�E���jUV=���^QCYOk��Z��QO2��Ky��Z�}��8m���H$����k�BM.�Sww�{�M%pc�6,d���3�n�����6���7@M{���T�PTw2�U�9-��hyoV�y
f��MJ��VM{��z�,��<�$�;�.��+�*���9S�g�v��L&�v�Z���*�J)���
6�Y�n�p(�����5i�$7�A�q&�u�~A3�u�B�*�C����|A�\A�|Q���B>��@������q����F���������p����5:��7��n�7~}����Y
$��-`�����G����6�ZHY��*�X�f��5���R���z��\���CK7���K��&M>�4�q��q��J���B.#/���`���������U�����i��
�B���H�tN?��C������o}Z�xh�[�VO/��O��
�Z����!}�����'�/�;^kk�N>�d�������i���k,tc�6o��m���su�	'��S��6�T(�P��i����^�H|����i���I�|�
��C���cNWU���`ck{������?��)�\��B7�bA�p@����;(es�������U��o���oz��[t`��8q��9���:u���keUnl����
ox����w��Cq������T=q����V�A'�y��U;uwU���*���Y+��^������l"����o�h�N��f7b��
Gc
���Y���U�����
�%��Yxf�}�qa�|�:��3u��'�C��N9�w�q�m�����{*�`X .O��Ie���_u���*l�`P�h��&*^��hu���V���e5-�6!������o���e����;^8Vcc����5}�t��=[�'OV[[������������|x�����)
+^��p�V�`���+���7U�R��B�T����V��
^y�P�<��6c4����%�
��I�)M�����4m��H�V�X���rz���-m���uZ���uV	B
G��k6���6�H����<���W����5��
�����?�&j�B6����K3�bQ�������kA�1j����*���k����l�����+���B���J�#n�
m���*�����*xyeS�.p�5������������Ol(�6/�(
��|V�_v<���
����z��l�v��d�b�
���@����w�^���{�&�����`�5c,X���������Qc��7�+��;����������8������~�awvfw�c�y���~/������uCZ��������G�e�]���r�����{��g�y�Yo�?�8��W�3ddd�����9��c�E��^��_oN�������"�\.{������yn2^�0i^3%�/-��8�n���#e����Q[Y�pm5B�
sSq������z�;IC�h��Rd��jQ�~��{N��sC�~�|�Eo���ov!��p�9E����d3����n��\��S3�m"""�k��v�����5mD���6"""""";6mD�mD�-�0o
n~��_Z��e�6.O
<)p:Ym��"�k
�#���)���c~5��\��+�$�h8�u�>��W�����~����KR��c�5�[���|��W�I2<��s�����3nDJ~7{���H�R�Fd;Q�FDDDDDd����H���������]������|��w���6�A	���1d�#Q�3�;n�� 3}�4�A��_��������$f6N{%�d�W9)	�w����]�z�����*++M�IDD6�0h�8�������������f�w����1�O>�*kM�&!	�%"��6������k��G!��b���������������h,f/������f�>��E�z�l���b|���x�����c�������O>���~�h�������T�F������������MmD�U����EE�.^���^��S�a��r���t:��������O>����i>���S�f�����88��f��l�����V��w�n[�}��#���	Eb(�	�cE�"���i����JKL�4	S�L1a�P(d�D%.�������\���4h�y\D$Yi�(��DA���6"���6"�=UT��xC5�a�+�6�]
2���CF��i&,"-�0h�s;�+���a��;4��Cm8f�^�
+���?�n����#����6��pX'm6O$������������4�=����mNN��1c��� "�|4u�������������6�J5}��`h�0�'��
�wF���(��Wd�����8������Z<����..��a}6TG��,���0��F����o`YY4KM(
��X��v�����fbp��/�����Q]]mB6���YYY���6RRR��66l���S1s�L����(h#"""""""""""�*�,���M�_��g_�.��\��	������I
G�=es�����S7?�t�%�	����`���XS�i{�s
Gb&�S��2��� ��������<��7g�|���f�!�����B�����
V�)((0�F��n�C9��9V�^m�+"����
�h��;�����}�BL��3��i����s�����5���������HZ����'�����+�X[���G|v�zak�I<�d��IIZ�P�f�B 0�j�����i6=��f3�p=Q��*++3AQ�FDDDDDDDDDDDd�����o��G^���+JQQD4�����3�QM �5%U��������x����z�$'���{����1!�mB��V�Q\2!�-�gY/���!l���U~������1�Iq]�3��-3A�l��Q�i�b%N)���j��������""�NA�-PS����{_�����'�N��������	��lx�>�����?��	31i���$'V��Mu�s��~EbXSBq���m!V���T�M�1������A����$��"
�D5hx��m�mJJJLe�d���������������fb��g��coL3�.�N�)�E�[�/-��l�3
����p:�~3}��/OA(��E��r�Ku�S���d(�auE<l��i�!����M�S��>k]�<���T�FL�&�O'Fn���J6
1l����(h#""""uB��?��"""""""�)�J�1���P0���m��xR���k��,Ns��I������p$�����w��/&I�a��47�2<��	�mX�f}�6����� ���x��u��"������2�������MV�!�G�*8�
7""�LA�$�.��/^��g��iw��i�:��rf<0^�'��`�)"""""""��W�b����z�q���<�W?���18]�q{���MUM���w���i��)�p#btj�1��aCu���M"d���1dc?����
C6"	�`SPP`�6���H����
��TTT�}X������""���I:�^����b��.��W�i�6%3&�`M���P�����ik��X���	T���������H\u �E+JM���t��5�`�4�./��k��jBX���eE�a��4:�����0l�����M��
����8���A��@���M�>}��~���
�tR�0LSIlg��>������6""""I��.,��i�}�o&\,]�h�1�py��n���s8P��5����'1��KP�au�%DDDDDDD��~bH&f5��N��nJ��Y���]�~f�Z�P^����Y��(L�� �����	X�1�mJk�&DC&dc-k�6�0��J6�)
��/y<�1={�4�~_�Z��5kLu���j�������l�8��A��b��!f]D$�)h#"""�T(���	�T��kb7Q�)^7��<(Ls����7����"\S�u?|���>h��Ivn��Y)�D��#�b�H<��D�����s�n�3�f]�����M�/�6e���6&dS2K��M�L���iJ^^=�P�|>��aS��ak��[g������M04����9��mDDDD�J���X����Y��j	��r:�)���^t��X�=(J��k������.8�.8�}W}�"V�:"���,"""""""�,?'�r���M,A4\G�I�x�h8^��g�sw+�0�E���(�� ?��H��45�(V����&��Ua�IL1��
�ur��u�O6�[�n8�����wo�a���Dqa����0`�e��w�1����4�DDD�[�$�~��\9�X��x�Ku�0��T���n��fT��I��@%��~�`�:��DDDDDDD�W����]2M�UjB�rD#!����M��*��%s������l��r��
�n��m���&��>j��M~��D��7"����?�pS����^��@�m�0T�}~������BAA��L!mDDDD�H����T��L�����N����h�{e�}N�q[�8�^T.��pME|�����lw�~�-^}�U���x���0q�Dsq$���l-�����1e��^|��?�������>""MAN*������H(���u�F�pp:f����y���D����t�#Qd�����z�m"����aV�����`$��7���c�X��!nt4�������w�G}4N;�4�q�;v,�<�L�r�)8��#1|�p������mDDDD�G���]�h��t��{��qs�'�����F���kX�"�QDDDD�L"������M����7�=����p���b��q���K����{��'.��",[��j�5��kNII	���z�>Gu�������s�9�w�}q������?�����X����z���=QS������#P���
�k�M����b�� ��$9�~{� ���c��H��n��X��u1�e��pa����
���-���L���]�b��1b��d�*7999p�\��""R��6""""I��t�����!>E��4��m.k's�������V3�c���������
�,3g�����O<w�u���{��3K�.��%K�����M���z|0n��f�B!��Z&����7��;�����?����g��H�����M��~�����D��U�FD�I���8�����������T�CM�*������E���(����s�=�u�)��f��R��6�'ll<�r*)>n�mDZ��5""-������H�����t��E�1�����m,Il��������S����.""""�V"d����0�2o�<TT4?�'�u�;w.n������h:��p�|��~:�z�-����[��b�
�9����`�����$�]�t�m�1��D0d��!D#�f:)V�a'��Rp����������sk����0V�3�_gM"����� j�QS�FDDD��Zq""""I$��\�T�)	�Ql�2
��Db@IM!��F����*k-"""�VZ�>}��
���>��~�����^�����'����~�+dff�}�\g��������ylS�ff���Gyk����P��K�����O?]�^?�0�b��}��y��w�m���$���]�vE�N��bMS�b����_�)~������M~'�$B6a��X#^��Ou#��B���T�a�@Ha��������H�0n���Pk���FL�;c��������0���j�Z��FV����g���m������?�l�n7�?�|�9������b�}��Yg��G}��?�j.�2�����|`���z�j��:u��0|�p<�����kq�)�����g�m����]�>���o��!]@�dQR^��LZ�'>�
���uN��C��i�lo����~�w'�o�;Y�<1!~2}5�cyint���s��>�m��V�F��<h��R�F�Fee�}ODD�������H�~
w=��t8bT#XV���JT������:�%jQ\e��EF�a���	�e��&""""��bp�KC6^x!n��F������y<����b�III����3~����T��/��<`������_��:�T��A����c�~��8��C�^s���&����tYD�A�6�7?��w&����j8��.:|iyH��oJ\��h�L-����x~�,���$��H��,+��lxD�Ms�(����T�]2�H�:��6��YVB����(..6�x�
�f��g�y���.&O����j{O�OA�$�p����q��?
�H�h���U���,��e�fYY4�:�h�P-�n�y^v���+�������j�*|��w�"
18s�����nBZZ�y�)��{.���?�kq�~�-����h��sxA����vi�8�s�)��
�W\anX��%"��c��������?�uN!�t���Yd-��K��'5���3;���
���r90w�z<���YNV����������Nq�*6�$���t��"�S?l.��R�F6��o��+��b�m3g�4��+V`������t�/���i?�����I2n:�vzz�	�D�5��
�"1�13�.	#\U���C0�w�`�A�y�DDDD�M|��Wx����5`�]w5�M�l��m��k/x�������|��'f31l��W/��f}S~��_��O����+Wb����~S�sDDv�6Tc����Q�8�n����I���{��e�F;�[\�Tx����gZ�����������%��T��pC6?O���.�m=���j�6Y^S�&�)�
����)u�Hc"�^{�5S�p��5�1���Y8�N�WXUU�%K�����~���l!mDDDD����z��"�t�m(u(|9�&p�.G��������Q�`�Y��p��L(GDDDD�F(2#�y�#Qa�O�>4h�����7�f8��]w��K.�������q�*V��F��0`��i��C����82z���u?���������������.x��XK\�D���tyM���o*�T���E�:5�h8*q�����5�O�J6
��6��?�m�\�����N�$0�07+�pZ(�k�An������-+)�r%%%��8k�,����(h#"""��<iY(}��W��u�����0�����Oa������#��0��Z�F���l�2���O�~����Gi�[�����Kq��������i���~���[M�����;�g�������F�i�����&M��DDvL���,���j6NS��l��0a�;��/����+6��$�1d��4�@��M�w������u������p���0�������C�s����_m�Y���M;''�mx����\��n�C9�����#""
�����$5��
~73=T�N{��>���MV�QH�6��,{OiK,�?{�l{
���������%�k�����<s����#==��6�~�z3�SG0<�^kNU��L��/7#�EDvT���!�P�F8\pz�u�����Y;�<>��@DIY
J+f]�?:�,/�&�Y�d�!��*�4����m|
�6++��}�m$^1�Uijjj����,l+������$�����,�a@��m���&�#""
�����H=N���'""""���E�6
�t���fx����)S����_��������������4U	��j���Z��"M��]�����*�������r"'#���{��m��7�f`"�����v��������L�&����C�*2�8C6�73d����?�k?���j�m$�����a{��E%��
��\g%����j��YuQDD�iW8�S��
�:MBII��j��v��G��O?�\r��&��c���������X�`�F���p��@��j
��7��S�N:t���n�:TVV�k"";���4Z�5�X�p-�F�-j�#����a�K�x�om3�&B��MU0R����9MU�-	�$��M6_#��Z�T���"l*�����/O����LU�D5�h6�Vd��~����p8l�ED�Y]�d�]w���~��"�
���I�D��n���F��������y�\v�e(//7�������{p��g�������c��qu?CFF���.�s�9f]:^���k����������[o�E]�����?���Q8�������.����#�<��)���9����5������>��������?�w�y�p�����.����G��^8^<�����VjI���p��X��{`"f.X�����"%�������D(P������*#�����e�t��9NJZ������ep���w;�'��(�~/���XVV�3tH�:�#�S?9�8dS_i ���A���K����!�c�~���?i ��%8�_�/��]�R;���%?c{�m��k���4�B1@��sgx�^{���yN�u��xn�[��Dd{��wm�]&O�l�)h#�l���6m�9I d�
���{������������������#mDDDDDDvl
�Hk�hcm���>��j1���M?���_n$���/"��
�tb��p���d���.��`�������������/�0�������=�����^A^D�����Fl��l���R��/�����!3�c�!��Lx���ry��z���E�(�����\�H(�P$������i���[�;
!?��-B�����������0A�	E��<�r�s��w�G�~�`Z�/��)�a�'h�=��g�(�c}v��;2�w��6�
�2
���;�����M�=�i=9�(�Qq�(��x��\`��6l�PW	��8c��m�9""m��K���V��V������M�!�,��-?�����}��M�^��}��[��>����/�����������F���o��<�^(h#"""""�cS�FZKEE���:�{��f�����S�b����1�+�|��>|����f��K/�d�cI6w�y'.��b�'�0p�
9����5��C1��^�z���L���{�����?5j��H�h����������b�f��j<��|�XX
��e}�8���M������t��,�P��f������w���t����#��FY�_m�g}����xw��
1lS^A���4_��l8mTy ��ul�Mq���1~o�;��83��w�-��ueC ��l�� ^������B3�l��6��Rc8���U�L��������z���Dd{������pPG������"��9�����'�,�����h���e����s��O>1s�s��+���~������[n��^���{����)S�p�B�>\XM���_<���7�~��������H���EK�.����/�~�i]��}}�QS���SO�`��0P���O��k���*�D8�<`�LZ6�s�����-���8r�=H�m�h���5^x����o����7���x�`5j+���|jJW P���RD#!���������w�k���X�������wT)'
��H��~���Nq!/u�	�4�������DP��i&��c���jX)�an��p��v����u-Z�h������6����s��������i�������;�G6m���8���0c�S>������o&�������t��Rx���5��h#����,��oCC�5S=�����#��>V����6
��}���z�����kX��S>=���(,,�i��mF�i�h�g�����h��D����Him������_����������������C�c��0�Cd��p��w�a{�BA�!��|
i��p%S�c�3���K��*�����}���#�7�}�x?�=���h�^c{����5>���a��m��T�����,�r
�V��_�~8�����v��[MM��-h�(iM�lN<�D����]l�z�)|��7�|qB"l���x��V�i8�3����Y������������L��Q-y�mEAi_��N�#a�������U,_�����H���#BZ�~�����H����2s�L�YbF}���P�����H�xQ��+���w�]w��w�����8\_�����c��ys�p7�������Yq�-5����S�N����Hk��^M�&�a��
���%�|�R,]U��%UpY�n'zw�����8t��=���>�}�=���?mj�z�[���K���+h#F���/�#�x��e��(�Y�V��i�n�������6�:��X��x�km��r�9��w���m�=����Uw�q���x����?�a�v�\�t����o�/����\I|�kjj�<���vs>��C�QX����w�����~�^���>�����������{�\1������?y����������_c��7�t�#�~�X���E�'�""""��pQKB6��%���D�G%�[����/q%���K�,������������O��x�E�-��8��w�����������?�{�a����q�u�����_�+�;uw�?��	����6��;<�;��*���FK�/u��Y��������=z��|l��z�3�&�Ul��>C�l#r>|8�>�h�!}wi���=,���6��/^�����^����p���g�������sg���Nx��6
��`��}�� ��\��<����a��{������w�}�#q|oV�a�@DD$�Y�b���`s��k��������s�sy���u�]U��s����0���,_o����������E����\�O���;��#����c�t�,���KC6�}5��kWdg�<}(/���rjK��~U������A�n9����c����E�+Eyi�����h,��u�����t\Z�s���C�xe�2����7�Y���G���Mjj���x��G�}6wJQ��&�8�����X]����3��t��������������^8GvII�9`7���>����Q���9s0p�@{�q|}����>Z�^,{��s��i��M%""�E������U(_0
p����)n��N�=N��1BQT���e=!?��=�
t;�����X*""��i����s�������{S�y��UX�b�	��?�!�Di{�g�_����8���{�������sG�.�����O<���H�L5?O{�������w��Q�����������0a���t�R��i��^����Yo)N�}����k�a�f��� ����w���8�t�v\DD:�h8�������������c����K$�Eb@m8�?|�Ey��H-�ao��b��mk����Pt��4��c�Q�v��H2��Q��x��_��e��y�'�p�F\���r������>�)�M�k�z�����o"""�,a����b�L�����T7zd{�9�k���S���|����������s?��*�-"""�+���v	�<���8�����^{��3����_���_&�����/����SM�����x������������|����{��7���?���W�����1�2x�`{-��#
��l
�E8B�>��TVV�kq|���>osC)|���r8���C6""�cJ�y�	FQ�%��l��+����p�����)S0i�$3�������?�hB����I
��f���o6
��qK;��7���w�m�z	,0���}�����6-77�TjJ��cc��g����HDD$�O�e?ME,6�lrR����F����N-������Ku�(�k��	������Dm��S:����l��V�����LY�����&T�i�g��e�����hq'uuu]��7��?���~�;������(�D_A{�i�ZC��+��Jf���}��k0��%K��k-�JC�����	��H����)��$�b�_������L'N4A��~�	+W��kGs���^z��� ""S�F6�d��I�9#���{�����c���2,Y�9����_��W_5��DDD�U����T��.�a��iF�%�'�}����.d����V��NC����'"""�%�l8R���O�!�������3��Zg�555�9s��X��������q��W��	�F��~��K�.������Uf67��X���*�x�^3�wG/�
��`X�f�����&""�u�G�����@Q���}��%)���}n�Q��k�����AN�j�l�q&R���D(�!j^�c�����3mi���6��X��~��#��w�4�i���W��������;w�J7@^^���2
V6T�FDD�Y���*���i^��\��Y�&���Yp�P���n<��������y:C�\���O?��	u���!K~��_���N3�K�~��uSHq�(����9}�?��O\q�8��SMp�A��}�@��y8���'���.��W�����������j�k����*3�$�D	���M}C�1����ZBb8����^�za������H�p9�����f*l-��p��/e+d�����1c�i'��-�vgVV��f�kk�����v(g�Pe��)h#���[����#���o
G����7)��c�9��j6��l�au����4h�F��N���%""��`%�`�:��:j�!�'�����.	^W�{\��
�}�AT�\`��i��a�|p���.3 �>������&P�0
������v���Z\p�8��30f�|��8��M����.����������{�}���O>1!�����3a��S11|��~��'��0����"G��]�5��M"������bLB��=�����^��[v�yg������g�6#�[��|�rs�&�?;�eDDDZC����{��x -I�����"��mC��Y����X������d ;33���(((0���C9~��F��DD���6��8���{����8�9�.7�n�R�|����=z����)O�v����m���0X3m���*����$O�ur�������_4�����Hcz#�q�����h����RpUDDDZ����8��p�5����
6�p
����<��C&P�sz>������m�o --
)))���[�n�����c�����MX����^z	������x����n��w^{�5S�=���%����>�����p���/���_�~8p v�m7{mc��
:�^�WNL��`+1E���S�N�:JDDZ�/�|����W��^�����?����R�a�-''��kx�.1uo9�E"x�kjl�Z_�`��H2c���v�uW��'-��1G�]|���#�N��w�{������A��e���;y�d{�����{�����_����7��<��H��P?����o����PhK��cY�D��s����%�c"""�����\�uS&��Q���nY>x�fo���S�U!��!����c��O ��H�4�l�9��0'"��������<�z��G�TD�I<���{p��g����i���W�3��;+��s�9f}{`���{�T�Y�p��(��K\~��f�"v�s@���5Vi����Lu�7�xs����������M8��������?���7o���:�d���>?���r�Yga���u��SN9�<����/��o3���>�h����{��6�}?��L�pfh��)��������������z8p����DDd�E�!���&f=8N:�n����s5_�Wvl��/
D��$��F��a�O#��G|iC�O=���
��"��Y���s&���?�q�9ns�
�d��C����6���y�Qf�,c�M�J;/x`&�m��U��bI��FP%�_q�l����
��bii�lm�~����H$�EDD�����u�?��o^C��N�����Iu��(����p$�%�AT�F�tQ���Qt���v�
D#��"?_�c�o�����4#����v��m!�m��7mp�fS�T[��6���M��?�g����#������Nm��#v��?�����*���p�*�]lo�?����0�����+����b�`8-�����SC�����^{�e����/���O����a�O<��z�	�P������sqd4�vt^}�U{������6"";m�1
�l�'����>.��s�D{�1����eeef������d���$m�U1D���`X�xSF�m�6���v&:�����7e�7�S�a������ls;[�a����;�DDD�9��5����.Ya�{��8�9�c����d���[��G0���J���Z�G�����? }�����[;+0!?c��V�^�u����/�������cE?^���:qDDZ�gH`���
r(h�8N���3<f����OG������o����s�cV"p�*��p�������������f�z�-�;����C=�L�u������	O<�n��&S=(�������(��N��0��7�X�����g����B0�������+����/�����}��f=���6"���U���}B�;m�1��W�f����h�"�Vd���5�����>����T�!�c����GVV�YI
�6<�4g�����s��]����q�%�`��I(..6�Ma���W�\��:�~�q-�(9L�����A�9��#�o����s%������)��%""�f�c�+=�`
Bk�ZDbT��Gc�Z�(g���k#X]Fy bB6�P�^#���1peZ�8&ql��S�����G�f�f��
�#��x����a��a����m��O`��_[
�`��	&�����p�a�a��f�-L�6�F��d���.f}{` �?�w����>�T�a}{��G����5j������?8�{���1������#�~�Y�f����3���_�Tb�aTZ�ti]���u|�5�l2t�c:C;%�������{����t[?��y?�����4����w����0���?�^��������dU��t��|>�}o���������GUM�;eZ�G��R1���r�\?�n�w���6����Jk����;%��>�4�?��6��}��7uS��M���
��C��}�U�JD�g�a�!��s��#V�Y�|y]��>�����y��yS�p:���1r�H����,;s8�7�����N~����>���N���P��6�(<���7�����
Gp�OK��~E~�Q��?������2�%������������u�N*���8��P!�a������������A{����X��x����L�b.L���h�%���un���n��a���6��SS���6~�;���m�	��6�[�j���g��#X�d�9v��>$�jX��������`U_V������^w�}�y/�f[��;��a����mU����������RRV����!�=�%#���t��/������s���}�$MPEi��U�f��]�P4�3<G���6}3�
��yU�r1��*8g�u��UD$y��SG1P�������_mny2�l������>|�8���l8_7�	�t���������L�&��.���'��
8���GA5�a�������
�\}���T�����;|�L\�i<�����/^���!TY
����y�"�W�"5���@�8�\��N���&!~���c �#����xq��l�p�=;q� �g�}L�������������[KA��98���O>i���
���������z���SPP`oi��{���;fz�WS��N�:���N2�Jqi��P�FD���������8u�K��q��Sk���^���B<�q����'2���c�P�'m�1��W�f��o��_4�r��o��+�6u��9m�W��3l�0q��V����m�}�]�a��'��:�Ht&m
��d�����������M�x��#�4��������z�\(i/�<����/�Av�}��������D�k��-��@�9m�{�9w�q��:[�a��#���DDD�Z$P���_`�7o�|�T�Z�X$d6��NH���;�A�����}��,���]�v9C����5�7���N��nncgNYY�ir��9�8x�`�����@Ai�_��N���W_��/����K�.����7u���~����^�:|���u���V���������{[��4�?��6"�V�a���,����&L�v���n_�.��
[�A��C��v �aX������o��:�8m�1
�lN	�>�W^y�|"���)�x
�6�p�P��z��r����HR��A���{�TN���,M��PMK�S�.\p��ko�&]9J����e�9�wK� >z�h���u��2s+V�0��������/L���/1�����0h��SPm-mDD�]��I��UU� \S���?���_V!\)�H��jnE��O?a��	]�b���3\O���9���rNp.C�5�2�*�(h#m�}
�W�6k9h�}
�,����e9
X������EN{�������|��r\s������r�p�/-.o����	���5[������G��t������1�y�8m�1��W�f�p&���~s��1���]��#�K\��v�����7l����%�GD$5�t����7���?�L�����Z5dC��������>���^�C=o��&�.]j�!7�|�}/�%��i)vh<����\�I���O"���qV����
������#""���c�?�2zE��=�e�M������m�B6�,N���l���2o�~{+q���q�6!���EDD6W��{��75����Ez��i��z
����v[Jx�o�^��mDD����j��r����J6�^�K��'%Nw��
�6N�.O����gZ��!����k����� "m��2�Z4++�\�c{:Q��3�p�(��U����&�b�������
+�0`�����g��1�����O6����j���s�0�s�m��-,m�e����+l������n�9�������6{���:���#�w�i'���^g8�a������J6�p�BSE�>�4�&��9�������[��y�-1�)�QRl��-��sJ)���08�pfcxl�1c�8�r�!f���1cp��Wc���f.5|M��PVY�������j<�ka��:/�����1S����
���u��	��yk�����V�;������65C7���������3h� w�q�OGDD��m��S	%*�0(��d}���J"L��/����Y.��b�v�a���mV��k%����=��	��>��4�0��*7�����d�0�����tV5�V����F��(����H�TcU�85�����lv��M�-��
�3�jr�D��h��j�����N���C�w�����a��x1��G5��z�)S����?7S�s�.���p�	�����k*l#""���&�E+J�r:�c�nO�u��e�	�8�)�~1����K�m""m���f��e����VG�6s���k�b��yf]DD��e���D���a���kn����0�.��b��ra�\.
%��2�\F�iB8^x��t���q�����a���������X�d��%�5��TK*��|]�����
�����s�n��ghK~^����7a�D��\-��Y?�����m(""��A�	&���#���3�4��?�$����[�?g���f�>V\cU��_�����_�?@DDZ�~Z�b�!V}�p����6q^dm�vvY�Q�Z-)�FY�*}�H��w+B2���R��RRR����R*##�T�I��pZ�/����/7���H;�$���y���>��pM"X�Z��D���.0;�ts�i��{�T�����q��G��M���O��r���3��w��7�l4r��������<��B�����O7
��*Qc�sDDDDd�q
�D�C�����ELn�~l���`5;qDDD�����N?�t�Y�n�y���;�y<�2e�$��8����^|������m��r";#�<���31��T��:��x�z_'+c���ED���|��G�o���y�,//������1g��:�}�����7~���VJDD�Y���c�5Ul������0��-�5�ar���D�������<��y���=�
�w^1��\gVc�.]����9��qn���Go�8�V��;���~�f�����i�V���D�:�4u�mF������-���|In<Npp�]w����b�L`�cN�D���|����>����gt�W�R�������)�x�{MDD�u���07�:n�A4\G�In�����u�ka��/"��q���?����Rs���h�i8*1�7�o�_����l�f�S
GDD�Y���$��o_3������G�=z���V����k���m�����3f�FAY����Y������nB��_~��F�kC@
=����*�����������r��
5�3HC��I,���>eeeu�jkk��O�����S�4iR]����{��q�
7����3����r�J|���f���]w�?�0���:3�����/\y���z����6�+""�)��A��Y�DY�&�P��H�,{��b����:~9���w�����l[���:x���j^e�������\x=�����Y�x��/"���U�f�����[L�N!5j�({���0p�)�Xq��d2n���*�TVV��b-�=pT��7���TQ�<
���]|�����>���m6�������3v��&�GDDDD6Ks����&����_���sA���0��v o����C�1�$ED$��O���O�8�~��|8�����3����/7����E���x,9��Leb��\��8�p��^0�y���k"""[/?'������x�3
 X��H�:�9��
 �/��k+��%f��p$��T/����l��8�Ui�l�i8�DC��6�>>_DD�Y�����3����i�~�����VW�Q���������v��W�kM�����,]�d,��j*���S's&����.����
/��}�O�����|����������N-��<�s��a(��n��c�l��
�������f�=�4�j���0y�d{
8��c����={�4}1��;�TR�3��gC|�o����6};w�\s_DD�5��x���{a�]����S�F�)CM�*���`
�� B�
�V#P��p����������������l;�^�>�����?S�:ZSA��J���Iv�*h�N���g�g-���������[o��a���x�����7:� ����X�d��%��7��q���>��~4�/��y^s����~�����e����l�^{�y����~h���F��}w�i��������c��<���9%;m�	��
c�a�kx�s����+����+x� O��g���M}<���?�^�c��a���1���e�9�GDD�5��������]�&\[e�55��QS���5V���
C6�����.8�������l[�>�k������\7�~�u�k�""�R�
�H�����}�Y��I�����L�V���_Gq�Y8
��.��4q�tfX��9�h��?�y�����Kq�����;���p��G��O�y��!������d�;�2���n��
/h����N^0�H'�f��1c��������c"""	��/7�G��<V4��{X��������Mc8�6Q��8XHDD���>�+n�`�:�36�
G
LU�H8h=Fu ���s��KO�^���H[a@&++�T���~��m�x�����t�}��4F��"�p����m���X���������k�I�&�b�C6�����hs�]wm����1�������;���i8�z"d���m?"""""����1FY���3�8c���I'��SN9Gu��N��K�"""?��a%��F�����?�4�
��~��n�_<h�����v9��������q������E(�a� |^7��3SE�t����a(��9*"���`�D����YY��62C6�PL����Hm8RJ��6����;�hr�YCG}����jZ�!��>��������w�����9������M!�6���i�4�{�6�2��*�h���[DD���Uh��%���3g��O}��mv:BV�Y�x������"""���\��	9)�ZM�h����^���e��JO��mi+$�~�D��l6l�`�gM��/�������IV;T��a5��n��f3���{��f����~�LC$[f���f��g�y�T���?�aF,�|��f9��q�M7����o�i��pdsFF��
-�3<����~&L����nFI'���n0�����LK����������;ED������^��^�cG���+��'����q���>�^�%�nb.�������im��0}}�xb��]��e5p���@(�O�J��'s��?7�UcMD�+����C�1�bU���b����������j?3`������}<P��G�."��:|�7�
b�����G}��O��N=��+�b��}���������q�������[6���1����q�E���n3�.��~;.���{��o~�^�z���r]�v���#q�!���[o5��x�K/���}���."""""""���{�YW������g�}f��7{�lL�2�^��ags��~����1�/���JDDdk�+��_��)�zg:�.^�
����"v�X�hU�X���N^����~����6"��x�l��1f�	��y]�k��[�5k�`���&x����6�^������"""6h����lr����;�4�T***��;����[o��BC.����M�N5$[��V��w�[�le'����b���{)\#""""""����������3f���}��WU3��Q���������M���G����5��QU���L�G����>�N����Rs�%n�.G�85g�z<?~&���J�mD����}��G��;��D�Q�g��lx��6\��U$9������ ""6h��+���?�.@�N��mX���/����VYYi;����m///999����������L��s��/����^{k;�:� s?��)��Ot�5�����3#s��=�z��/""��0&|�/0N��L���� %���N����KR2�����t��v9�dUyuV�m|������!8�a�f��Z$+�0dS[[���j;v��d�������Cmn��3��a����z�A�gbH��p�8p��h|BN#%""""""""m�����z*���c�KJJ����N9��a)����a���M�0
+�s�9fj����?�5�8�����H��4S"""�����L^��� \�!��r��Q�?�������[\�T���ff:��-]��~�Oq("����}��M��O<��~:?�p��w����o����Gjj��I���
�3���3��_}��w�y�t��w�
7���o��q���S�=��Z7�������Y�~��m�������G�!��:u*�L���������okB5�M�8�<�&M�d�O�lX]��3�4A��TU��E�������&\��i^c�xa���}|�pZ���������6��������=z���A�LH^weP=77w�6������m�~�mS�7�#��}�Y�a�I�/��^zi������M��=���f�#G��W\�Q���/�P�FDDDDDDD�
q�����'\}���v����8��}9,g_G����N�Z|����������HkY��kK�Lh��)��i��������:�1�Cm0�e��u=BD�
��EDZ��m������]w��$-?~�F
�C9������8]7��������icu�e�����7����w_����,����?�a�����:u�dF�&�B_��k�EVV�����H��e�`0
si����t3?�<��p���QS6�����t.hSYYi��W�����Y�`��]k�����>�hX���#�����
��R�FDDDDDDD��egg��N�_���aX��^���>�s�=]�t1�A5�
7�\s�5����q�]w�������=DDDZWN�i����H�i�fm���DCf��t"7��T�����lo��UDD6��m&O�\�a8&���
q���e����_<����g��������*������l_�p��n��f*�:�����_b�����UW]UW	GDDd[JO��{�LD�������c�f�t�& (��9��s�W���������\{�0a�{�9����x������c�����@�X���^����pi*8�p�'v���4��H(�W�����n�GX������8L����v�Y���_�t�#�^���'"""��y�^�ae����������~
�j�.1a���nSo����*�6G�����`��"�\i���0p��_�j�$�����$���9s&^|�ES�`��)�;w./^�Y�f��
�6���
V�Xa?CDD:\��~ &���~���Ox`�Uj���;U���`�<T�Z���]������<�5���
����>��/^��w���_���<�XUEDDDDDD�U^V*�0�z�!��MmU	j�W"(C$Xm-5��V����z|5��z,�0!�_������~5i�	UXDc1T�b(�P�jI�����(j#��-�|�-��>�G}�e������pn�N��\�
�f��������3�QED�g<�t	K�~��������`s��w���/��1b���#������������k�4v�X���[&l3|�p|�����"�ZjkkM��
~��u����4{���H[�wB������~��X88]�Ym��V�me��"����;�
}��?x2r������/=��#����P^�� ���L�s�=8��3�z[x���1n������]���.�s�9f�#�9sS�[��~}�����~�z����S�����l~L�j�z�gE��y��5N�����u���0b���?���cxW�6�W�J��}�q�p�'��Y����a�Nc��X-��h�nI��{A�m���/�#�x��=�����Y���7�4����XJJ
<��6��
��
�-��9998�����kW��DD���f�����HG	��c��G5_�0e�b��O>i���v���9k
���e�]�j�*�>f�����
�H�R�FDD�S����L��a�LG�D���T�~��h�p��<fYO���V�����<
N�?�B"""�mZ���+1u�Tc�����f��E[�WSUU��N;
g�u��������V������w~�������1���u���A�������6��d�7J�A�������!c��
�?0s�3�/���6-�v�Z<��������^��^��:�(�����M�z+�k�r�)&�#"�l:|���CW\q�9a&���gOs������;����/��tl����?�����	��>�����[Z��6""��"a�z�����	��5G�e�]�Ku���N���G�qTPym��B�
����iYz������j"""R��6[�#g?��S���&h�����gH��)�[����o";6mDd{���/0����!��(~q����nzt�����C������&�c�f���b���+%g��R�q��p���N,�h���I�����0�y�1��W�������Klk��g�8q"�^�i'����s^CMj�8qNUZZjc���C���#�v�d���6o���:�({-������tO�Z����_}���5��i�8� ����/�G���~��$"�AAi����y�������:��Kq�(�c*��e��i���``Y �e!���HM%�p2��gx�
���������:�<������[L����4t�����;���dG����l/�}<O�=kK����
/�p�lIO�b�!]���G�{Qf�\�!���P�x6��d������~g�T����)^��Y_��t�����a�[�aJ2��,���.py�(y|9E������<������vQjj*


����
����3p�@s�1�V�����6�(b��5K`(&??��<��?�~4�w��X�p��w���b��I�3g�	��w������_��DZ��6""����mX��������=�}H�:�
�
��A�T�	�����]�_�w`���)h�ex���}���}���D��r��?����&;*mD���Vp���J6�x8��H�m�/bs@�����~8���8x�>�qi��N�����`}�#\[e=�|�$� X��x������pz|H���!�������jq2�q�A#��t��tkJ���R��w�m��]��������_M����S���oV��uZ�d���6���A���LcX�f�=�4_�	Lh2��P�^���G�O5n��)h#""������?~���i��������h4hC����&��!��1,�������*+""���6[�S{_r�%x���:�qx�������;v�e���*7</���M�������T��f��g�E,���k���p�|p��������Rk�;��Y�����C�����V�Y�����������R�#�x����"Mc�z����|��Fq����;���������f���k�<�����H2i������w�n*���Hj�e����a����(dC'�x�/c���B6"""��
U�!XVlF91X��8��E���FY���~.�L��a���Z��H8�����H+�>}:�M�f�'B6#F��s�=��~��_L���K/����+��~��g�CDD��L����>���n�<���?���"����M��/�)��7%��_�\�Y��{e�	�����Z��k�����ld������	�7�^���aw���H���Y�5
�<�n��&���Q\������?��f8�vc�n���
9��a���'-���g�Dc���e�k��g�gN�������V-NT.f�
�}X��S|8�L�}V����UM65bWDDds�+���/���j�]����_z>���p��f����o\�4�����gZ��1��L���OYb�������pb�(.�����
�#�����n���t��
�=��H����M�&�p�e�y�h�5'�p^{�5<���&���EDDDvt�����[�b�	G�A,��O;�0�E8�����U_N��F�VRYYi��	��z��f�'�����?�_�7���3����8� ���~��o�q�}��lUM��mDD���,,,��TUU!6�a��SGQ��""���6��L=z�����j����W/{k�8��9�����MrDDDDv4�E�����a�LW�FQ�l��W����(Jk"��M4b=����4{���t��E�~u���HGP]���p9�9�n�u���1����8]�<)�~1T��i�C���P����

�L{�����+--�h�(>_DDv�������F��=�5��Q0�`3�S,2Uj���(��pV(8�?x��
��EbXSBU0~2��@�����_"""�����P?t#""�^E���E+KQk�3�<N����4k��	��q����5(��_���X�f��<x�	�������@�p�T�a���������F9r�)~ ""I�IV�=vB�}��'=�X5�(V���"��@������W���4h��a=�H����C����N��_MDDD�u�Jq�����U�Va�������H{�r9���C�s2�b1�3�W�d�c��x�z���@Vz<x#"���z��1c���i���b����������k�~�z�IlgE���;��]��fJD$�(h#"""�dzrr�m:�8Tm8���V���dC�	����J7&dS[OZ6zq>���_EDDD��>;����,^���'""�~��� '
���b�9t��f�6�E4�`�����P�YD�m14s��Gc��A�zM��c�N��
6�l����?��#����W��y���1a��z��f9����z��o~��|�"""";����'\�^�������V��@(���(��QCD�!�*J�=p7�����;t?�DDDDZ;�O:�$��������x��'�}��*��A�.Y�DY�&�P��H(>'s�b���
�����ED����r;�0s?++�]��5�&*??�L1�����T�)((0�ED$�e-���]���s���a�������L�����3a�/��?������U��y�p�u���
EZ�[��21G/��y�^s_DD���S3��k(����N����j,[�h��Z���YH��]8=;�;��Sm#��L�:��Op$(�|>�q=b����M�����o�3z���e�]�z{��=���������/G��=��W/��/�R�)��b�_8M�>G"��<nJ+k��wK��s����\n�������r��n���D�j�	��"Q��������T�(������Z��N~���,�u4<���"�'55���8U�MUUUuA���<s���)))�3DD��C=d2*	2h���9���W_��+��H�N������EAiU
����VV�����D6��l���Gf���7l?t��0d��.��`6�v;�#~�����3DDD�9
�lv�s
)�7��������u���>�����_VV�����^��*y��S�FD�
�~�N�a��R,[]���h8�H�SH���#�E$X�pm9j�J�X���3��8��!8x�>��D6EAim�f������W��
��J�������� #C���Hrk�a��4�8�;1:�(������m�#�<��&���(Y�l�i����[�nHKK����l�P8��S�b��,YU����H�y��S�eb�]z�sA���H�p��*����l�Gy�]v�	tX�s�=8��3�z[x���1n������w�u�9�����\��������M�=v���d��~[�����.��B��Q������3��MQQ����6���S����|���ZS���.��d}�8��\;
�[�l����n����4%-�JHk'���]`�O)����R���{���J�|�	f��i�4�6�M�6y�67��<0j�(�����n�a����Z�����8��#���������6l������CSGIkS�FDZ�ox�a��<�������W��z,Cj��^t�K��������?Y��5p�h������P�f�%:���]�'�|��.���com=���}���������
G0q�2<��t|9u�S�p9��a����&�!� 3����c�.�:���S�FZ����������}��8=��nS� 
!���V���8��cL�FD$u��
;����/����������	sAA����QBZ��6"�Z���'���l�8o�����3|��Cd�:��Bt-������'�F�_���>���N4�u�?C����^���b3�TJ~w��@��1H-���fb��S���$m��
��?��{���t�7I�X)��;���dG����l/s��'����Y+����6T��!N1��S&�u����{b�a]�����%�2
�Hk`���g�5S�rZM��x���U\��D��}X2�&���;N>�d�""I�a���ML��{��"d�N�=���z�2��XDDD���������c��5&\�~b�ub��g��Q_j�u?�����\X��
/�?�~��L5%b���U�b���`�s7c��a����d�(��V|�<��f>0+>}�z�>?�il�/\�'N�����_|�t��*��I�P\\l�)""��D?�|`��l��
UWW��DDDZ�����mh�g���q�G9[X�&3��������n&d��G9�������?b��
&P�6wvv6�����zM���1H�i[�������+W�j8""�+��>a�����IK��NmDdk�\u���r�2�=bNz����f���X��:��;&�p���
�F�G���J�?/?����&��M?�~7+>~
���C4�n6���V{��1D�6n�<}�����*"
��������E��E����]�%�A������Q.";"U��r�W����^�����sy�HMM5�
.�����Y��xaa��vj�i��}SE�j�<?a&��j����*2����6�~��6j�>�8��C0\}4��T�F��j�~�i��f���E��RS��f^��sh��I6��M�bv�(d#""����K���5fNo�%<)Y�����I����N��&p�t��M��/-�<�v9M��w&�������Is�z�B,{������b����q!7��.�^����w������`�Z���9���UDC�uD~�`�S�f��iX�n]��������
G>1(�}�EDD�#k�N�j�3d�����?���Y������o�[}��[��p�	&d��iM%e5������~���Xo�����-�Q��W�tU>��w<���t�y��J"�V�]�f
+�;���
�n��v���)b��/""0hS�R��^DD$p~��PN�u��a�&N���o��[^8p8�I����n����.L��
U5A{?IV�H����O��jK1����B�,/:gzP��F�t�Y�����c�x�sf}���s?J�}g��H\ii���a���A�r�9���k����������2�^L
��$""q������s�5`��������o_3��557bWDDdsUBx������U��Q�`�I�����������3�3s�:<��tL���5eED�9��9�wb�Vb��%!�D����*�O��t���i��Vw�TW�JZ����(,��@m��wyR�c���o��r��M3A
��a�_[����e����OP:o2b� �VS0��BQ��>|n'�V;�c}^�'���f���.����X=�M���W�d�����
K�,1�r������3��f�&�����y�|��	����Pee�}f��1c��i�DDD�3N
�������sV��N��YF'S���?��<�K����\p�X���:�T!���g�vv�,n��z��[�:""����TN:���t%��/]�����m(����}����o�$����>k?^�j�a,\���zJ�U��������]Hq;����n���H�G#q�
����q\��x�(�i*���~".��E��h(~���%''��z<�a��nx���V�����:s�LSGDD$��C<^0�)""��q����]l��vY'�N���B3����5S|������|��
�tR1�]�_���~5�m�����f��+
�$hS[[kn�o�oGD$�u���>���O<�^�oDDDvtY�~Si$q�!�Z����gp �!3�e�7}�$;��U	T!f}���NS��)�������WE����U?������+Q]]m�3�0
o����}N!����Sf��UU0����sg�����<�,^���i�*k����^9���g��K�o4�A/������7�:/�O;������*((0�4\�'���
���������UD���pA&-o��v�����������[�|�r�.""�#���]
3���(�f����4M��)����!j�8���0������*7�>������P�\�a�����6��Y��P��ub
J�JrZ�~�������\��q^�DU��������Hrc?��!C�5���~2�DDD��U�*�����[;�.x���w�Y�Bn{���zj���f��EDZ�:��B
C4�4�v7�i}:�����wS�(>_DD:`��z�����~�.l��
��L�0���>V�XaF?m�������7}�f������V"���&�b��h$�P���*X��w�l��.{IF��L8��g(�u�l>���d�_��g�g�$O��:.}��;[***�(b���2�+�4��q��a�i8����$�^�z����Z�d��&""�>E��Pa7�����s��5�\������&��KEd�c����N;���Y�f��u(//G 0}4�`��c�>�#F�g��f]D$�u��
���n?~��b/++�W_}�q�����/6�I1x�������~iw���	�Dz�F�Z�H�e;��&q�p8��P[��P�D��:�1��2SPIr�t������P������O���*`���C���*�/�(�Q����34�`M��|l*dC���}V�IoDD$yu����������O�8�>���O-9�������?RS��q�Z�����fO����u����r:��i���c�ED�5N�=f�dff�k�lg3X��5��]���b�������z���������DD�G�
�|�����_�j�����k�/��b�NG�N��dh��=` R|D��C45�kd���
�h�T�	��Z���n�M�M�6��F���������#�+�S��ipX����(��<��76�����&*�� l^��6����lSv�8�m��:\���%:r���L����s�18������Z��{�����g�j�~��$�����E��L3(�T�)�4����q+�E8Pa�s�~�^]��DD�H�.]p�q��S�Nu}9�:�`
���R*�x�
��)))�+��H��|��G�z�3�<��I*g��#vB8�J$1D����.A�|
�KWZ���,�obQ���������=�t1[�����'5�T=b���UaT�FL��������b "���*��J��
�?��B�.���k*�K	si�}������2�����ED$y%������+q�m��cG�����5��^x!�z�)|���f��-Y�}�]��7�UC;""���2|�����p���
&l������	� TU�h4��u����`���+"m���E��>��m�D�����
4���H��4�����+���[�m����3g�k������"��I�e���0&��u3��ED6��Uex���xa�,�ax�N����X)�p���	b�Q=q�C���fZ��}X:��k*�p��s;��w#�����4#�j��J6��D��"�%e��A��	��w�_I�+���kX�r��>���X��m��5��
�|���l�,����k�kD���(y�\v�e���X��UY�<�L���q38)�3�4�]w��s�9���W���:x����a���<y�9V���h�}�������L9�*9�cc[�Spa;$==EEE��RDZ�I��Z������x=.s���������'�1DM�&h���"AD"Q3=����e��a����I6�U��N~3��N�)=0���@J~7{�M����Y����o����ic��v��:)�k%�w���<����$��v���U$���Cm����;�^��������������n9@x���E��(h#"���2���_�'-�O�J�tU��na��N�i��){���C���=s�g��143��+�����`{�����q����LM��M03
�hm
<i����`���G���}��7f�U����+v��bC7^���}8]���pb�3l�w���~%��OA�-w��7��������K.������5�Q)h#"m)���/~���n4���������xv���D�9w��o�a]p��_!;CU>�e����v�Z<��s�|��A�>�yKb�������,\'���|��*T "I��m�:�,<��u_��z���O?m:�

���H{�����6v�,_]����U�x����!?;��^�(�BZ�Nz�q5�K���������m�^����"����7zr6�G��O�g�x�T?���i�$:i�D"f?b��!�#�<��
3����(��r��z+���{m�Q�&9(h#"m�6�����fa��ups4/��/c�Y�G���H���Q�2}���E6MA�Z���������Ql'q�V�3$�����gu���R�6�z(F�a���$��A���f��{���l��s�������B6""�L���M��Y1�������#�����u�N����f�3����1���#��.pyS�	3���8].x������0��Q���
�H�X����������[V�	�>�6���#�8�
�_ADDf����|���g�-���fimf�}�a�>��p�}��X,�hi>�[/���CDd[� �������!���Q�;)q��q������03�/""����*�$R���?����&��������<i��T�j����Q2c"BU��E��ahZ['�nR����!g��>��Q�&1L��O?�y�W�Z��������<999&��^�����Q����[�b�����G����z��i?";*U���VVY�_�_LY���*�r�/Z�1x��/�8���0|@'��A&p#�R�h#[��z'�;�]������z�W��m��ae��X��������$�?uT�y����7�����g
���H����>��4J�OF,b	���y����n���Z���"�����q�����4���+**L5v�p����'v�����{���x��xN����MAiK�P/8�z�D�1x�<�8���Z��^8���h8�H8`-���;�a�����?������/$�	
���`x��%x�����Q����sgs�9|+�Y����_��s�5�""���O5|�p���7�(d#"""�Y�^�K'<��Y_"� �t��v"'��.�^������X?Vc�'�`�W�!
��#����lt��0a��#G�o������B6""�.(d#""�i���x��i���q�p�?���N������_F�Y������C.����C�L5��m�m`�b 9�[�6�~�q�)�d��R*�zh��~qq1���k�������qz�Eo�����rY�*'rS\���5!��47:�{��}��B
�(��s?J�}g����i������������`���j]%�.��^�M��75�:��Y�ns������_z����l(���Wa�������l[�B����R�����$h���r����6�""���m��g�����6""""�2�~��s�E4\������P��A��e��pd������D~�E���u�<�_�5���`Y��j""""������t��]DD�FYE?�]�/���3�M�TPNH����$~��oj\���:���	a����]DD�1�*,,4�\.�����p;�����ED�m��o?���p�m�a���������4�l���TR�����[g�����K�gC1'��,�.fm�)(�i
���~""""��N���{w�y��H��n�:<������[4�WDD�[u �E+J�r:���nO�	�����0�m\�k�X��ED�������C�*�0DSZZ�p8��Y���-��a�������ED�mX���;�@�~������q��'c��	f]DDDD�V�j"�*����4�~w��Av��M��i:
�t�ze��""""-���+��g�}6��oF��G�~�).��b\z�������GEDD�D�xe)�0��i�N�o�9k�������������*~�[Dd[bE�`��A�B�1V�)..Fyy9��	���
8��������y����gO�."��:\���%����b��w7��/���\r��t���|��V-���~����R��r3�S,1!N�R��vZ�V�a���P
�V-4�!�)��v����C?������^���:k�n��'�|�s�=��w��ys1�#wEDD��r9���3a���Y�;��y��~���I������lk8���-+��{����Y�v-��_�����~�o���p����""o��V�����o���w���N:�t���~��p����,t��y���Y"m��Yp���#"[�
�e����/&��uCZZ��UDD�m|w�q(�i
���s�i;D�8n�F����p��
y�S(��{����`��^���|��(^\���4����{�K�.��"";�Gy�]v�
J�����{p��g�������c��qu?;����.�s�9f}{�4in���I�������w_����_��<���\<�_TRRbqq���~��'s��r�c���UW]en�c��%/q��yzz��8�v��Hk��p�����"p�=�g����cs�A�_r 
!P��@�����]{��q��E����v�{�q�pz�H)����R���{���o�����9>�����_[e��m*5j�����S'�."��v�m7L�<�^��m^x����Kx���~�!&,���c�o�2�|s��������Z�b�	E��x��)N8������r��E�Y�f��`�
�����?�v5��S�L�W_}���z'N4��ED$y�S����/����{�������'�0��C=7�tf��Q��������K��s�1�������?�y�������<����o������HsR�n���eGb�E�*L�����k���S��=�e=_D�����|�Y8(�aVvd�gP��y�����B6""��SG������I-�
�?
�X�QT�Xi������(Jk"��0����<_�>^�|���0}�tS!��2���
�\x����U�V���o�m*���Hr`Vu��L��;�T�!�\8��m�����������7��*��x\�`-N9~��'�i����>|��gf���Xu�a�������:t��U�ODD�E�sR�����	��H����"a�8��7��V��[��p$��T/���lik�k��p�G����3�&�?��SL0��w�e���������������_���%J�k�(iM�:JDD���%31���z�b��C�����d�\����n�[V�	F�X[���08�|��}��=���������Y�t��(�v���5���Lf��A�D;�����;������}DDv�:�e�,Yb���pS��r�����Bs����=���L����kF����x�v�Z�\��x����6����;��o����a��������g�B'�U���I�=�[k�(i+������M���k���M���M�'%���'"�A���T��X'�i)�z�P���]�Wi�������������N���6,9��m�����{�SEZ��6""�^,z�?X��#�T��t��v '��4�>ka���b%���"V���������������W�dWQQ�	&��l��B'�7����T@���
�g�o�}��1fdT�}ED:2m6���3�������2�������}8��/�����T>����g�����;�nL"`�Q�,���G����P�FD�I�OY����)�*k����u�<����"B,1SK��s!�c�{���z��?������e��m��tN�]VVf�P(�����l���a��$%"b��A��JAi/"�����X��x8�N�.x��������[@8C03
�hm
<iY|�?Q0� �I(B�*���?61����999�ga�u����
�H����L��R�"";m6�����;��5������{K���-�@��Q�p���*`��S�FD�Z(�'����wg`���������y�����C�f�E�������c��E�!-������S��e��
�]��
�Ng��m(^Cf��H2k���r��������F������C,X�P8��P��QT��
E
��(A���t�-���B6��5k�����t�p�^�d��E.�^��E..������l���|V>��s������{/.��B�������5�ag���^k�:�{��O��!C��V�q�p��}��3��y��B�9�y�(��EEM�`E��8h�����}q���������w�}�}���
�}3�>�v�M��}6�l��>���fjV��*���U���&\]�u�>�����r���]�X$lJZ{�
�����{���G"�����D~���/b��9&h��
����S����/Q��m#v��������=DD:6U�i����|=G�����?������(��0��)�X)�s��4h��������7aOI�h#"�SyU-��*Cyek7T��<��]����C~N
ze�{�lU����>���a�����(���a�
�p���~<�aU�~���}DD�����N��v�:&U�]�`�Z��JQ�z��9��t2�E�v�
�u+��g�}���3��������i;gx��P�Y�e���=z��&"��)h��*++M��U�Va���f����5<���5;;��k�>6�7�������'�.�����l=mdk���`�������
�������M�v6+��-����N�:����B~~�YI&�z���N:	+W������?�w�qG��EDDD�Cr8�������]A�C�B��N0�De����4+55���U8697��~�64;sx�K!i
���������O>'�x�/�)q�f�#F�o��
���H�������R3s�LTTT�P
�crrr��)N��~�����M������>��={��j""��]m^x��ar��������5k��������M�SDDDdG�t{�{"���v�PUU��P�v��S���c���a�i<DDDDDDDD�=f�.]j��b?
P%f^`?M�����o�j7�}�|igA����q�-���SN1cV�Xao���\7�tf��a�"""""��K�.���5\8M	��`gM�������3K�>�6�;wV�FDDDDDDD�
���������!�6�ar�}?%%%�Z��H�kWA����/�����o���>3��SO=u�n>�������
���A�g�������\DDd{(�������
��w�i'SN�����Z�[�6l0�V�������"$;f����4 �j#"""""������DD�/����p�(>�)�i�����#"���UJ���3�|�I{��f���D����N3�m��m2���w�m��\t�Ef���>��l�G�x���q�5�ltpiK�����<�����}�������C���Ix��X����S������'-����{����{��C4�`����_o�5\��f5v�$:t��sO0`�#�DDDDDD:�k+����p������>���~�������/�-C$����""�+�p
�D�������p?���l��nJD$�1%�7����+�=�\��n�^�	��������������W��]�x1f������&L�������e��322�W�r�\��k�����7^y�<����<y2���������O�W\�8��DZ��X�/���g��y�-1m�j�������������>�KW���e0o�z�ZP��g�B(����p�B���G��"��_`�w�a���X��(�2��~D��%��eY�*�H�8������q�vf���.�p;����c�����#�sEDv$S�N5}���78����w^[�6m��3��3z���e�]��H2`���Y&��d���Tej��x�����p����������1g�:,]]�����e���X�lzv�Bv+���u��"��EQ��'���..���)��hxR3�=D�������������������H���*>���w�����[ED��C=�QA6��U�[���[�mX^��~0T�(������S]���Q�0r�H��_���0g��e��y}w������_�=
���;��3.��R�1��I>��\�l�i��3��[7�~Ed��������^��h�:��S��41�
E�)?
�������i~]����W�f1���}�`�z���v�9NY'��4���uA��OB�}�S��4�SE��;�����Yc���a��SK1d�v��!C��*C�����G�e�]f���}��s�<�L��X�w��qu?�0���.�s�9f]$��������!���!"�
�����S�����bM9�^������&���qa���8���8`tO{���E#!���f�w�^?R
z`��=���n�"��(���/M ��$N���l'��>V��uSV)&�s�!����""�f��v3�Z��k�A��*�y�������Lsx0�����_XXhn�����	�V�j4X�P?`��?������l>mD�5|?knz�,YY��:�3�M�������h4�pm|T;p�O'���G3�9����p�x��X����0g�� �t�����g�k�P-R
��`�C�{�������4���rbh��N��5���J�999
���MA��AAiK�P���T����6��)p�|�RD�:���k��Z38*�a��N�����GQV��D6AA�Z�6�v�Z�����z)�F���)���nw�~����l[�M������5���$��A�v_/�A�N8��~;�{�9\w�u��_bG>G�r���O�0�������
���������w�iJ�+d#""���~��)Xi�2d�t��K�EJf'���%�,��Nfa���r22�����O�[b���d�@��%X:�a��1�:q�"����q!'���^����w�a}���TJVc��Oc������HS�9���Xg���w���h��U�FDDDDDv4����#�O�C6N8�slz�E�m�iy��B�3;����������������S��'"���L!t��J����N�[��,+��k 0������>X![����O�>8���p�UW�n�9�{��c�f���x������O����'8�lDD�����%������L�oj�	��<i�����������c:u�`����|0i�)e,�-�`���A���p�L%�\�������Ea�E{���a}�<6����������$�i5.""""";�����j��J6��a������������n/��t����g�
��p��\�/�-�_MD�m����\c���3!p8MgfH,�p�<�AU��z�q�������u��M+�0ps��W��;�0��k���,<(0��)a������(\��5��z*��o?lDD���1-�A��._*<)Y��f��z�ur�26._<����L5������}$����3{��ZD��`��e�5Y>|n'����t ��D~�E���u�=�[����D�l��j"""""""���"�isV��s�@���oJ���1�3���7����Q�p��pZ��U5!L��:���Ha��SG���������R�rJDD~���6	�+�~�.�^��'����>���PM"X�p���t�hKV��S?����������<���l�/��I�*������FIV��&#\Sa}>\���Os#��4�$�G���NbC1'�m�8���)(��{�*K�-"""""""��:���pY'��q{�L����V����I�����~��H[a�f������/��6|�SB��kff�)z��z��Q\8��g�}�U�V��ED�m��?�:�g�}p��'7�����f��
���HG�D�6��=���f��9J885����Gm(���`�kHr�^��@b�g"�����ts��l2|�xU�U�����EDDDDDD�E-^Y��]}N��.��j?��Y;��>������V�BS}�HY�n>��#3U��r��fJ���|�������:C7��}����u��$h#""��s�����Y'6�pM,D���Qq<�DC���KZ�9��4�$�Puj���F����4A�f�����e>7DkkP�zQ�k�������$#��i�Z�1��X4�S�M��X���1S
'-�k�ED�%�d�O�n�6.h:������l�|>x<���MJJ�	�ddd��6��Z�|���#""
����t=;g��qY���!���s�N������Qd�4e������)�p8����t�������Y��~��X�+��S���(��`0h:bDDDDDDvDEy���N�5<_�Fj�S���6<����+�x=.t)H�X%"�-��a,X��T�!�iUk�5�/����!>������6"""�����q�L�!
#X��p�������b�g����`|����0F*B��c�%9q��?�����!b+��i���|B���Z���f���9�Q�v���7'N�[o��W^y/��������Zsy������%��F�.Y��r�h�@�u?���v�����U�����g�l{������4k��1}�\�I�l���
����EDDA�c���������y�v \[�����0PSe-D�5�PS�
���$)�`(����0���L��>���Z�'^��:�����*������/�N�K��i�X��
�/��C�gk��5��>�_}��N���s�b��Y�������_����6������"""-�\�s�DDD����T�WSY���0!� �c"a���in�AQlj�����v8Ez����a���lKl3��������oQ[�~E���jTTT�u�d�����H�QN��+���E(E��2�j�V�G�|-�QS���b3��SF�#�x�8��Q��N��#��`����f�N?�c���*��	?�Tm��?\��Hk+������F�+�7l?����H5���~���_���������#���+V�0!�w�yeee��EDDW����^`�������w���n��TQ�6*--5��Ya-�%DDD��2���
��@0lw�V�"P������EC�V"X��z|�y�:�FF�G��C��[D����5�P�hK7�a���#"����@:�����������Np�`�a������x��Z3*���{������F�j�I���2z
C�^G���G,��`+�BX]Bym�h5����&��eA��
���X1)g��(��p�S3�W�)������M��k�^/������a���4�\.�	�>}:&M�d����4����1���w�qN:�$�z�����Kp�UW���n�u�]��>���K��v�����C�-��RW_DDd[��#g5�^j�a�
T#P�5e�L���l��ZD��l^��uHg�~�0�UDD�=��dff��6VnI{9Q���fgg����u�d�����H3�o>.:yW\z�n=��2���QYBYUn�=�2q�o���3���1�1�M���8����iOS���5��!�(bii�����!3eT�:�f�����^G�����W*++��_`���&H�y��i������\����%//�,����SK�����B������C6����a���.u�Q������/�j6��p��%�2Z$AUU��L��Y5���?��7��q����?��FDD�)^��{Dw���b��]Mb�F��|:T[���l1Yr2�8k�\|�n�z��H[aLaaa]��mi�����jq���>>_DD������q�!������������
���;n���6�@�������Ga����g����OC���@�_����X��p�n�T�FE
�������`��nB���=�"6V�Y�|����V�a�&55�T��'.�����
;p8����R""�����7]t������O>1���:��?^]]]7��U�^|�E�{��������JDDd[�z\8d���3�T���Q�q�����i�u�i^��K���e�ED�
�h�R�f�-�_����$��������oK�Ax""�����H�N�=�����f*�3����	�����������r�)1��F�#�������]�?�3��Dk����������7`��E��c��������^��f������������}HU�m~�o����RP;���n������&��X{�������("�������{��eYv�]vw���of���YX������|��F���-#�%=jW"����	+�\}��x���7�G�^���k��A��/^l@���3L
���[���sO���|��/}��~Xf�5��������X���T����c������DDc1�N;�d�k������LVgR
c=Lv_�n�yN�~
6�{�6�""�N�6"""�����d �����{��O�����.}�O��/|C��w�|���s�e��;�/RWQQ�)!L\jk��f���Myy�*���H
B���kx��w�W�I�#G��u�]g���������;',�8��C��Ca��Q5��~�A~/
4�)""�[8�����~�������.�@�m���/-n��Q���i����f`�����""M$''c��AVV�Y6*�����\�~�z�t�GV�aE���|��cG����jO����h#"""����qF�>h3p4:�~��{�p�w=��F����5.R�~b@�����>k�]�������gDD�us�S�N�{��g����s^x��x��8�����@����#6�w���e�]�g�}�,�jk�w������^S&_DDd{�q�<��t��������GZV{�ew��!���z�����r�q����0{�:<��iV�J�!iZ��u��G�~����1�����X�a[�����q�G�*�""��D��d��q��
K?|
s^�s������vV��7��%���p����f6�N�a���l(�����|��g0e���\pn��6<��/�A&j�U�^�����~\~����q�`��	X�h����J�����X��>�.���v�����f���{�n��@��n�G��'�c���U�8}��m""M�KH~���{��k��f����������cq���s���'ED��h#"""�j��n��.���o������<ik�N��'����>���>��O]����gFdcm��1KFQeee�,��0H�D�YR|������l�Ie��O���~j�������;��];s���x���������+V����s�=�_��<�isV#-�
�n�Y���Z �����a�s�7F<^?�n*�#�����1""M�K��^��f�Tm�$�8��U�PQQa�*""%���������ra��/0����j���X1��2��8����x,�pi!��|�Eo=�������;D��v�j��0������aj�����7�00��|�d��r*��Hj����I���O\"�	��g�^�-����c�=���h����1c�y.""�=T#X���,�ry��gZ�v�x�z��������K�������k�H�b{������O�d�S5���p���"��R�g�������������mDDDDR��k�a�GOa���V���<{����K��S���d�yM�kw �������z���G�����{���;�l��0����u�P\\\3`��	6���0h��m[6yyy����H*+((���+�s�[h�ok��c��:t����h0SDD��X<���JQ�Z�+�7��
��Q�8��
�u�o���
U�k�H��5j��9���o�~�z�D1����c�5\J���<���e������6�oDD$I�6"""")$��%����Y{��6it�����u���s��r����C���m��-}�	�.�j���z�Z��z�2b&�0�����k�������M�#�=�����kPED�u����jg\^p{� A��b�5���Ul2�|V�N��8�[��1���
�#�%n��$j���5N��%�����L������1+I����G���l�}&��}��u~�������V]���/1n�8<���x���������c���f�����H�Y?s���x$����M��s��<�nx].�����>���Y>xY��Cu�r��������&ddd����n��fo�d&�8m��|d��������M�FDD�j'�0	s{&b��.&����l/���6/���$�g������n}��x\4d��>�On}���wK�.[�py��m��"ZU��������wF*K���Vnf9��7���Z1f��TML��dV�Lu��Y����2��$x�tX�f��f))1����s��w�w�}��-�|��g��~����5��>�(��d��^z�%����h��=�:�,�;��.��8��|���I�n���:�����{���WV��^�\?��zC)NCqEiE�Q��Qdt���|�]�cD\������g�]�eee��v�S~�G�<x0����$i��y�\y���H��qcM�����e�]V�g����<���;��7W��~;n��F����f��Ga�k���W\�/����_s�5���{���p"����'��5������N\~�����Z1��T���:u�����lwKV�������%p��BZn�d��/T�h��e�M)+�����Ni�+�Q,T�5���5����o���0���\��u�\��us��OCV���Z��mM��'���A������/Jk�e�9F�%��P��S:t0����s��Y�p8l�R\�������I�F����o����6L�a���K/����/�@3s�L��D}qqqMp�������O>��/�l�O8�Z��~WDDD���*X�X�	8��H�5�d�����nS�n*"�J R�Y���C�q��N8�<?�����c�9�T�����DD��#F�o�������M�����)g�`���&���t��������=�;*��f?�B�r�Y~����#������z���"�~47�6���a>+��
�����i�a����i[��N�K��^B*��w���{�|fk�u��������Zi�����(��$��l.��X���?��i��6�<��I�y��Lb��rf�K���o�g�a*������V��r�J
M@��d�����yb?��y��`<T����j�C�6gX5�k����Gc�=������W/3�)""R���;�{�����G������~0��h�"�����_�)}��������D��{����� �Z��8�U%��F$X�X$h��#��*���!X������9��o?��{S�g�MGI�"����n��L|�l�
����;�f�������%�����h���6�d��q�c���PQQa�EDRGL���-������[o5I3u�����X����������&0f�����"������A���������s|h����h����"q,-!M ZQ�]�~Ff!"""-���g��PO<������B9�3[�t��p��W��w��<���~���z�,!%����������)Kq�c_��2��@r�����#���d� ��$^���������3�K���Q�p������{[��7=v;��7$=K�TPP�����9Y�c���Z��^����*�t��Mq��O�.I-u��j��6�<����b��u�+���a�����|�������i�R�D����g�����O?���c�=��G�6""��z�2��1����M�]s�{���}@L�YWAAY�hn�#����f!"""%��:���>.�������rs�QG�@>+����sq�%����>3��]w���.��1q�D�w~��7�W�<�2�]{���+��)�FD�Z8�g�.�+��w�V!3����1''����'�����������vF��y�}I
\�����%���/Y�DZ���J<��c��&���9c���x�T�_��e���s���IumZ��Q����d���f�w�}����8���o\�_=^z�%S��6�����7EDD������L�a�{���	���)�Qa��f��D��vI0f=&��b����@�}��������O_|qMiz��e�zz�!�����6�	������9s����_7q�.��T�!g0���o��'I-~��,u��G���wE��m���(���:�`(��m3q���������c�d�Q���M��'����%����
�@�1���
6��I�<�������HL��������O�{@�=��s�������Wa�,���,���o�gw�i�������1a�%���H��%����x������q�c�q�#����S��s�f}�$R[�]�7#��������b�����>�Y�J6�m��"�
�}�UW������`�EDDD�'&��|���k���W��M��[n���p��w�	R�>����op����e�8C���h4jb=���������:(�#""�j���G��������q�����������q�{�� 7;��arTBD�i��~4�$�KJJL��I8�X)�9��b�� ��""�m^x��]���K�ef����o������-J�����=-^Y������^������}�3�}����������������t���Dl������c����+CU8�Ue����,C8�@u$���V����*
�%��A���':�~�I�i}������gv�af ��g��~�,��QKK�,1KN}����y�A�v���g���w�}�k�����Hc��
`��:b��=q�!��������`��.��EKE�����4,>��L����
S1��"���iOWWW����,���D.���wo�/"��Z\�MqqqMP�3�XV��:���1x�`{/�&�mDD�9�-����q�������X���U!�}.�n�<�Dc(,���W���|���-Jv�D�������{"���*���V����$ll�|S\C4�@,\
Oz6zy�{lh3����lo��0���O��;�@�.]6����D�SO=����Y6JDDdG��R""�ENN<�@���j���Id/**2�5������l�x����d�����""�Y�K����k.�Gu���Z���8���M6&��Di���*�so��og�4I7^�>_�����Oo ���4/��.�������.B4�����:x�����k���s����W!��J6e�*Bq#VG;A���}�c�3���]�$��DDDD���z���_��^z	��{/.�����s��~n����������o������#�wEDDDD��u��c�=m��5�k��#��I����2KF9K�r��{������L�DD��%�,_��~t���\��~���Hs�D�'�;����i�H�����d�3�6�����3���Q�s�M��%��oI �s_�:����w���������>��8��U��v���+zs)�~:�}��~ �q�&����1c�������/x����v����������^k������R�������s��7����C��]5�JDDDD����.]jj��oj����HL���������vDDD����0���2M������e���\n�=>�}id�C ��y��uc��R|8q!�*���$�����:���<�@�e���v1������f� �Sot},r��4��QX��I7g�u���J�t�M�����d�3�<'�pv�}wS���x������L��2e
&M�dh�������222�c 0��p[�n���E���ED�&�p�@��?���&s����T FDD���g�D(���Ey��3��&�01"�~���<x�]@Z��)?�BEU�>NR�9G\�^���s
&��+�"	�6����(]����.����Z����������kj��='b������VL�a��W_}e�k����|f	�v���Gnm��1�lk�������q�PTTd��Hjkq��SO=�&	��w�5���~o0��sg�(""��,\^�P8j�{�p{�d��\n��v��	��e��b����d���_b������^F����E,E��d��B�����Q4s���I��T���!"""""""[C��D���D"�5k���L��[�X��V�a�
�~��p�����S&��]��g���MD$���D�#F�����B<��c���{����b�
{���OM2���HsPZ���j���*����f�%6��J����i��#X�����2Ijs�����~�	v��|^��{�1���L���pq�JZ&��W`���a�����7""""��������/""��`]��b{u
nxdn~|<�z����L|;s��izL�Y�l�^��2Q����9��u7V��2Riii��|����p���5����;>m��)9x�`{&�r��������J�����k����J
�4y���I6��r�Y_�1c�2��XD��P(�����d�v���4`DD�F$����ae<n -�#�y&��a.�"��.]�x,���^��1�chW�}IEL�����X3�s^��	x�.���������+���:�p4�x4�@^���8��6���g���W^if�RNN~�a�u�Yf�)<�������j�\^���y��g�[
���l���������3��`���������SN9�~EZ+�^x�pc|�D�:u�U�����p������/�`��2���PY�����.����}^F������r��>�ai*lS?���5�A����v����JKKk������E]�v����Q�F������Z`E�A�������`.�X=��C�+I,oV[�$�LX
�n�M��]1t�P%���H�����9?^f���x4h=������2�����KG�vY�����fM@�����C�[M��4:e��c��
�u���.dX��g���������C��eX3�]��6��D6'o6X#""RW,���q�5���K.�u�]��n�
w�}7^|�E�H������j�[DDd{+����'���N1Uk�W�{Zz����
���r��������sp�����eJ��&��R4���c�[���T�XO0����Hjkq�6t���b��������/��I����/1a��M�\�r%��Yc�c�
���dC�>��DD�Y��-2q4�D,�$���I6�"�r$�1�\T�nm��k�A�+����V+��4��^�T�3�M �hm�j��b�t/r�<p��Nu %�� R^l�#�)k,X`1?�����[x��7L{}���X�z�}������a2��g���_~?��3/^l&Y��5�~��LDDd��Dc�x�B���YXUX��4/<7��L��� ����l�|~�p�������wf`��r�[DD��b{�e{k
p���\FJ�5����D�~�����/��������/���������o�,E��s�I��{L����M������
r�![uSijcF�6�2\�'�!\��H���u�2���#�J����*���odO�d��������+����,��T�i��L��	�MU&�TZ��+���RXXhj>���h�2�L~���L���������c��IfIM���}��l���of�6��DDd{�q�Z<��tDc	�����G +i9��O>��`�g����_3����x��if���HS`�
��b��c�\n���e2NEE�y�����oo����:Nk��O�d������-B����#+��������5����c���fv-����u1����o6��"��AB6Z�"���L:�%�t���j,XVd�7�G7T���-���M,R�pU��f����(v������l�T�J6+�x	���4�6��L�{��i�z�,G4n�c�
����
0I]"�QV��3g�i�;A����3l��=��]�tA ��?i]�M���?��&�����?�����o
��O�G}�����C��#�~s�{+��1�3���Ox9r$;�039j����k���i���wQ�J��Nl{0�����/YYYfpHDd{**��+����/7�D��^��!���,��rY�����Z��<��(����GJ��"�^]���s���""��m$.X�t�Y6��V�F���,����9�g�(����fLGD$�<��SX�j��g�M�de�����������(//��)S����
6��[�n���I'���\5f��0��|�r3����=���_��"�����.0�F��`��k�9��u�a�&�,������������nV��,)*a����qJL�~t���}����|�*�����������a���#$��]��,� ��T��,n��?�8� >r�t���S����*�<����J3��8��1V�m*�?���������������3���g�}�K/��$m�������q��W�D���;��_�V�,�������J��x>1��S�N5H""���������dU��f�K�A �#\f1���Y�p��
�D����o���v����H�a{���|�MS��m#&��������V�q\.���8����T}�����k�I5�F�2U�-z,c��13��{�9S^��c���mX�=p��G�?�`��lDD�%��O7^�/���>v|\&���m"�
��G��X.1��sn�p?�9��I�����:�i����cE����)Q��s�m�c�H��P[;���s�����I���4�D1��	�����];�m��fs�9��c������3k����6o�<�dC�<���Y
��x�h��?�3��1�II6""�=U#X���,��5^&��cCI6����~x|�������4&�0�x�����
+�0��	5N�����
��}V����3&�d#"��*��r)�����L����>k�h8������*<����un��w�	��f"""��,-(Eaq�I�1		�?8���3b���:�����?"��N}�	d��H�<GU�F�7����:�����h�[�������"�d�,[��f8(�j}m��1��h�������d�
�y}���2��������H*s*���g�y�YvKDD��b\�1��P$���.
�P'���s��R����J�PQ��4%"�8�z�QG�W�^5�j8������
c=|�	9C�1���V�01g'q����>�$��|��&����{��5�5;����)������;3"c�D����r�O��?->?� fi���j<��t|2i�9^$�1�f��JH��Z[AE(f��9�G����k+�(��g�0V]��C�C ��J�W�6��xa2
+
�����edd�$V�a����YGDD����:���EDD�3V�I�����Xc=8}�-q�����O�{DD���a���c���c;u7��vz��=MR���l��m�r�;���HK�����b���dB������ -�#��; �����f?��ou���z�G�������&Ie9}vA����dW"f*��,�`ME�$���	T[�Lqu+J�XW5�K%�A���:�~���!N��LKK3I6[�*L��5���\mDD������i�:�g�Mn�Yn��������5�z��E�f���wn��gs���X���O?��E�j������z�e�f���)S���DD$�U'�����&~X�)?�B�,��7-�$�x�Yp��p{����@f;kkku���y�X��~�e����IJ�q�yh���H�cf���Me��ea,/	��UeW��'W����^G]�����oI�28CN�1��j6N����$��gDD���e�]�LY*((���+�sI��RD�y�H��W�<�_�HV�
�[���R������jD�U���e}���]r��DD��?��&O��u�����	T�T��Y�1�nV�X��'b����xQ����H�����Eb\�����hk��b���
%��L�I��7�i�;i/��i��������'\����?
���%PN�4Cy(�`4y.�����gW���[�?l�I�q�:�3��kw30����������g8[�����~#F��n��f�AA�:x�?~<N;�4�w�y���y��x��GLE�-���SQQ��_�,�����]|���k����3g���"��H*i����v����d��	4��"3����M_������~�~����2��wD�Y���x��_}��i�q2c3m��E~~�yt6�sIW��m�q�����""bZxfT���;���;>mQ��;�4}������7���z��8D��P(��������(��gV����:�/����k��@FY������N����B�c�{�	��5�>BR�J�Uc��W������pQ�$�Q.�nm�F���Cn���u�����K3��G��M���M������L��R���(.�z�	'�{���=����g���W^���2��e�~�a�u�Yf�)<�������j���>��&9��{��������M�LR��'�l���xo����q�]w��l��>/��":v�h��)�,��9���O��f���e������X��s��2dN:�$p�[��dcc�e���f������:uR�OD��%���L�O����������/-.Vv�����.����'L5�S�?��!�TD�1q����>��i�L�
�IL�a,��HU�~�W0����-��~�a�����I�F����o��]��']�v������-���k�������M����6�~�.�
��lL����������Il�8O�6�����eA���O(.����m����\7�z;�qTTE������s��?')����K�n��\9�p�9O�)8��9O�/m:"���&X(Rl8�VU�������q�5��`
���''��3�{���A���8""�c
��	`S ����]w���7�4'��g8��CM���n��wFuu5&L�`��V�2�1fU_�?�0���W_��?�l��m�s���68��I��D!&==���7o���k*��VZZj����|��9f����;v$�=x.q����&���y$"��]n:�d���W ���J$k"AS�&���rD���u��������w{��S_[D�c1�J�G��222��M��n<�1�-��q��j!"�:X��q
G��]��s��M7�dH��Y*"""�Qz��q�'� ��#6��1��������� �����h�����?���nA���!TT�x4b�#�S���x�p��a��[���HEq�+DlL�0`�Y������XSRRb���������9H�,�2���
3e�EDD���\r	.��R�g�4.Q�*.�&M��Y��}f��5���^��$��D����b�k}	3T;��Y�..��D���;����_MZ�h����C5�3DDZ#^;��'�?wt����}	4�6�*DB&�&OVc��C���k���,%"�T�dl�I�a��F1�z�(����6��0�FD$���iF�Z��QG}4�{�={�qpv.�:"�����_��;?��?�2�z.�r:X!�����o"�tT����O�/�q<zv��v�+[2s���Kg���"�p!��F���,l������X2W+1�]�c����_O@�Gd������;w����
�0\J�	�����R<��P\���6UERDZ-��8�t���j*����>}��
hL��q����3�8�E�.�����M'����z���e�]Lkk0����}���&I�.V����[6J�����5g�y�Y&���\PP�)S���7�0>���s>� �=����M��Z:JD�Reu���#��|J+Bf�S�~���\����7�7��m�N��u��i|l������l��}����k��Y�X��/�������KG��kZz-%���C�3����{���TK
�Z���w�}�|�v�D��x�[�5n����=*=��Y��^r���.]�(����'�\1�����$�J�`��7c��	��(fu�=�
x�&����
[�V���C1���!��]�����A��oD�������N��.����4����l(`N&��o��,_��

����TJ������[o����F���O���7ii�{�9S��q�i����^2$���/�����w��=�	=��s�I���W_}�����Tr�m��k�|;v��jZJ��������y���I&����/������k���x�X�(�vt�����;����G���{DD��|��G�s�����M;�������
��]��k&�������6-n�(v�k����/��O?����������m�������4;cv������	���,B4Rm�c���1��J6L�`5���@EU�����,%G���3'�t�t��RQV�8���9>��y��u!��6�>7�3}�������y���%��e��oI����I�g�&�3�R;���ar������G+�FDD6�Y�L�a�������Z��K2���]���d�e���o�%����hL�ih�r�?�|���s���Mr��H*�9o-�yk:"Q&�������GZNG��[��#�zL��_Z6���=����x��i����"�F���3l�1����|�\��U��W���*i��6��F{�����[q�!���>g�m�MDD���L����k������|�����X�
�x���	�!X��K8��p{���{�FF��ELue�f ��Z���=h��5�5<�j6����?l(2'7����v��P�x�U��"�q+�����&���vn,C�@��a���N;)�]DD���g���{�5I%����l/UUU����	H��M���Uf�.�k�B��e����@�hY�����9��p�QG�{�����{""�WQY5>�f!����u�e������h�/��z���/o iY���,���@0���+�����oi\������,�M\��Un�l�$��joL���|t��""�mV�Xa?h�l�>���H*8������)El*�D�U�7�5�e�M�M�|�I�I$�����
��G�W�<�[$�U�Y�x8h�i��n�MI�����������r#X�
����}��8�������fF<�6N`��I�^��,-1}�t�����>���3x�`b���9g�y&���N<���x����y{����������-��5k�t�R{&�u���������q7�����}��l�������K��u+����6%eA���j�X��
_ ��\��nf��f?w{�g�������BeUSf��DD�;���.&i;��67�9L�f����|�6\�������{c���f_D$���D���_{��Y��I\���������2\���l*L��x4l��2%�G��
���F
�ju��WH*���s��:g���2I6,Y���!|��?s�X����*5�\"���������1����m��z����d�O?��&P#""BL��:u�y�{����;��������>��w�6o_|1��c��-	V8��`Rff�����]FF���L�R�������W~��i���,YYb��=��3�~6�0��X�p&��2���������4���d3v�XS%��&�0FSTT������k>2��j��������a����H*kq�6��;xC�lXI%^��������KH1Hc��}��f���oX���Mq�p���e��e���8 b�����u.�s���?������sJ����1i�$����&@�R�� �0\��A�u&�|��w���S�XDDd����^�8��#�����,;��kW���ck�G��n���h��6���h�����\Jj���[���@��S�aD���[}�e��
��������x&v�0v���Q,���*TVs���H�����<�HS����l��a3Q��j��x���*6l��n+����7Br����D��E^Dd{�ufk���������S�g�-Y��>jS���e���}�g}6���n��3��w�tk7���W`��]��c�c�+�������
~
�������)K���@0��[=�;�[k��y�����B%��#��'.�@��N�hk��i�}��m\�!�}w�s��O$��mS��wc"�i���[n�����^��i�m��5�o��F�t�M���o�����~������w�tk	7�uxq��!�����oa�25TVV���8�I1Lv���k���5��s�5����Y�f����#���}��E�����|/+���9�<'����i��n���
�X�ak�[�q|`[2��5�""M��g	)�zT��l|���^�zm��,"��Z\��Yg�Us�_�j

�~�����e�K���
��3����`������������5�OIm�DuZ�I�	�(���2��+V��>�><{��TN�]���M�(R��`r
�m�D��|��iCT�RDD�v[5??��O��T��$+�8�`������z������L����n���v��w���\p���l+1�����O>��>h�m�����<�QE�A��Y�""�]��Yh��n]�y/�[]���]�{<.4{~��������4!�?��3���S�����x=�J#L�v�y���z�{��q��G�{�9��c?i���8���T�V��l��FzNG2��N��l��s�MEU0j%���l�
;nV�I�Q�aEi�QT��n��x����^��?��������?b@�yt��G�6""B;v4I��r�J�X��<OuLb�;w�����~�����+�#+wm�#_�<y�I������i��mU��)���k���$��|������[o5��K�WTT����6�^~����9\���v�m7�/"��e����K�����=�(��r�y����u%�T#����.�[��m}^D�)������o�n�:��6"'Oeee���@��
�l�~�����+"�jZ\�
���w�}�L}��Gx��G�zu���EDDZ��~X��Ze�7
q�=p���lgmm�r��R�V�����I!.zw�{
�z�q�|��2����Um&�:�zy����g"���|f������Hs��/��Dd{0`l��$u(X�x1~��'{/���%�crk�=0|�p3)��c�E��}�#��s����T����k��[J���Q7�p���J��D����\p~�����.��"�y����W_}��;�u�f���x��5��������(��M����%��T^���uE�=��	4��"���u�����dmv?)�D���$���	dgX�����N����M�6m�scr����1a��>�x<&��6 �J������Ey7�C���S-�I���M����hl������������O[�4;���M�$/�{���)����N;�d�o.�I%�=�B!����1��,���^C�{�9��]}���/�������N�`]
�l����?�?�>����Y����������w����x�,��V�|�~uc��]a=��<�8��
	U �a�7a��
A������T��U�Z�Eo�����F<F�:��UU������;��}��w?����@�w�T���8k�C����80W����
���mS��m�)�n
}��AfIm�<��I<(++3�999x���<���?��.�����jcL|�2B���^z)�z�-��a����C�>}�~�b�&�0�P�-V�9��L�
'���j@S�L�k���I�����]w��#F��4�����~���~�Lr��gL�����1l�0����������m����������ue��<���X��~��LN��3�K��������G#�E��.���u*�����Gw��G4�����4&^�X��U���3Y�	5��/.�De��x�cy�����k���������c��F����o����6��c���7�0u^�?��C��g0��s�5KJm��o"""�����������	�p���B����Z"�E�T��Yv&�����^<T�DL��lN�FDDR'���7�A�.��|��7x����sJ�d�5k�l�d���w�I�qmF�m*�����_}��x���L��v���3
%;�UUU�3f`��U�+��}�G[�p��q4�3��8�[yy�6m��m����:��q�~=���A(7�j"�2+
Q]��l��5U!	�$��u��+G��U��(+����i��m{nL�ab6�	e;.##�,��l�������j.+�`����n�����i��T�v���#��������K/��c���f{������:��WDDdG+�a]I�)+��x/s��L�
;K����������F��)H��b����L�M��U�Q��[�G��+"��l&g�mk)�FDD��r�Y����R����7�������������,�?t�P�`CL�a�!�bJ�7�Uen������f������+x��g���p2�I'������&����#G�,S��_���%��\���w�s��fs�]���R��E��`�����.������`P�D�"�H1.%,G,4	8	������{v��cz 7�k��E����6�d�N����-���l�$o�;8��DDv��������)Z��QL�a���S���l,���i"����jY6��T`Nv�p$����M,YU�H��_F�u�nn���X���+�EQZ��9{�j�/��k�����X=�mS�n7r���"���d�y�8_Y(��UQDb	�`���3���v�#�e��Xm`��If�;/\��3���s���u�����r��o�c�9]���$-��g�m?���V���MKGm���>�L�a�
l�;�w�}kJ�w��i�����I��=[�rTY�v�Y�h��E&ye�=�4��k��6��7�4n�y�h����l����+�]u�Uf���M�68��3M�N��������v�����l���0u�
7�����Q��2���|E�10T�e���������f�o������>W�>��b&Um��\Dd{`L��@���.b[��kn�����}V�9���M5������x���a�S����#)���h�?+��nLJ���M�6"�k]r�G���U��O�EZN�\^���;4L�	W!X��X~����x�����Wi����9/���z�\�����A�l2|�Z-C�?@�z^T���"�bUe�~�9�s����K()�~o���yd�a[�6.�-���|��;�������E�Y�\�Bm"i-�h��>��c�������7I!���3�����k�3�Wo~�E]d�\j��6����s�����^�_��g<��c��Mm�g�6�	�h���9LJ����q��'6x�^�t)�|�I���n��
.�������&�����cG�,"��q���"��;��_�ee���vU/>&'����4/F������������G��4^��y��G�>�El{s��
��M�=&���`6����hM=���'��?��kc���!C��N;a��w6�_���/�P�\d�R����Z���[���<C���Pznx�
\G��EQ]�
�p�)C<�O>��b,:�g�I*Z���X���
V���y~d<��k�E�a��aWG�F���/�^�(2��7��|���f@.��3���a�
����
�����L��~�p�G��""��m����t�M�^��������R�����o���K.�����{����
PTT������w_�@+��v�m���K���0���[.K�HN`��&�g����QM�n�
�&���Di�h�|9�<���j��D�G�������e��X%6Y���u�L.�����������&�IO�/��
c8������
%�p)����a[����B��H���h��2I8��v"
������4A#v��E�$�_�qv��lDD���{/S���.�,2KC�tsoL>��
�lX�&j�*���odO�f5]=i��V/4I6	�\�Xj�����l�8��:��TX�U&���� 2�beH����7������$�0@�.}��'DDD�� ~�����	/�6��}���P�j6~�a�+��q�8��s���0���6��;�1o�<���""����k�����2�����@V;��t�����;������_s��`-�~c�u���HD������W���'�3A������\{cr
c<��s������D���U�$��?Dn�����k��N8tt������X�
����T!�B"A,D$Xf������D(�C�b�����eSlB�k�s%j����s����6��.����m��j�b�;�m�i}8��~�a�}�5U��kX�����|CL�ar����A�)�]DDjpI(�ux�h��?�]��[��������{0�)L��m����5k������v�F	:[�����Tm��O7I<""�UqY5>�f!��*���6T����h�/�$��\�y�@�����,�5�kb�4c��DD�'>����������0��D�
Y������#'L9�;�7L�<x��m""���E�W�����RQ,7/""�*���p���,��6\*X��$�T��F�lM2�&Xnu��������G�W%��:�/`JV;�5�)��$��<��H�������#baP���}��E 0�T{7b9���"""N�z�����SO5���K/�d�{��9��Z7��Yj����Y���b�N�������{""�OqY?�^��4���q���/=���������N>w{���h��o��TVE��O�����42�g�ds�A����6"n�T��D���|�>'P��z�9���Q"��Hk��GHX��v�@DD����N����'�����p����QL�a��h$d�/-b��;�����T�q�%u�<^���
����Fb&�fs��.�8&vY;V����i�]�o�XxmY�`&N�h3�c��F,Q�|�r|�������H��}��c�<�HS���6~?'m9������q���f�=~)�k/����V7��);v�����t�����?�GD�5�
F�xU�I�a��?n��}����I6��������JH�����c�9�����dl����$�l\6�i��:�q����|�DD���tw�yg�0u�Tu�E�Q,Y���MdGb�f�����I#q�o�����"?/�����PVF��C�ny8��]q�{��}�����R�2:��'�	���.�Q����:���'P�!j=&1��}��k��lPXXh�l���g�2�pVg8q&<���t����~��}��)G,���v�m
n"���:!���������?�g��3�<����q�������>b�c����^2��S�N�^�rrr�i ���������O^���$��K��
�wC���Y���p[�Q,Gaq*�#f_D��pI(N�����x�����q}h���j�*,Z��<��X���MdGrB�L���Cp�y���?���������_2�^9�x�������_>UZ��a����D<�h(���"3AA����s��?�X"�~(��UW���}�mo�K����$��\����g<k����m���y������l3k�,3CJ�����[�DD��e�����^|�E|��'x��'��_��n&��?~�A�]w��T���dqbnL�e2�/�e�jOP�}���""����D|�
�X�a�I6��������C�1�������s���_���x
�k��8��r����7���]�v���k�o��h����n�X�4i��LDD$�p���%�f���%��rm9�-+��EXUX�`H��������q���M��+Ce8�Ue����<C�:��TTq0������Q���W"Dn���a�#�����MR�����y�����j�a@���������{�g�������Q��IDAT����������w����IXaE�_b���&)���s#G�4������|�s���o�������p��{:lj����wim:��F��t��l]����B5I����3�����A��,U"�&STTd*'�m�,
����fX{�7.��,�|N�DD�Z\�m���3kj;|�A�N���H*pb�KJq������&���'_��/�[�������3�87=>_|���dS=?y;�B"3�m�TD��,���m�����Uo�����e��Q#��`�[D`�����&���'��lu7��(�1H���5k���IM��������Ku��������f���q���{[�e�g��i�%�s��A��C�517�}�U�>��#�_�~�~��:=a���X��I6L�i�2����%�,#e*�����d��zY}�H5��*���2���U�YD�i��!�������v^nn�I�IKK��4�G�p��TNl������c��Hjkq�6��������gO����y���QG^DD��b�f�����_���O���E(�!�s��c���H4���+0~�R<������~@(��������+���s��j�f��p%�1���F�&�hmv�����h���e'p6�����������6���fi
i�V�Xa*�|���f�RF\^�vG�cc��e5�-%�4]�v��{�m������;�����^{�53A��$�#�8x����:u������r��������>j�S}?�����e$~�a���I=�+I[DZ�v��k����N����*D*����6�k�c�J�*��I9�8����g����""���h,XP��d,�I�L�a�����#�g�b�k�����W��a�K���HS�L�\�����������{`�]v��!C���
�o�}�i1�iB��/_n���9��[�MJ5K�������'��,_S��Y��e��xp{�p{�&�����(
G������}q��>V�IIb�u����.�����U�����on,�B�����So�����$I����1o�<��,'��rJ
���W&�pm�`0h�c�9��E����@mD��g�yW^yeM!��L>8����~Sx���q�e���X���0��v���{w�y���PEE.��\z��f�^x��~��5�4���.��_~��J�7�|\|�������5��&�����!��u������$�8	M���<����m���#7�A�?������Oj>��}{���o�����W_}�i��2nw������6����S��3��{c�EDd{�y�:������q�=�2�K���G>k��x4�X���2�1�X<���'2���%�D�i��0�'l+1~��r6���{�hUZZj�U,�p��������Q�F��������$���RmD�
�%��|�MS��6v��j7�*��Dfj�l/J�iY�`l���"�)��f.����k	�So ��\�h�D	�P"fJ3x�r��p��c��� ��o��.^�\(��5VN��gOD,X��L��	�]����~,���}�~�a�y���f4����<l�sf=�G��i�����W�8.5�n�:�p�v�I'�T�"�����%J������7�t�M�^�UW]����������p�
�^���������?^[�������f��5&������B
�}��r�������3������1���Nh0)�������,c����I�����;�����k�������N;
w�}��j�#(�FD����������	��
#-�W��(�����Z��x<b��1����~#{��<9��J""���7�Q=����B
��l9�j��1Ag���f���L��������h�FiS`��vi^�_K�6-��1c�g��%���lO_NY��=�J��VG���"-���4���[�����x���*�Y/�Q^��'�y�Gv���v��-�~%f?s-��d�~q�i^���^�	��	TE�{�u|"�����������d�sj#I�&O�l��L���w�o��������Q��/�v� ��/���4e"�4_J��+��x���^�5�\�{������xs����{���lj/��R��s��4����	�������4|^�l�;��[�Kl�g1���>}��T�a� ���	6t����$�=�&h�X^mJ���G����2����Afz��l�5�����\Tz�������D��V&i,�V���O>Y�h��cG��wCq�{l��:11��U���#"���M���""�pf�����(�g�@(�:A.x�~2���e����t��<������R�L�IO�Z�_����������c����l�t�d����4��������>k��[����~������"X�?������.]���8��A].W����3s��UUU��H$b��x,-	�%�D�ayyy&�����X���Gu�i��?�����;Na������0�[�����s���$�f�����d��X����9��M&���4�9s�l�d�d�����T'��I6"";B����������/2���X�i�Bh���\��1�z��E'��e��2I6
m��ll�;��q�����x�X���d#"�$T[ZJE�^�z��|cb�`��eZ:J�+U��_������sW����<�:�u��@N����B�c���9����������
s����+���L�
j2|��T�24�@S���*�����bUe�q���}������A*���,�������|�Y�~�����I�����b�a���j6�8p`�q""-�*��o�������j�6x/0`v�i'�OL��5kV�V�����?v�yg�����V����O��7�`��y���Me���
fl��g<C����\������5q�D��?��,g��8�����X���~&�����I6�h#";�����|���\,ZQ�0��Z��$����c�]���C���3"���y��W�Xc1L�aU��-������M[���}y��G�������t�G}���~{��~��;4  ��mD��(���7���������N���m�^�B,Du�JS�����
G`�=����q�j���b��#����A�<?r�zg��,az���0�����Ef��z������#��������c���%b1��$������@N����cM�D��"��(�fS��7�fu���1���^c`�����ey��=��\���1t�P���=4��8#��Fr�&��i������?�mD�)�V������)KPPX������=��}'OX�"7����:��U�]���d��S��8�p�67&���\���q���b3q��9����_?�/"�JZ|��HK�D�5�d��~KV�$m�;���D��U�p!�Fu)m�(���������Y����3��?`��/��N��^�\���\+�>n����V�E��a�Ly������}���&g�
��rMk�klq�����n5A��B�6"��mD�)��Q���L<��v|��2������\��q��!k[�o�V�zP�����}�S/U���SZZ�7�x�feN�b|��%�����N���*.�$�0�s�QG�L�I%umt%i8�S;���,&�DY�}�s���v4���67
��g3�#)�UlB�k��s)1�}n��QCI6�s'�:�����U��X���KyJ��s�W����I"���T{#.��sg�)Q���Hj�}�>
�.""��D�1|��R���YpYl���?{H�nom�j���;�}Z.<n�9n��"��?`]I��m""���k<�@ddd�e��aB����
�|d%N�b���.15f�%����t5i!�t��)=
W 	Z��p�rD�P��)M��[�:��%��}�ju�9���!.&����#������Hjc
����
3�0���n�`*gEq������������n�OL�>�<�����GVTv�0EDDZ��%��t�"W��qY�!�I��g����ip{����7��@v{��r��u�h3����i��oi={���G���
��L�ab
�5�b��6|�UnF��C=�T��$���������Dv�v����k�f�T���,
e2!�@E���I%���
'gEUV�������}IMn�im�X�>k/��H���<}����`$n�w�N"_v��wM�)bar�7�|��3g��tb�3������)iii�Xh8+j��I���MG�%����6i����q�UW��o��W_��?��~GDD�e��
c�����&���l�Pc�	�f?g��/=<n������G��4�A��
�
U&��'M�����w7���H�mDD�q��g7���(#u�!�� ���:9.��U��E���<O�#�EB���������Z��
�������t&XH*�����L��s�<GU��F�7������(
��Y)����@�}�L�����M��l�`��m[��i��<�����~��h��<y2�,Yb�H����_6��H�8+���,C�Q^^n�#""���)�D��
�M�_ ��L�o���6>x��q�s�l��&�i"���7s��5qr����ae�i���MDD6hV�6'N�d���6���������}��+""�������=�1VI��P�:�XS]�An���K�{Y�P��\x����g���v��7#�x�8PXAE8f��nE��`� !�����L����Xu�
�����*,]����G(2����&�&++�@�$���
�s
pV��>_/**�O?����2�Yi�x�������HK��������;�.���3�g�v%���*�#����HS���-,LV���(�gff��9Y�	9�X��-��	LHDD���3���w�����#�5��D����_�MI{�����7&~?.D�@-_����l�t���4V�yb�vC��';K
���<|<i����`�uZ&�-���8e���M�c�����G��������U��� ��F�t2�G��m���(	�L5�\Y8����0�����{��M������6\�����]�v
�o���\:��8#��n�;�8�.i	�F�-y��gp��W�$r���~g�u��o
\����.��3dgg���y��g�w��������=����3��4�^����k7��4B�����0m�j�s���Y}i�����Z��6�
���E�(B������{����?�~_D�����d���~��f�	Rl+�B1���
�6\.����|��t�b�8�d%"�j���G�q�l���5[}e��T������� ���4G�?
��c����4.!�W]&���Jv��&8q��]v������$�H���G�N��sV>����"��e�(
cEY�zFQ��$�F�I�B�#/Fv�!�s"��4L�!V���P"�_���
�����EDDDDDZ��L?�v�6V��(���v�M�	��X� �Qt!=�E�.��7DD�s�9s&���k�j�4�
��h�*6�H�GgipNp&��^��g���MD$�i�MDD�a�����?���JV�qq��tt�0�<gS��g���P�q��I�������nB�QG��O��	��TU8�����q#v�q<����p���0�P��N�18�j	N�Ia��u�!|��9�����I�qfF�������$9�������G4\��m���e��F[<D����jGL�X&����|��Hcc���@1��qeN�b5>O��7��a�LN�"�����H3K�����)�l,AVW��=7:�1���;�����Hs�bM�~c�}��p2�����d!����\x����VG����]����w�MG�.[�hE)��g���	#KDB�F<�_�	��n33��5��T�ii��e������4�h��c���P��������-�X�
��bT���^���E.|�������6�����X���nN���0n����:\���t%"��x�4���w��,��#}����$��8���.�u�i��\�_+
a���5%������i�+��m��f?��-��b?iZL����/���%�s��r���?=n�.����	u�����<��[��s�X�����m���E���5(�;�j
�9�ik�Y�l����S�XU����������������h��D�����Yv�����|3+�n2��	5\�UlX�����N;
}����i��,Yb?�T�^��g���y�\y��������?���:��7����]vY���3Px��w�yfG������?������:��Hcq�������N�:�T�����8��b.�zf"�V_���D<^�����;	V��Ul�1��I9#u����A�����Hc+((���?n��b���#�ZMC�a�Naa����R^x���D$��5
������mDZ3%����5~�R���_��,hu�_z.�2���M�����[O\.$�QD�K�Xo�7�L��p�w�p�~��0�<�������7�F�����m�=Hg0�:����`E���K�j6i��b���#o�Q��I����o0i�$TUU��+&�8���� �3��d�~���{,�v�j%"��)��~J����DijU����l�p�py12���;�Z�k&��[/�~����~GD�������G1�k�q�0E|�����N�6mp����������6��.""�B|;c%B������@F���q`u�j:C|�j7�����7L�HO�b��U����?��h�7X?s<b� .7rt���M�i>��l����w���z�,��9�������X3�=D*J�o�T��wo�8��hqP���������q���>�r�m�,l^^�������?��T67� ""�-2�|��1�������{�Af��o]�_�y��{\x�\��=�d#"M����]�v�=�8
'B�9�Q
�F���
1���Q����H��pE1B��y��g�I6
�:Hn�9��r��vaQ��Kk�$���L�K��t�[<���@���,5������E��m��cN���������6�����)O E?����
V��3jo��b��5���3l�m�a?.�RL�a0�	5���5�3)��w�����""�Z�.]��3g��hM�}��Wf	��
$���l+�]w)*
"���b��2�JU��;6>FD�q��
�=f\������0��vb���8����C�&��$��g�r�a����~�~�f��5��\�5��Q"�k�W��������Rx�@zN'����p�t!
��d�Y�����G`��=LUi*�����_E��0���f�<^�.��PQ��1��k�>�|h(�������V�E�������$|�m�[�>�~�H���A�QG���m�*�
�8���>f��m�8�.1p�j��3�H�<����;�0����f6��Hk����Ww�(&j����o*L������GZ7�7�t��4���0^|o&>���W� �_�z�w2�s�(���;N:x ��etE��0.SPP���z���3m#n���f���<���a%&�8�8�l�%�5�%"����Q�*��c���bM|,,,T�]Z
%�����%�N���X��$�h��	��\�������H5�K�hEYE��v4�����}��xk�}�^���Wm1���>��onX��k��^�L��S�ciq��Sm}G,fc-y�f��At���?�&���l�*�g7�?s��1��|a{�6��v|��k/>>��~WD�uP�M��&��H��<��C���Vls(�FD�J��3�3~�~��#1�}�����������.����L:�p!f]�v��]�?:�g%�HD��0~��o����e��J9m%����1�6m����OF�Z�NDRS�D�f���v�Z�A����EDD$)���S�,x�e��(�o�U3�m�������s�l�u�i�ok�^�1kg��������g���c6$?���`}G�1��c}��~�����Ie���[�y�?EZ8.��I�7�e=x]b����UDDD���+""���\���5�N�����!��i9�jom�H�������������\!�ys�z�"��v�ygu�Q���31�k�T��wX��m������9�%������mT�FD����>��MdG��-�$�P4\�X$d=k �$�x�P�I�`i�>]��|^Z�������������������}�����i�{�&�;�"�>�O���S21�:w�#1S���6�
��8b&+��x���JM�����G^l}��e��������������i������MZ#�f5�y����h8����a��qKKK��N&�L�4	��OW���8>6���������>$""�����7�xE��d�J����&�������3��}x�rL��7�i������
L����6���d����0���8""�Ar�����bI2g,9%���`��Y�fa���h<x0
�}�����k����9�<�#V��3`��Ys���Z����'�#L�]�??������Q��YQF���������I6��"���LrDiEW�n�����L�R-�M<2e��M;��KC��������=;�����`��B��W
V�k������V/�1�v��$��`�,A����{��4L|�S?B����a�Q�2��n�:���*���}��g�2eJ�L'&�p��v���wVUU���v>��������cDZ�eK�tT��.5v�XSv���^�/�s�=�[lB�/�^�t��4�E+Kp���a����z�O�F ��u���F��CN�
��F��e��p�A���w��i|�,���o���r��
5���$�8��v�����NB~~��I5u����Q�6����w���?�hf'/^�k����Mj��-z���=����r
����_���w�q&L��E��������9]�t�W\�<�~g�S�M��Ai��y��5n"�<�y��
z.���Y��	�x��2DY�&G(������?��>����$�Y����s�L~����L��\�@n{�-��Y���������int��!����}��Y����'PXAaEQ��hE1zw9zs�I�Y��'X���Q�t��uA��nB�����z��-[���{������*6\���6u��lUVV�%b9c�m��#G���EZ�eK�hS���67�|3n��6{��������,-�mD��������m�?�2�k�D�4�����D<�`�Z��K��\�gD<x����""��������&����9l+qs�d�^f���8q�m+������O<Qm*IIum��"�
_���?������>�o��v�$���?��'�x��{�1�����K/��/�=��c~lj'���s8��D�)�-"��y��!}�[�5 �O�
��u&@S]���k��H���"�8�3������U���
V��*\f*�0�j�7o`���!T�9}������M���:G*Cq�*������u��f���`+����Q�lT"Dn��g�4I6k�|�Eo<������x,*@����]�FR�Y�0�������L�_�7'p�Dc��+V���H�`����$��V�y��RTVG�{�u��9o��f��r��M��b�8
�*Qd�Z���*5\�����&Y&77�f��scu�o�:��'���3�|ND$�)�F��l.��b���+%�t�����>��Cp�a���n��77�py���-�6m���z�����+I������?�~5��W9?GA4i-��������}�w�����\&*�F$�*6�f!����?�:f�z��}v��ka
	�uD��.Cv��&���E������ \�=��yF�$n��8VWD��,���a�(�$�����j�	d���DN�aX;�cS���`!�/���C��/D�]������
���,�� ���5N�Mzzz��)~�k��������4�b��L���D�Em*ysr��J�xDD��,X��L~"�h8i�q'����������k���7�EDR�md��[��$������_I:��s�����������}�QS��6&�0	�����0�N�e�:t��w��'�|����w�}�bN�>}���?���?��3���������2!�g[O6�q6�t�	G�XWR�X|�����N�Y=m�l���{������k�u�oMi�D�
�H�J�c(�Qm=ge�he	�zF�So@�����>��7�(���Ag���#�MG��K*`p��h��+1���]�i�YS\����n�3""""""�U�����N�.�k���-Lt�����<��`�)?�z�P��4>.����Nq��N�MC��q���]iBD$U��&���+u�l�>�l�y��8���0t�P2�
2KK�~����?�a�T^^�?��5�3
y���1~�x{/Y1�Uj���Z{��6lX����=��Sf�Ho���rv���n��r���������F��g"��_ ���1^�udnPX\�'�;�O^�d��v�^�&�Lz�_����������z������ik�$x�XG���#�C/t?�8�Fts:
�}�Eo<�i����������*x�� ok;����4g�z�jp�����E�.���V79�h��T�:9�u%�
WZ}(2������4>�m��_o*;�6�L�5�G<������f_D$�5�D����<y��S�lXa�}���+��\p���jx�^�U��?��y�����M���.�'�|����<�T��y��km��������#�7n\�����D�'_��yK�L2�q��l�ew0[ ����#�����^�������eE�7I*�7����1�?w� `��������>��
�6��k�HF�����~�^�6G����d#������N���i8�isI3|���M������SPYZ�G��&""""��]^:��
��ak/a���T������[�c�n�8�1�B��q�o��OY~��kw~��H�bL��0�`lfsqG�1���|""��Yg�01b��IM�}��7
&~���/b������~�����j0������o�$�0q��V������+�:�,{�at.����u&�Up�h#[��hp�Q&N_�I3V�%~�������z�����cm>?<��3�Y[[���<&9���Q^������*�,�����Ef��0�P����R��X��F ����`���6].j��l�:���5�2
���������-V]]]�hS��""""""-MNf����a:"f?'�PU1��k
�#	Y���I��T!XQ�h�����~�g����a����N���7�r�&���������p#"��x�4���w��}���0���,X�L;�������
0������nF�e�m������z�,Eg�q�I�������?,X`�g�y&�}��M�n�SUU������)V��S[�p���Y�|������[7���Zw?�
��0�p���]��q���X��D�e��*�2Sz��W�����c$���^��f�	3^t��X�=�s��x�<�/�i*�<�nt;���J6�%�q!��m�o�TUPP���~�W�6mg&��i�������$���2����$�����3f��D"�jp��+���\��A��~x�&�l/�?�<.����?��<��;�<��#pr����g{�����k"���.�������,t��i��I""�T<����.�mON@e0�4?�5.x�~��^����NG�E���X���8����������4
�b����`���R1f:v�hb:
M�����5k�F�qc;��c�wEDR�!���{{��W��tGl?��C�7�T�d�E��{�
3������u���N<��Cf�).�������@Gaa!�-[f�%���[��XFF�Yb��w���f`GD�%Z���$����	�ws��\\:�:�Um<n.��u
Le�l3����gW�d�,#���\���3���/��I�`�[�$�����~���}�%J�����8��|���L�a��A.��������������d#"""""-���������g���;u��;��Nb�p)�`b��d����fb���
�E'�T���4)Nx8p����8��0N��7g������������8J��h�#��Q_��Tw���zN�
g�����l���o���O?��$]��=�p��_4���<\B���]v��,I�#t�gPE�VU!�y�;XZP
�H��_z^M��~.��AT��4���*C���G`�=����R]����t�.[�x������1�d��g�G����z�bsn��v�����*^c��G���E�]l���v�?m����^{�L6"[�D�,�����I��ID��q��>��g��_I��9�����L$1����C=����$j��@m�����7�|���;�����=��OmD�)�o�D����x��H�W-?Y��{�\����������������������f��q�eX���%'�������e�y�S�F�IEu+���D�z(��~�>���'&��=�Q:�\r	����d��������O��[������'�h��f����D�5��(~����dUI�D�\����N��H5�K�hEYE��v4F�f}�>$��J�`�GO#T���2S���s�����`�%k��`	���$6
v���.m���/������3<�ts���l��{�73��4��������H�8���/���y�L��mC�wjs^c[n���2���E��Q�M�X�����	��O~�9���s������h#"M��*�W>�	�N^d*
{=V���O��du������a]���v����$iBK�,�k��f�����z���M[�c;�FL���N9���IE-*�����H���XE��b���9sj:	���R���5k�y�G�.�1�e��f���u����D�zq\n�7��5u�T������O��a����R����Z��!��.Hv|�s��������n6�$�pU��L�
�<���7��%�>B�����{�@��9&�������~\:
�BK2��D��3�C�i�j������o��z��k��2<�nt{&<J�i�8&L0�~:m���D�������Om!iq���K���8����2%���KC�Z���x�`l�I"���%��HS�D�x�|���D�#1�}WpY}�4�����CnSM6!
[�"��8F
���\|:�S�HD���~��G,Z����:�~�������u�]��""��E%��wQ��A1�D��^�z���6��9����������M���]ki������v������d�n	3cY���a�������|...F���MV���w�������%��,�{���mj����3�����o9�Pn���]��7P�����KW!�4��}�q�c�)?�>H�E����P�b\�}�M��K�`
��dN��L��?��$���2\����hu9v:���6�tU�i����PN`��5&����`��v�����$D��$j�ms��<�E�h#�<(�FD���9�q��_b��r�}�_��/=�@���DV��GCW�"�B,n���n��\���7��4-V'�����rQ�a?7���9�]D$���D&X��L������Y�L��:�}���������+��b^����>Gq���?���o��!�={vM���[n�
7����``��H:>��r�!;<���F;b	+�:
5,�I6iJ��g��M�M@Qi�uL���?�=<��
wt��i�x��b����#BYe:m7���!��H~�:�������_������~���c~�)�23�>O���>Oj�2�J���2C��}��+��w��������[K��>[�v:��?��s�fs�����u�l�0XQQa�he{���c��1�m�2�"����-�K�K����h#�<(�FD�JIyO��^x�G�d��r{�lz^r�f��c:�0K|�+�!,G4t���M��=�vM'"�xmbebN�_�n�i/�����Sq<�mv�e����D�f��M�^{�ef#3XO=�T�x��X�r�y���S������l��A^^n��v\|���+�� ����I6L����6�|��Ij�s�v�
qpis�z��X}�!��w���tv{m�M-0j������O����r�>S��I6�`��
�3q��q����k~7��p
�d��T��W��+�u-Bk!n�w�	 ;`���/��{�c(���:���a����>m�3��d��fc�f�N�p��4�`$�D���
mFg���f���$�c,X���'�����`n�6m��R�kXUU���L�
�X-q����mX� �\�������~%��M��R��H��Di*�V���?�b����������N��������Q��,-0}��tN;:mw�]��7}�tS���8,�����R�Fq\�m)���9�����""�H�6��^{�5��w�3#���Lv������	(���N���������������Gy��`�q���
����g�s�=�3�8�E�&�<��&���I.um4���q���0�[dG`����0ym~Z\b]Y�&a=���6��!b�q,��c.�#qd�yp��;cx�6��\���T�D�H���VD�-E��AF����{N������*C$�,��<���
UmR��b�*j*� T��C���]W�ML��:u�I�a��m������}
]gx
����w0��c���;D�;�[^�xM�5�A���D����%��HS�ia!~�;�0'@�ew�?=w3}G��� X��,#�����\}����H�ai��ex���M�cx������S�1Y&�p������,N>a�
�nDDRM�De��V[�b�F�&K�.�I���
i���:s��Z�\`������Kq�]w��+�0�:x������-����	��Z��]DDZV�i��)��A�uBz�	�n��E,\�p��P%b�d�M,�����8��>�'�T�Q�������pY��*7�]�[[���*R��3���t����9.�E6������YR��>�-8��l�?�E"pa���V����M]�8
��];`#"���������W���:lb
.�o`m�=��T&�X,��E��
F���Hcb��Ym�I5sc�7NN`\���i����<��
�mf�p���=r��6�%���q��W�A��X��I6'�|��J�*++q�M7���_I���q�!�l�o]���G}��>��u+��|��f�����V�q�T]�y�L���^��~&�c��M��_/���Bqy�$�l,y{E�8n�������N��K
���`��W#�z��O��q�)��z
��u��&Ph?O&��sP+�����x����m�<^mjq��?���|���/����m��s��XXXh���8�f�Q����S��	�o�TU,T�F�y`�EmD�����������������@znxLM�}��wU��j�&0�_{<{����t����	O������6�Sts����.c8�������v����H���Q��J�y��Wp����{���8O��~��f ���O7�B�M|��h��g�a��1�����hs�����[n���6��w���4b��*i^6|�������s��o��q�0c�Z�4%�WT�o���k+rt���F��y���t��r��Ng�U�[���������x��s�����i���&j��$����xk�����XIi������zO"�;��[�����o(V]�g��.��O �o&�&{��70k�,s�a�M�6mL���fx�����3�SO8�2d�~�Hsu�9���6���������������s)�F�yP���4�����[�E,��1\:�=��yV;�����KGq��=q��c�o��4"N|z��'M?�[~~�i'5�!�WZZj����\|���x�ND����Q����Z�c��$���~�F��O>����g����n���}�6���=8
m�kc�������ik����9K���7��3W!a�Wn���,��r�-^��z�Y��/�?����Kx����T�x���u�e����>%K?����[�3��+��@����ot�s�K�f��������iK����Q��e���s��y��1���"���X�%<�Yn��L���~�6m�mcCC[}�k����M�d#"""�'=���]sM,!�!���Gkb
����� ��J�O�BF������4�c8�����R�����9�JD$�)�F�T�)�:��Mb�d�}������s����f>x���~��l�
K:w��`���H��Ux��)X��^Or�������fd�7[ZNG2������&��}k:&N_^�:�$����.@(Ge(��`��*��2El���dM�`����:�[��B���&��������f���w�<+dsX����L�f��U�l��V��X������6$g�gff��DDDDDDZ�vy�=�+*�#O �D���$�p���?���f?��Uf��x,�7���c�]���DDWY��)g��������8�ylZZ���T��mT.�y�@J�n�����&\�~���Q�
��D��gO3�����m��#k��HK�����1M��?&�&�=�i�fp&�p���Sf;��|��\
�`]>�z�*�\m"���U#�0����EZ���6$�iNv���O��;	#Z�*��ND�p!ng� ����1��M
���]<��&?�5l1����f��<adlW������
g4q���&��_g����b�a�����dEDDDDDZ��������`&��Z,iB�k
�[���Y*���Hu��V_���'\�L�c���1|�N����4.����m�����p�Y3������8'N�����/i��#$��w��}���0�rHM�
/��~��"�V�����F%�n��V�p�
���/�5�l����?���c7�����k��C�T������Z�>���o
��{�����b�`G��� �x�������fp��/r�c_���� ����##�+��4���R�����U������]&�q����!��'���d-VM�/���)/F<������huz���R��'���N��I���1��.����;�.�PS��GD����FV�AHk�.���`�{�tDJ���x������+�����mE�(�����W���c�v�h�w�L&��1(��{�a���5m9�
�fs/l���0�@N[����{����v""��3�<c���6��{���q�Yg������.����?Cvv6x��w�yf_$����_nl�0�Jz�k��Hm\6��I���N@0����g{�~���~��:&�x,��~��%2�p���������H�cl���>����M�
�IL�a����X��q2UII�9�m�<��sDDR��Q������{���f�h3s�Ltw6�~&�o�����^�6���>;���n��>�(������"y��?�,O���|�M�~��fF5���?��Diqxw<���0g1�	S����$�M;@5��O4X���5&�����_��.;u��,c�f6�"�%HD�{����Yg�un�=>���
&����������e�S�q��M"	1q&X�s�u*W�3���'^���L.a������������?�����������������������h������Qk��O�~O�i��U�0e��l��:�����];�D(�v*5,"��mD�%��HS���w���g��k�*�a)X�����&6/���y��.�p�)#���*|�H���c8o���i'�m����W9m%�T�a��m+��8���c�5m+�TS7�f3�sMo���&������i������-�#�����\[�~�6�^����F����{���7�f�H�rT��;DD��h4��`q&<X��(3����n����%���(J*�5�!�DoZ��e �]dt���=Z�f�=�wG����v�;�[�L���������1� ��������!�c/S)�����\r�#�s�=�8�w�q���w����
q�������<��
?���������jc���)��a�p�Q&�p�n���Sw#���D��v�MI6"""""�j�b	�\w$���8I6������PU&���4�g8���4L���c5L�w����K�3���y<&77��L�$��f�h#������������l����j�D�0����F_�z-Y���l������g-G&[��ZC�����z������V&L��-\�c���u����'=�E�������m��b����b��|�&�l:������Z����>5��u��cH�JVu��7��t��l�6�o
��/��>T�]�<HR
�p�?������2��������5k�������HKr�m�5�����Hj����������~���*8�g�?�@�i9�����x�~w�H�~\�G��=�m��."�Tv�ygr�!f���V��%X��W�������$|���*���8�q �����k2W\nl��������J��`l}�{�q�m��3i��MrR\\l�������?��,%�%�{�np�Qzt���a�&�	��d�Q/�uox\<f��t���v9[����\�u+����r�\sl�d�\.��p�m�{�N5��#�X9�e��1�pr�FI��4a���=�b�T�eE����X�������\��Hg�P���K�6�����Hj��`-�ys:B��7�n�m������#`mi9�cV{�Y��R�������HS�5�Kz3������n|�ct\�b���8IR���"�����������K�g[���?�����(�����7�1���>���{�1,^�����9�>��I�q�����^{���6"�b
����l�.k�X��"��!��6�\.^���T� �����(v��#��6\S����g!����l9�|��I6&��=�jd��X(��N}���.�LI��4q�D�X��^�1��m��m\.�M�6��%�N���~�i�e>ED$5mM�����-""-_Iy����s�e���m�����a��v{���_Z�I���3�>Q2V�����<k��m""M�m`N�b�bg�������x��f��a��$fK��39�TdK}�Q\z���^���?��.����_yy9�=�X�7�~f�e���U�����yyy�^���^��������k��f�[�t�i�-������~�U�	�BX�|����?3�9%�S�d���sJ��1xs��_����S���_z<��u;E��	�#�0�E�qti���.��
��NI�.Y���>wZ&��9��6�;��b����|�L�"A<�^t��$�����F���?l�w=�$�H�`��>��T���3;;�$�������KK��sc7�&e"�HK�6�l�3�<�+��eeef?''?�0�:�,����y\v�e5^�x�3��9c�������#��:t@��]ME5����o?��Z)�{��������,t���������Z���=���X��_Z6��;Y��d��>�p%��f�AF�'��KO��~WD�����$�pr�3)��[^�8����V�;�����{ws��H�5j���{{O�6�
�����������
L���;�4K=��F��i������_�_Iz���q�)�l6�q�W�����������SO�u�]gdu1�����2I-&�p�t�^��W��mZ
�Hs���~��Y������y�n+���6�b���=�H��-���}p�~����lZ^�\�����y��_N�YsL�q����m���d�������X>�H�{+:�>�{���>��%^+������/����5�����8aVZ�/����!�4�*�l�q����7�3���/�9<���NB�=�#D�7�eK�h���������o���l�A��>�����{o�T6������.-�mD���������c3\*���g�O��uY��x���W�Z]k��\}����H�a��To����/�}��g�o'��mb��+**L5c~�8��q���\�/"�J�h#����sq�G`��E�+0�������'�p��93�1�|����4e�j���~�I�M}8���5�����J�����'�x���7o��U���+W���J.������n��c(��e� �4g��\��}�3�������r��OKs�Z�p,G�mq���p�����)��U���U_��p�:��}�j2�j4L��	��k��P5J�OA���:>���� �=����cs�=��5	6u�c�O���~�af9+i}^�u���}��Lnf��{%�c���^l1h����!C�#D�7�eK�h�����3�fG`����c����A������]�?%��HS�[}�O&-��~��,?��2r�X�i��
�
���*A�|
���i��n>
�iZ�YD'J}��G�2e��F�v���Vr�lj���W�c��:��0z�h����D�n>��C\|��f
��x>��������z�j��$&����f&��`
{.\h��m�s


�W��@`P�xv4%��,��jEv$'�?~�R���D�/�n�JM0�G
��g�������6���o�_e'�l#�����N�N<B"5��U<D�}ND��oBZ�����Z�z��+��v�}L�����3^��\Gaa!�����9�SM`s�i.�F�-Q�����_�b�|v�������/�������1p�@�i��h#"M�����]�K���v�Mznx�*���T��D��=���hM��m�IM�������4+Mp�
���p}�T��IR�c<��N���x&�l	��i
�h#���b��c�=f��yL�9���p���nu����>0?��G�_����dCJ�iY��c?���q��g";��+��������E��S���������}�q��>8���(`�*���6����_�h�b�jt��$��-m���Jk��o`��Y&p�$.'�5m��[g4�p�����G�4oL`hHS&RH��D�m7y�d|���f"�{���IU\��>�`�����1�����e�'����������IL�d���rs�/JJJ0s��M���j���M�6"�Tf/\��oyq�~����������������ED~.����O?��U��a�b�
�66���KL�{l;;Um�83`����xC�9��w�uW3&"��)�F�;�����Oc��9���'�|b���@1��Ui<�@s���Y\�gq���?���;��=�P�����4J�������?���[����<^?����y���x�H�
�����������
O�o���Z��$�������*�p��vC�E������_������C0i�	6���7���4�0���q��K�����>BD�eS����}��b)�k���$���4s���������W	m��eX�b��<��X�v�y}����C=��#G�c8���y�����oc��I��N�d������K��Di*�W������k]c|iYH�����@�(�@uYb�2�}8�����)�����l=�a?��3����v�EL~!���>�R���96����<����
qy*.���{�����\J��F� I>��c3pB������<�o�����o��Z�3pysg�o�����F�a�|(�FD~-��}�?&��I������}^&��py�V��U$�hE$X�h���L�H�����?��zm[��4o�H���up��u��fd�;�1�������b��@}A�8�YTTd2L���L���S""��m~�������I�!��������~�)k��X=���_��w�mf�2�����w�a&�����<���@�$���
?[_2��J���RZ��oM�?^��Y���F �-|m�q�p���E��X�H��X��g�o<�wNNV�%�~���OM�
��[�����mK�����h��?g\ns�9'�f�=��_i��h#��(�FD~���-�mON@IY�������k����3�L��*A�r�u����2��N�
���2��ED~%�o����3L���d�6���?�d�
�cP��r�!4h��D��P���c?��07�x���D��n�
_|���/��������6�\s
�����6.9�g�`V�}���M�\i��h#"M��������X���n��@6�i�p����M<A<4�E�
1'S�����S��6��,]��,��5I�L�Y�`��������n�H|�P�
�8�W��I����3	�lom	�z�
fV�i��&�hj���H����`e�������$��TQ)�A���wPQAQ"��Z)�Q�X�aXP���^C����y3��F�f�����23;)�����������(N����)�����O�~���A��P��`MXX�8��$���	�������w���i�;�8i������I���Ls��}r�z��V��'b���V���@��)�����&�������Z�mO8l���j8Zw���f��C9D�;�<���1���h���8�U}���#%.6R\�\�g����Jv�6��n}�f*���������38Z�������!P	:��]��Y��e�~��I�>}LH�n����m�:W�!��C�5z������n;��#��*�w'�p!A��
b��=���o�g�lJ�'Z�82*A���%"<L���Gr���������/�z��RI']��*���*6:�VJ�����ZQ��e��O���!��?����{FU��u�Q��[�y�fY�z�Y.�e���W�\i��P:���{+S��q�x�XE���+'-���3�p��L�#R�v=XF\�S��C�Pz!TyC�8i��.]���Q:w���0J/���t�F��������iii���J�6:�S��(>�w@�"h@��t��=��1� ���E�~Q��J�����p�AH�Gm��j�����W?�Vf�0���j�M0:�����8��B6lh�w��]�F�8
���v���������,�xr�J�������.��<���_Q|�Y�f���V���5���5z��������9��n���r�����M�>�������&j*�1'����_����T������j(��I�����vv+�WFi��E��	 �������j
W9�'��C����q��fYniA���dse�-<�)3@��=�������w��{3��!��#�b�]G1������}����#?.� ��Z(�wg��>����:��u�F�r���Z�F������\�M���q5 ?Bm��i����_����j�
��?�\��o �����={Z���+��]����j���9������?�6�8�s�m�����+1u�j5�����{$;}��z�&���#�I��u�=�����[Y�p������ZXK�W��T�t�F���p����3���m��}�@�1"�3m�41b��"Ti5��'��A���/����/��5��'�����u�����w�m��������v�Y��~�A�
f���[o���z�,��?����������O<a�x��C������z����+���-Z�M��2G6l�'Q����[G"���A��=�����9�2m��bq�����������������L��qcA�����c����s��E�?@(������\�������W0���U�V��wo�0`@����A�&�p���W~������
�DJl��g�Z��9����������0WW���<>��4�OyO�C'bf��e�h�F��hE�`S��T��t�f��=&h��F]�v�~��Qv�cD�'�6z�j��c�e���2t���J6�7�v�m�����=���r�w��!}�z����zq���y�����~��7��I��2A��7-[����L��O��DHtB=��M������/z��oe��W�8�w�3'M���<��7��t����<�5����i��]�V~�������=�Xi���i5�p8�� t�\\_| ��c�9Y�C�T{�1=z����1��r|��$�i�z�x\���-W���0���j%��\w���,���'��#�:�QfJ�!-�'^�zk��UfBF'a�J'=�l��������'�4h��:I�~�zs��r�J����M��w����@���[�-�C+�i	{���~*k�����W>��K�,�?���g�C9D:u�d�m�����+�4l�P�>�h�@Y����_��"q13'/��$�r��=���c�g�����G|�DD8$"<L������M�g@m������]���f�������l���
�"��6����@i���w���o�]^~�ek+���c3���`����<	�3C����+{�	�h�&��#��4�I�!���fR'���#�7��{���X�6J�W}��7O����r��]�l�	��IP
�nU�����|ll���a
����O������;
��>�L~��w7P�4�8a����������Y�����9�kM�����Q�F�r��{KE7��^����E�77i���m
�}�l�"3g�����i�F:t�`�P��l������f��Md�^X����&���BqXX^��@m���^xe��n���.��6:I�eu�G'������\{��fh�Gy��������O4������[L)e�OI������m�L>����?�h9�u}3_�����a����&p������=?d��6-���d��]�u�����p����M�E��/k������N���+��������~v�J�*�o��V��(<�o�`Off��^@��R�Z����.����^I�-m�������1c�������wn������{���
Nhk���:��/����������B�EEE�*m�E�e6l�'��N�{��	7!��>�	��p	���<�\��;C�r�;�
�pA���^�Z����N0}��z�)31���;�4}��G}�����O�I'�d}w��>X�y����\��w���K������������Z��8�}`/�rx�������Kn�K<�Lqf��+']<���a��������+{��G�������������|��"*�s*�w��67��z��@���U|���2d����*g��J5�����_m�{n��&:t����[E�7���Jsa����m�L�8�l{��7*(��__�>�lS:���iu���"�k���UiHG���c?-�[���?�@�^�d�������o���:��R1z��k��y���Q#3���/������9sL�r�p������/��pNE��Bh����
6����9�����x�V������Z���q��%�v���Fya���l�.�k��m�����D�6���/y������h����e�Y�%*2���f��=�����}��=������3������I�XSZ �n��u�0�?�u��R�n]s�T�z��-?�MP��`i�w=0m�41b�yUZE��
2��0}�t>|x�����h*()I��}��Gi��}����sG5E�;��G0c��5�eI�
�v�����M�_���k*�o9�����_x������5����r���o��w��p�y�����nZ"p��C�7t���VLj��qA�
����;��q��6�Il Q�I����>�x�or]V5�T����������/���V�zdd��������^�z��S(|1R�m6o�,={�4���woSJ��/�:�t����������-1\Rk)��9r���?y����WZ�!h�:�X�K�yz�������)��%��2�8aa���#W���0�x�o���F�%��;T"�
�A{r?IYm�1o�<��"++�L������*�}S�9����'J -Z�(�w������1hc�E��p���{��_|�������W\aZ��o�^����t������_�h��E�i����/��&6�6|e���r��oe����}�q�$HL���}�)�~T�;']��m��3Q|�C.�������P�6P��A��;���O?�l�NU��^'����^����)SJ������?�|@W:)Sx����+5K�������jB6����c���
Ft|=	�pHDD���i,��o�}@�tBEOD�7��X�j��+=v����fB7Jo���^��������4�N��T��J����l�v�����g���V������G1X�~��v����J��s:w�l�K
�����MRR���y������N3��������S3��ry�����!��=��������=����8��qe��{{��%�����(9�����H.h���O[KbJ�v���Z���;���:��~���OD�������>��%�J2�]���S�p�<���U������*5�'�����P"c�H�#F"�b�_��_�%:���$�7�:���yG�|��r��m�dd9K��������@!�����Saz����J'=.�}���jtY����h�F�k[;��'Q;t�P������5�����a�4|�w��I�d���r��������C+�je��4L��yN?�t����Tm��)��E���j��>(�'O6��N9��H��h9�Wk�����8=���\��I�a*��z�����3K\Y{$;�����{�$.&J�uo)G���i�,1�
��:��(�fs����VYZVY'Vt�������W�+k���4��W�*���p�e�,��P������9�%-��}/�6�B�2�Wn����������(�Kj&��Z������o�Y���+;M���=u�����Kl����
6�;W�v;�������@�IMM5����G�R�n]�1l4���?���r��A���xZ�G7z������z2����*h������_�e��h�cl�(�[G��-�TFF��r��M}��V��bJ��Q�LNN6������9��U�\�����:
��xr���W��/|/Y�.��������h	���p���%���~ry�������c�l!�t������APK����QP��oU��N�W��mO��_��L��D��b2P��w����R>�v�|X�x��������=��O��D	�����f����W�h�~a��+5#G���_y��e%�{���L����L�V����L���_~1�z��+�w�t������
6z��m�������
�$M�>}L5��~����-��!�}��	�T��>�������UN:�$�9��sLph��1f:���q�f�����b^P���w�����v�v��}N���\q9�LUgV�����R�3��9(L��s���!�pA����U��b��+���1�N�>f�.���?�����{N�m���IX�C�L��2���?���[?O��|�����\x(�+h�E����E��P��g���i=��U�����}���'<����	P=I��W/�Q[�l�o���T����z�Va�+H�J[���q���+�(]����K�������CZZ�\w�u%���*�l
�7�{���n�M�n�j�@�������Qn�������4o\���&B�2]����lg�����������K�I��
�|��z3�r��:�]�v�|��~�z�kU��-��f����E�)�
(�,�>������uT`���������P5�7��O�����B������Q��cO�$�Fy������~�n�Z��7���+1	�%*�������z�;;M��m�\���H9�{K�?��Yv�fKJ�s0�k��h���$�:77�\��T��z�������������������1�V���l=���5�~�9��&6�y�g�6��v��S�N����_����^
�bbbbd��!r�w��=�D�o��9���������Y�n����-���m���C��R/)V��O�6-��=�?�����l�[G\���o���{�L�(�=�/�U��#��]w�e��\��2e��?|�m�VV�^m����'_~�%�Q!mKY����P�r�����#ko��y���N|�\zZGi�'1Q�2���d������x�{��\�#c��q?�?0+u��r���hR?^��_Z6M2��].?.�$'�l%��$���k�V������'�`���&l�c��^e>\h�(].L������9n�����~������5��
�h��>>:�����Od"~�����L�F�
����:��{�n���i���Iz��?���M��g�s������}O����4H���
��V\T�����|�����.]�Xk"
4��\���*k���1c��!��k��&�]v�~7z����/��D����OF�U�	��6���
j�^��/� _����z�r�����O���[.�X ��N�my�,Qq�$<2*������<����Iv�v�r�dd�����W#	qQ2k�
y��E������Y��3�8������dK�,1a=i����h�T��MFF���9�T������r�� bW������N����������M��
�>�?"hT�n>��9��S�-e[�~����@��={�����{�{He�a���M{�a��Y[���-s['�����kw�����/=���n�m�B��
��#Z���F��E�9�P��@��m.!r��G���M�j����c$(��4�9rd���^���O�"�6���o�m�F�!�Sv�K�l���p�'�x��e2���ug����R��D"#�=*L\������*��l��1��L�n�!9;M�F[A����y�!��{_/����M6nK�~p
�~�'{���������z��-�����5H�-�~��gS��"����~k���D�q�j�����t�9��O?�T���7�|c�
�~����������_]>���s~���J�?����9�)3�%�?�S&��@>�~�l��&n�Gb#%&*\��C+�,Z�M^�p�L|s���l+!���X&N�hJ�����a���n3��i����_5i�{��Gn��f�������"��iSy����y~��G>|��z��E�=��c���N��
�Z5�� 7]�M9����I�'}2o�Lx��q���K�I�fI&<�'�]9i����n�z�=���%�����}�ra9�}#����"��;�y�br��GK�ML����S'9����N�:f=;;��m4��r������t]��h%���r�J�q����uk��h;@�����a*�y���������r����V�5�y@us{r��_��s��f*'�;�ER��X���#QqI�`>E;��|���W������=��O@ ���L�(�N��r�-�i�&k��JX�*����z����[���4j���o��cm��m��
���d��*��Q���Q�m��$��Z(����g�I���4��t���/����kJ�DF�Y��<�{��z���������������N:T>�f����_�7=�|_b\�<��\zZG���4�*j��E2w�\IMM5�ZVX��=z�0���>4Ps�gH�V�L5m?��th�F��x��������q�*��i��k��F��[g�{+��KC2����!����f�x���^k������+���S���-������H��I�H�����p���[r����L�+��mt���b�z�6�:
�W�uT�mTYa����I�&�+i5pS�V����M�v�W�VU�� hX��,Z����{���r{���[���g�7�Y!?������q��>*���M�#�446Z���O���,��,�x��CI	1r��G�E'n&vT�Vs�����i�&�zR^Ot��������%++�����D�����>9o���cm���Bz����dM�^��E��q
��W�o����
V�_���40V�5k�XKe��M�#F��}���V�����
2��0}�tS�������'����������+s����*O?�^x��f�~�������W�x^y�������h����v��I��uMX��A}����t�����`E����M��f�a�A�I�����KTB=��M�����>�Xgb4d����T(v{����O�����n��@�!h�����9s�|��g��_i�TZ�^'lN;�4������E��_�|(W�w���v��a�����	,eM4k(��������^�rr�r�Q-d��c�y�D�g��k�vy���eOZ����rN����ss�����Z����-��� �+)uce�yG��MDxp������aZ�j{$}�� J0����h�H
�I,=�c�����nW���t����k���$%%������ ���j���r�!��%�6U
��f��m�6K�.5��:y����6R�������;��E>�6|e���2z�7���=!��D����n�S/�q;3$;u�����N:��
��B��tA�'3tBf�����_�����[D�l�b-��@���e���U�^����b����0'tF'��t�b��6*k��;d���e[�wV�Kztn.#��)���o���f�'�~�^�vzd�������%������M���q�sM��~r�\w�Qra��8�T3mG1c���B'%B����	���'z���;zmo��4��~���}\*������O6��6@p
��Mull|�������J[N�2�TEA��@
����F$<�!1�
$*V/(��B~Qa���������Jm�������'Y�������E�r����.����[�z�)3��C[>����|������I&
���Q����{���c�����[7�;��%��������2-������j��8�=��������,yf������Mnn�4oTGn����dc��P}��+�����2!
]�a�����=�q(����}t�����lC�:���	>�N6z��w���_�����@�����5���:��
���g����IZ�������'<\""����yo�p�~J/���+]�s��"*@��os��m�@@E���$���'kEv�f�����<����F�K��;_/��kv����,����L�uo%o|��l��!.�G���h��7K>���������+g��NN;��DF��c��}mM�+������pZc�������w�5������.��=��~j�mU����8��E����n��������c�9��6@�
��6�]����~�\@��=��G��n�����6|A?�|���r��})u�Lx&�nS��������j7��=�����'�4���)�Q�F�T���e�( ����]��������>)X�6J��cc"e��4Y�%U\0�n��qK�F����c*���K�z��eg��]�&@i�'�N1?H��#��Iub���Y'"]o"�H&��;��6zBG+'�I�P
��������Qp���N�h���X�-T��������2��6@p��MMll��6��5��{L.��R�(����
���d�N4�c��S��Q����A�����7@-�c'�6P6�6@-!hT?-�{���d��4����M-�������h�W�n���p=�_��_�����n��q���]��g:�������H���t�N���NF�	V]z��g��X�I���ds�J���J6;v� h��`
��*`c�:u�:��n���V�����6|e���r��od��=!����������MI�9���o�x�n��u���a�t�n�#h��������%����5E&�+�K�.!w5��
P�4h3t������Lt�j���#�
\+�x���_�#`�j����l�����������������
|���QQQQ���O�t�����m�w
K���K�1	�cl�����:*8�U��O�>�BY0m|��}��wr�-����m��"�>��YA���_|���u�����`|=q�%y?F�w�"�F<�L�I�)��tq{D�4H���� ]o����6P�����1C�,YR���k���n���7o�y3�A����U+ki�z�P4h#�������8��M���}�r��\k��/W�'�C��v�x����!W2���v��I}�/�T
z,^Z��~,B�1!h�J{~�P{n�d������M�G5h���/�u���4i��������
_Z�j���2W���W�!�1u�� a���P����ug�vQ�^��'�QrN��2j`/�'@� h���������^xA�l�bm�>Z���oT'�6���(�� h�3��o�'��
%"*�{K��~�D����tqf��>/���.;b��l&sBB�G���{�8%#�M��"�6�:T�=�����{i���c�
G��M�+�<.,T��([�m���d��qr��GZ[k�V��J6+V�0�c���x�,��
_�x_s��a��o�|�qz����a-a�m�<N��W�mz���cSy��>R���e@�!h�����ln��Vy��W�-���
�A���I��P��MdT��E�V��\w��]��'�m���V���G��dm^�`�{��9�+mP[������1���zO	tz\���e*B*}��	���Od�������5�����r�\�q h�8FDy1h�u�V@�O<���5�,�n������������������5��#3�Y&���Kv��������X��E������I�#\ztj&C/�"mJ1�@m"h�����W_m&�JS�	"=Q�y�f�fT�6���(��h�F���������)���_)���>��=�;��I���p�F�G�:�����<}����4���h�H������������>�����i(���B��m��(O m��9n�����w�<�����K/�����d9��3�����*���7�����WM�65�������G��j����b��7��y�����}
fe��|�!c�#B�uh"C.�"�[r@�#h������z'
k���r�!f�ZV��o_�hyt����.����#hX�����(��x���V1�w�O���O=imo5{�;��]����'h��R���I�dN�dj���g�z|��6����_;8S�>���v�M����_5l�#�#�}��~��]}��6��^�`|���������G}$)))�c��5k���/�C�&hc<TFF�\{��r�M7Y[���%�'W>��Zzq��lY�uTx���z���:���H��zwmu�	R?)�����
�/��6:1�W5���z%��n���t���l�A���I��P���.Z8!"2F���{�47�%�n�w���ax��;t�&�m]�'y�N}����>.��V���v`��K���:
��p�FO�����7}�,�����]|�PyO��7A���1"�C���x�7n��Vs���0a���`E��/��r��;e����j��h���hFXX���+�z����*W�xr�$:*B���^F�e���B��W<h�_=��e�k)���C��K.!d!OO|�	�`��}�;��}����H�0�~�
.�6�>��*�P�_u]�
�����gW�}t���n/���c�^|�`z���>7*[��A��
U��|�pkC�*5=G��q�,_�+?d.Q���P_"��%�mFDT�8b�$:��w9��_�df�e�o���%E�y���g%�����Z�b��&�y�fi�����/*��/TZG��*�����a���zI�Y�O��L�9��ZG��h;���]���k��5`,4�'&�n�ZP!A�i��p�0k�o����}��m���i�k����$s����mA���c����^i�W���&��c��Q+q���
��hSy�<���~��R�n]kK�KKK���G����kmA��c	*���5����	����Hdd�8�%�Nc	�`g)�T������|n��u��'&7_���|O���he��Q���j������������O�6���X6�LGD^��}�vD�KD���v{L�a{�&���WM��������;I�����=T��z�����������i�Nyb�IrF�C����N���J����z�c�@��A���KM�(55�����h9����C�c��������_-7n4�w�q��sg.��4h�!���Y��Yc��t����S6lh�������JE 8���e���	���l��:u�$Gq�����
_�g�37�����wLb��M�~(��L����$;m�8�R���G,O�<)�f�m�|A�9��3��?d���
P�����/�1�������	�
q�Y�n�l����o�'��r\���iR?Q�#4b&�9nY��N�mn���������$��������,^�]����o�R71F:�m`*����[�T'��:�'g�n'G���6@5Y�z�|��w����hP�G�&@S�	Q
���{&�A�SO=�h��kU
��a=��������[RRR�� p��=>-<�S�|���{m����|���2����nB��GFKl������T
�83������'�4����!1����[m�|�i��MA*� ����L�=1+�'��6��������������Z="W�.���v����Pi�����j
�l��.�0_�x��>���!��� d�������w�^*i�.�x�����z����?+.������7��������(@U���>�@��]k���	��]��0�Vi)�V��9s�	�h���SN1DJj������g����HZ	�_�~&�S�U
�&��A���_��i�����U
����L����"h�v���|�f���������X�y�fi���Y�A��%������wz_7�L�����d��]�e�$k�|�wg���}&���J��#��<IN=��uk>���>�[^����I�1�Cub��3:�:y?@Q@��:���s���~3�1��AC6:�R�	��M�6�7�|c*�$%%�����n&w ���A�����r��{��*�:�����#������
��x�&���]{��!��X�y����_�Y�Y�C<�y������a�v�/d�4Dc��T��V�q��N�h���N�$��{���k&���e���!S��Cv�fY{@Q����ET�^��We�l����(��v���5�N8��x4d��CB6�^\L��lV�����y���.y���9������#ng�w�0Se�e���z��.h�e����jkMd��)�s�Nk
����(m5I�@My�7Y�~�)9��2g�9Dn����h\��SY�ar�)����F)q��RV��m���v@[7egg�"�K�.&�Ui�R_�}��t:�W���?S+������*s
��k��T�)i?�������������c3��riO(q�d��P�	��/:��&x&��sQ�n���L8'>6Jzvn�?
D���u�JMM5W�j�yu�������jB8���uT`)~~a��j�m_z�<:�G���%��<<�_{|���
Mi��J���?�
[�I��-���ON9��	������d���e�����E����8���V�@���{����_�x�x{�p��$��r�d��u���n���5���7�J{�����%�����-[���j�?@m���uT��n�*���/�V���M�65��������~\?�#������������)se������~v����:��I���H��P�	���sL�(
�h����9�o{�}`/�'@��c'ZG@����
���������g���?��Lh{�����a�f��-k�<���>��*��8�6���(���l��s����	�Uv�[��#u�����K��x3S�R�i�>��7�q{��8�r�����[K��Jz���O�_)?��QN��J�;���CT��_�����f�e��e��_��Re�z*�������~�'6uT��w4l���2��Z�1"�C��d����<���oQr����
7�`��N�?��c��������o�� X��K����V�������~�c�0�������u(?h��qz��~�����Z	���H���/�_ h��������P��<������Q�Fr�!�Xk��'�����oT+�6���(�m5k�
I�t��5J�_�Q��so���:��}/�-�9������k������zxi� � PS�>�uOZ�$'�z��D�m.�GN�z�{T���-BGumjA��Q�6%����'c������9R�qkM����1c�Xk5G�	&XkVm�Z��#3�^*�~�Xv����������
��)L����cSza9�`>��}m�|�)kb�:9�Nq8�Pum'Q���L�|�����_(&"\��������5&*B����WO�=e�vQ�}��j9�M�x_���������zJr��BIY��j����V�Z�:
��cD���M���/��s������o�G}�Zy��G������j�m��f7�6|�mf/��f-6O������!�V ��A���H��R�[������� �6�nm'Q�em*��U|��B�Zt�#6�m#�<mJ6y�d���;�n��f]������Hm��{����w���2n�8k�A�Tf����J�z���:�)y.y������7��zu�{P��@�>h��m�T�������	��Z�	,�D�/���pz�s�F?�r���Q��:
�"8FDy��l��%����JTT�Y����8�3lK�.5V�>5A�v��Q�:�(k�A�����r��se����ZMXx�8b�xG��5(�uG�s����J�\W�i�!��m/��2�@m!h�����o�]����r�%��]�|6�6��o��������;k	�����=y�h�����m��J�� �A������Bj�me�S|���{m��^<����������	����p���'�qI��M��Q�E�+Sr�w�+']��&��������5�v�#h���
��@p#h��6�"-#�B����pY�a��{v����W"##�� 1u�H�^��W�������E<��E;��>��mW���l��G��':"��@�Z����j	Anm�@�@eh��������6��Q��E���e��l�����r{$<�!1�
$*6I��r�����\�d�m7m���#�Y�D�������������2�w;i�4��
UC��W<hCo$��
��_���_�#o�����w��~�W\�\�8Z�&<2�\dY:�ma���5��k����>Y\��(>fx���,�%�w��@� h��Q�	P@�3�*�-�*��~|��Q���:
�����~v^�~��\�Rv��)��|��u���W��1�\��O��������Q*C[G��m���G�[G�M�����T��Q���A�t=���<����������rf�v��y�����QP�����*h���wU��Gm-�+i��p�QG��@u!h���M��ge�������^{Mf��%k���;v���~u:����s��7�3�<c�!X�P�2rL��<���j�n��<Y�y�DFF�#:Ab�4�Ph)�1�9��o��=n��v�Y}��mW���l��G������=1����:�6P>������H�F_����S�J�O<�l�I���^>|�:�	,:9]_N���g��|��w�BA���C6�7o6��K/�d*�T��#G���?n�!X��+iNy���d���NB��5'\���#6Y��#�;�a����2%'}��r���iR?A����zx��������u���
2n��������	z��^-T�6�������@��Q�6U������~�Q��\u��u��7��I��5+�6|������)sd��T�rDHxx�D�$��;��������#[����qey_����g�=DF:��IP;�@��m*���F���z�!��?�����O����J.���J���;Oz��i~&�������s�����qz$7�%��=����;�[c�����3��l��\9��Fr���x���M��MMj���[�l)r�I�f�Lk���?_KS����6����P��(m*o��ur�m��|P�i���	��Uc;v4UI���_d��@�����5W�"��s��6|)3�%�~�D>��R���Sbc"%�{���>J��.���)�z���.G�od�
�G���he���Qo���~���^zi���3fTzB���o��N�T�6���((�cD���M���;Wn�������zrr��7���@�P[���^>��B���)�R����3'a�02>&JZ4�#�����j��=���
jA(�_m�`F�&�p�q�������S����C�k��g�}�����"h�6���)�-�bB7�~�J"#��q����rD�F��u�5�
A(_��
%[P�v��Y�Ux�-�R��89��6��S3�**77O�����'���m��6P���������f��R���UG�z��.�aa����rs%;��#A(��q�JM-[�,u��
�2�J[�4k��,��6�U���R�~}��m�6��}�Y�7�j�.t��M,X��e����v�ZY�j��Ed���R������w�-����P}rrrd��
�����'��7���x�V@��6m��1B���g����#'N�A��u_�>}�>��oHLL�'�|Rl������*��r����Of���.���^�:p t�e��]f������VJ��d��6��9+����J�#RmUO���di��|0���Nz���7�4U&���d����s�B�w�..��<h�������e���&\�����y�fk��s:��p8�5���@p#hS5?����I�����	��+�,��*��
��F�@ !h�+�	��-�������I�9s��p��-[�%d�������3�0��w��W_}�����$ �6��r��~��������1c�H��m���y���k���3g��u�����r*+�ZGm��M�v�*7n����W�^�*��>��R9Z"���_7����B���r��W[K�{����%����jp���Q��}�����o��F�L�"k��5��F�o����ukS�^?WW�~.?����_�~�+ZG�m��Hh�+�:*��6����\t�E�Z�K.�DZ�l)��{����/U���A���I�1"�C����z�-y��'�c�c������[�V�o�Q�y�k
���
��F�@ !h�+�	��Q��}����={��I����6wNC��_��X�f����o��w��G}dB6e��*+66�Z����|���E�|����A���^E[\MT�r:��@�\���W3Q����Q���(�cD���Q�7k�,y��k�srVV�\p�r���[[�h���:
@ �u��x����|���g7��N��8�Q�6��!����2�;�*��T[G�Y<��P��$m�|��6�:�m����H���kt[��-K�F0���7��55���UpA�6m��k�,Y"��w���������f��R����w�o��&�-������e����O���k����?��?���+WR�r4�P��x�7n��&����J�.]�5��:
���PS<���}���M����\�;��C���d:oa_��q�F��?������c���I�7�9���������d�=���Qj��ZG@��u���C�I�&���	, X������tR����?���	�he���!�m�����>*��v�6L{�1�9s���7����s���Y�d��	f�N^�~�)nA/��6
4���Z����w�����b$--�����e�a�C6Z	�����~��{��
�e� �k��&�\s�����V�S����]{����K/�e-�z��7����.|����
�u7ZGU���-�V�^mBZ�^�j���������w��x������t�R5j�|��gf=66VZ�l))))������~��fN�4��o�|P�}�Y��J�c��sg�����w�0��M�L��5k�����t��Q�F����g�]��5ZG�m��Hh�+�:*��6�����en�x�	3�`_���O��m��U��o(z�NYW���
7�6Uc�2����$��%���M���p��N����rn��Fy��g�����_C�)�,��K=z��k�N\.��V}|5���E�R/R���6����km�w��g��1c�G�����L�����o��W^yE�l�R������p��t���
��F�@ !h����I'�d^���^'�>�����Mu�����A ���3�Z��^���<;d�a���'��R�5�m���EZ�
}�z��A}N�������C�+�z�-S�F�l0����jm)J�>ZG[M���`�n�
m�6�6	A(_��M��l���o��������Z���	�@�V�JM}��-u�
Ch+�q����?lB6���U�"Nm��S�<�V��n�����?/��I���:� dS��^����.��"kK�E������HP��k��:�O[
M�:�,��[TJJ�w�qr�)����^*_|q���?�+�����e��%����O;v�������e��������n�S�!���Oz�V�����-�V�X!����@���Q�]v�)aV�<�)��W����Q���+'K����cD���Q��am��-����V=����g����S's_4hP�y!��Z	&�>�/]�����j3J�B�&M�=z������������_CM�>�����Oy��Ge������Y�fr���K��]�=|��Qj��ZG@����
���/J��dB����5�z�	,�D@q#�<m*o��yr�-����JJJ��c���3�}��g2d�3��N8�y�������������LU!��������a��zEdgg�*�:���iS�e*�A���
�@B��W<hp���E��!�Y�|�������������� _Y�r�	��a��>�H�f��=�vm��h�"s��p��"A��7��9�.]��jB"&&�����S��`Km�l�i�@p��s�i�m;���LU�E�����|��F���}���=Z���5j���s�m��e���s���NY��lZ�FC:6m���];k�?��P�����;vX�:�P�����j
�A���*�T$$�RSS��Y����u���;��o�Q{�1�:u����;2g�34����jZ?��=�����+��v[kbZ-������5k�	���
��z��2r�H�2��o�1a)@>��oaH�s@e�+�����[7Y�`�.�999������	����K|<=y Xh�@�������kE��'�4�5}�t>|x�����(O>����a���C���VS�����^j��6o�lB5����Yo����W8�P��pz��'���.���s��+O?��Y����q���{�5-�4�3e���u���$�2���r�5���V����-�C�]�v��'�t�E�����=_������+����K�#B���'O��'��'z_����������1c�9^�p��W^)�����$���]}�{���p�Bk��
�3m ������_n��&�p8�����-��b��~��G�8���o���h �o��mN��$��u���5k����4!���}}���6�\����I���������'�~��}����O?��U�]]�t��o�Y���*kk�(���Tll��'b�&}�q�<��/���7��(G��i^W�
�&�R��C����c'=����KDD������g�)���m�-�s��y�tA��f��������{=����U�o(��~;�Q��@p#hSy:Y�������_~����������7�x�<������~���W\!'�|��X�+�ddd����M+'}.h�)���4ibS=ib�����4S�����2�t���K�.-����r�^��m��K��6m�$���������2�)����s���g�]���R������w����[�}�q�re��m��G��.-����K�I1m�=V��e�|���&h�A�>}��c@�
��^p��W�*h����3�<c�������~��*�RG�v�Anm�f���r�=���z����N7�|O(=z�i�ek���	$�p�
���dm�_vv�L�<�T��6S����x�q�����*�Z���3�v�]w����^iHE5h�@F�%����>�����L�������3���'����7�|SZ�li�����6:��������?���m=N"h �����?���������-���
�Anm�f���r��7�����SN1�G+��"
�|�����'��0���.���yR:�PVI��<�����#��������^s�}�Yk"����y�~��W���h�FP�)��m�LPG<���*r���T����A�����/�9.��^�Q&��W~��fue��]�aJ��5�����;��+=����@�(~�n��g�����h�f��a�E���Au#h���M�i����������2�����:9����E��^US^@�i���[���l�7U��v������������u�Y�y�'N�V����_|a�m��53���B6�c�-��*3g�4���S�N���	'�`��;��6����K��
M�-N�i����t��y+����K�#B���'O��'M$���k�	�A��V�^-3f�0�Kz�Z��j����+
�R�m�o�.]�v-1d��G��@&J�o(��	P]����4�U����������"�6��A
��VV�V����7�ZU�_�~��M3����+C?�{���~ ��-���*c��1��MCK&L���{����6�]v�����Z�����o��K/����)
�<���b9_+�IHH0��|= t�������r��s%�)���'O�~�4La>����9
vk�O;h3p������ �h��/��Z�w��+z��~x�����e?/��6���IK��@���!hSyo����;�1����/�n�>���p�$�c{����uj���<��cr��g���ZGi	nmu�M7Y[��l�25j�|���f]O�hE�{�����A������sV�}������i��Um���6������/2��!� y����g������d�:�:�����=[��}�)+�VY����R�HJJ*�vK�'�7o����P�6R�����9��A���[[k"n���x6������\)��n��2�GMT�r:��R��J6v5n�M�Dw���Z��!�}�4�s��GZk����d��-��!��6Z:��c�9�Z@ ������n����0`�t�������x�F[�(���;�(���k���v�A������O������)Sjl����r���[���m��]����?���9S���k��i�uk�������[�5�:u����m�1�
�����Z������&��6m����D�8����
�����
�HM
������o������7Nn��f2d��u�]��/�X�V��+$''�Z+�UTJJ�4k��Z��o�~���h�g��m�Z>���`pA��m��k�.]Z��-T�>}��:��Fi�[ZZ�����h�"z����d������e�Y�z����q��r���Zk�4��Uml�V��������<��<.�K222�5���D9���%22��@p��J�=
>���~�l����w��:�
T������&M�Xk�4lS8�R{�l��%��7��e���c�=v�
�Z5�w��fYo��O�g���k�V����9sL{+[�.]��SO���������7�#FXK@�����
�5�w�QG�q�g����?�~�m�\Z�����={�����e���a
��r�)f�0
�0@���[�s�w=���f�4����:u��[���wi;�����e�I�m6l(���g�������5���w���{+4���g�4���i}��?���?�(�����r�-��GY?��i����O7_�X�����y��R+��v},��W$����N:�$i����V��z�1*��}������oZk�����I�&��y��6�]:w��g��7�"��>]h���\w�u������5k��I�������Z�6::Zbbb�v��x<3�g_��W�EEE�e@����?��/������s��v�y��f����_|Q�o�
&G}�Y�g�y�f������������+��|����RyC��0�^��m����+�������7�>�rH@�3���N�S���om��i�y|48���
���m?���<��C��������n���K��{����kKQ�\��������v~��W3��?K�������s���c��W^)x�����Z���.k�������e�����]8D5���'����9�����p��'��F�c����I�T��%��a>�>��P�U\����^�����u���_���k�.y������\�����m��L�T���<�������j�NzU�>��9��ys3)��M3-��"�XL�8Q
d�}A[
>��o�`��O>)�6��N�i���O>1U��9��2r�Hy�������s	c����_�������/�\?�psq��bV�\i*�o���q�Bt����(m�L��n��v������:t� �s�t�������aBL�)L�e�<�������������!
-ED�5�P��.�|<g����\�vD�����S��,
S����i��U��add�	�8P���g�����,\��Z��Q:!�!��n�����Kz����[��F�2����~31Wx��Pi�������?������/�{t��iIt{I����W�;J����G0��9s�X{�OK���=:J�v������o_k���	���G��7X�R�����}�����?����o���j����
@�dff�/�`�f�2Mu�lTvv���1h�������+���4L������7�,��z��w�}��o��0`�<��c��l�Q�;�<F��&z5�M�p�|����OI!�]���?�A�m������M�w�y��j�������..))�Z�_i�
U�������o���*G�5@dU����{�9k�teE����4��������m����s_M�6����r��w����=Z�H+?ku#��Uo��v��g�K/�$7�x��i��\�\
�h�F�V�9��@��7�@���GY�L����V�����j6�~������ ��-�O<�D���;L��;��Sz��Up��Vz����Leb�n|�u�� M��
��J+�����V{9��3��-;l��w�����3��C=$�^{�	u�����������V��j7O<��t�����/��y�������	����������.�]�os����
7O?��iQv�G����\x����mM�s�9�	������4������g�iZ�k�CCZ-EC7���f?��D��H[i��)S��=��c����Jzz�i5�u�V���@
��r����/�(�'O6AO=��L�4�T��J4���3��Te��V��p���������n}���?��
~sj�`pA�0�I��:IP^I\��_Z�����m�~����*�w���Dz��m�3m��5��hN:�$��a��������U�V���st_�l�2S�%+++�B ����c�=�T�����ICJ5AC7��b�.mGU�~}����� 4\�F?���h�����P����S$h��l��M�p�A$m������6+V������O�B��~��w���o�m��"��6.��m����|�����J�@���b-�W���NI���
�������;����7�U������w^�`�.�:a�W�����+��7o.������@7m�41b��������SG&N�(�
2��0}�t>|x����~���������z��G��;�0���h��	��kW�^���2d�Y�����'�|�Y/���_�k���,�~�����O��Rn:��k�.34���� �7���k�YN�G>��B�{n�D;"��V����O��)��?z��j�*y��7%22R���d���R�^=k@���e����mJ��?���
���~��j7�;w6���-�?���,��u��r�G��_�U���g�*+h�6Z*�@�j���o3t��O?�I�&����*�����I��5���f�&99Y4h`�u�F�N�F��m���Y��}�����f��:h�a
�h��������s.<�j�+��=��6>���d-�������Z������\���k�d��x��P9�����1cL8F+�L�2���W_}%_����a����W��o/;v4�K�.5C�t!T��6j����z�jk���B;* �6:�r��7�3�<C8 ��l�R��kg���^�-���������M��u�5�u����U���|v@g��5�h�"��
�������9R��_o�U���[K�=��s��a������U�Vf����K���;e�������3R�^=��V�\),���i@g������������J�����K�,^��L���$7�|��r�-f>��%_��}n���;�<����uk���X���{�������'�I�F��]%!!�,k8��7�����^�q�)\��E�Um�}_y���}�l��U~��7������w�5��6m�H��5��	�����*<��!��zH&M�$'N4c��	r���������m�����?_�4ib����d�����aC��9��S������D\.���7O��k�6`�s!���
��~��fh%
������g��C;v4�EUpA��-�4d��'��z�����{�=��i�YV�����={����!C��b�7��gA)b�Frr��H|��[[�i����Hk-�V����K�i��fG�4;v��W_}�T�����%33����k%��N;M4h�������"�7.1d�z��U�����~��D�V�����G���9�������M�V����6F������$%%�,��y��'�y����g.��2S�X1��"��6���v��gJnn��VT�f�L�n��
J�ti����dc���7��~���;l���������^(��{��i����?��V���:u�Tb���������T>�
��r�pA���kI��C)�*$�L�m���Z��V�)���?�������<u��1U��y�1b����4�j��^����_���Jsa���_n��|�I�������)k	E����'�6@��U�$J�u�&,�E��=�=�Y~�����;�,u�D�zO�2�T����V�\Y$�SX�}O9����OK�����SYI�c:��������[�n��i&�o�>��a��;h� ����O�����
���&l2x�`�
6o�l*��!dZt�e��]f��{BB�iA�\_q�<���r�ss%�)���'O�~�4La>�z|�r���;dee������u���G}d������y��g���ba�����U��p8�2����{wY�p���A��orzE��Q�J}�{��������ki�N�4In����}�N'/��Vm �������P���O�4��x�bs����^�9�`���222d������3i'���(k�����>z�����:J����[���������[n���iJ+_���o�>�v�M�6I��M�rq:���amPS�@p#h��6jA�������K���^�����
����k���v����P����������A��V��x�&���~��ov�f�*�G`�����|s��-�MAC8���c���Vn���5h�C�z��^�~�2�����P���|����wA�f��5&lS�R�������D����
��Vx��w��������!;;�������C�^����+(O�����q��h��Z�
�������?^������)�i����������bm���W_�)��b��4j���M,���Q�hU2�wy��w��um���VJ>��s�-V��P�h�?=^X�|���1�T-��m���|��;����H�(���
����������/��H�f�������������5���/�e���C�8���L�������!Cd��u�vu�UW�K/���)�AnmJv����}��g���a�������5+�6jA��������eB�������-[��u�6l��6�:JM�0A6lh��	�L�2Ey���@�M�-���g��Q�F���ce���r���	��+����e@��S�u���4z>��** �$Z����*�Qqqq��t���;�Z��%����/���T)��#���N:�HUT�Vx���������lZ�����/(���h�Dr�j4Z&V+��������%''�| �{�&h�jG�(n��*�^��%�K�@�����e��r���Z[�h���:
�%��j��	��@�(%*�:*��6���~���z��:���M����������e����|�����:H���::t���^m ����~�������oB�"h����_(m<O�����>����f9�P��A��lUX�^�d��I���/�	'�P�D��i�����6l��}��r�Yg��7�(���7?��
@���@!;���{�nIOO��T?
�h�����r�8�R>hc�$��l���~���
���'���S�x��g��n���;�����4��4�g��	�h����T�����z�n�u+��	�(
�4l��Z�����4����c*�h�;tS]���V���6J+���
B6h�����)�����?�\f��!O?�����������a�s�@�h�&::Z���%<<�4�`����m�z|�!
�h�F��s8��EFF�m
����������/������n����4h��|��f\u�U2l�0=z�L�0A�,Yb&��/""B����N�:&l������cr�U�M�5����
�
�
����?��o�����������O>)��'�_�e*�hi{�����*7#G���C���Y��[�G������`4xS8l��A�����vTvP��>�d���bB6�
�@��WB����[7Y�`�.���n��/�����WL�P]t�P'�����7���x�V@��6m��1�L>+�p�0�Vk�������������Dl<x�Y�M;v��x���R:A_�	�v����q���3�4�+P�{��k��<KHH���3��g�.�|<g����\�vD�����S��,
S���/,_��������qB�&MLx$�h�F�=iE;$�!=��C8eq�\���3����n��7���nu����-[�����s�@�
�:����{wY�p���gA�IN�4���o��=m ��)]FF�<�����C�8��sB:y�����z���]���u����>*�_�����
��F����Q%�m���p���h�F�����
B6�xi�F���-d��(I���_���_�<���~Xd_Ob�l�R ����?�q���>�����U�Vfz2@O�����h�"�'�
�h8�p����Ma��l�]T��l���*��2@p������JV�^mm�w�g���/����;�T��1f�y��W�������;N���N�������e�75l�_�F+J�h����w�lt;d��8� ��+�y%���Q7�xc�{m�7�)S�PN���Q�hU2
��r�-��?�u���*6�J���3��srIW����?�����_���_�����M��m����t���Qj�������:�0
���Z�cp���m��X]�gi�F����l4��!�P��:
@I������
��@p#hS�y������j�G�v���3�<c3�ll���g�5o�d���������G�G�@m#h��P�(
�h%me��g=o�Un����k�D+����}�F�P��A�j���c���u��&��U+9����rY!�����s�	������D��j�B��4L��[���+�����T�*��
j�^kW�Q���6�W�j{�^y��_Yk*C��i�F+�(
�;�N��eB6P:�6�1Zv=;;�Z�W�^���'�
�M��o���ZPYz���v���]qR��m�l�d�Y�l�|��Wf<�����/���K��+��"""��|��Z����Hi��������p����������~��rrr
�7���u�F6/����5J���:3�R0,#F��q���_|A� q-P�4H�k�.���4a
�h�]��HOO���w�vR�m�(�
�|���&`s�
7���~*���7���7�����Mg����w�^�V@av�F+��m]�MTrr�DGGT�!l%�����l48�����������{��%K�X[J+��h��^�zR�NIII1a�*��gdd�Pa���m4$s�����u��-E�������[[����m��&;w���@h�C6Z�������s�QQQ��Mll�Y���)���C��0h�������k��|�b~�QG���>X0�"g�y������+y��W�5������0��+��JX�DDpb@���M��4v���p��R�����������
x����jv��M,X���������.�H�/_nmS����N�:I��������5�.�����m��Y[E>�`�����q����v����
���4o������i�d���o�>��e�'N�(�
2��0}�t>|x�����(O>��<����GyD���.kM��;�0��c�=&�G���Dn��vy��G�5�(�{��@:�$PBB��#�����P�rs�$��M��<����'p�CP������c�/k����(G��i�,c�?^���u�:$����8\����E���j�/���3f�J.z���I8	F��I+��\��B6���EB6���-=����.�>
��xl�z��-[�����J�
�:����{wY�p���gA��~X���nkM��1c���a��-�7n�	������e�]F�~��
7�6%#h_#h���J~_�E~�c�������s�z�=�y�y{���z��lS'>Z�j�Hb��d��cH�Gd������	�2��vR?9��
��P
�h�F���nw���~NHNN.3dc�
8��@��?::��m
W�	m����6:5a�������Z^z�%�\}�;��s��_~����?^���N����u�B"h���M��mt�����*���7�[e�:m��qz��/��{�����������j-���s�yy&��!��8�i�<Y�� M�'Z[�
��Mzz��dS<d�E�>���i%�2b�m��
W����	m��x���(K�.-�B|�	'T��Y�����������[K�Zv^i�"C�dwa�3t���|���DF�K|�C�b)�Q��5��wG�������g-b%�r������I����!��=�����+����4s !�����K\\��>�Yz���o233B<*�U����CE���;�����5���WK����������r����(k���I�r�
�m �Q��d�+���I'�T���������������t��U���@����SO���:���`EE��r\�9{��q�$�jE�p	���n�����\�S��y��Y�L��i\?����`�h��M�Ei%��c���P��.J�$dS��v�K��\�9���m��m���[GO;����5k�m��A����� P��@p#hS��A{2�2�����6l�L�<�ZC�"h�VA�f�m�������s���w{����C<�"���A�
���E�m�f��i�F?�$%%U)dc��6Z�F����P�����,A%���Q��>�����h���y���������U�ZP�LE�h�p�I�#6����Sp>v���F��@`�kT��MU��i�F+�h��F�@����
��V����@�3's�l��z�GJ�^�#//��{���\#����UX�VNu��5����P����voHII	�j@PQ~�:J�����h���Un��/��}6ZG���_~����WM�6��N;�J��U�V���k��~������2C��s�	�� ��:�d����<��3E�x�
Z���+��k�����`�s/��|g��QQ�����^,��<q;3���!�������H�����++���r%:6)�C����N���-���_���`m����H�|�����M������x�(�6�q������?�Z9�����4oNJ'�f��)����,Z��l�7J6�:u�C9�L"v���������F��d���V�W�(�,����1Ap#h�V(m4d��o�x\9����
%2:��G�K^�Gr2w�3}��!���$�Nh�]������
�I�m��"�����+m��x���Q�Q������i����;w��?^���*�����.�Q�7o�/��R&O�l&7?����sgP��H4��!�F�������
�@�����4!���0�r���mwN�	�T���M�n��h���)Z�&_�sY��g:_���PD�5���o7������[�N�"=���%�����5Gy�}����|�|��\w�u���`:0���-;��%*���y�o�/lS$dc*�Ixd����HDD0����V���0Bs��(O�Q���P���������T��h�(-1}��W����km�[���s�9�
�Ga�'��y��������m��Y{����5k�~A_�u7ZG�A�^h�N(���S)�����WN�w=?]�6R%�l"_OqI���~�{_h���[G��z^���vKzz�Y�u[��QmP#������*+V�0���4h���'���]w�%�G������/��1c
�'�O<�L6�h����{m ���A��B#h��3l&y���	�(�6�3��6�6@�u��<�6l��6~����(�}OS���2*33�LJ������*5d�������M����o��V���cR��<	�������N���_�\�6R!���`�������Mjj����C[�w���-o��4����lz�Pe������]��j67�tS�
�:�H5��^{�42����	�vQ@��X����#�GE;rm�G�^��:J�-H@C�}��'��0�g(�{�1��T���0`�,]���;t��Q�9�y���
��H��#�8B/^\�oz�E��o�Y����wK�F��T�Y�z��n��Z�-ZG@p�u�t���Q���N���*�F*�C6��F�(�=^(�:J�������]Uz�.;;[�m�f�i�V�u�_mj������i�(�$cW���3��l�23�����h�F^�z�
�O}���r��gV����'�(s��-x^iU�K.��H�_!h���
����Au`a���dC�~���M��M��A��o�=n����,��e��F���x���[G!�����EZF�O�~���ReJ���W$P���r@!u���	Li���rYkTFim����H9�6Ra�l�Q��Y��8~���j68p���7m��6]�z��t�Mf��oLZ��p����;�nSu��-���W_}��P
J
�H~�f_~�&/�-NB6A���6��v��z���>���@USB]ff�)A�t�R��p�SO=%����������7f��LE��P���.�@����1a�����A� ���9�����,X�@k��m������T��/�(�]w��&r������m���2r����M�{��/����5,�E]$����x<�����OF�]�Ty6n�h��dddX[D>��c9���}��;''G6l�`*�h�H{}���[����/��6m��1B���g����#'N�A��u_�>}�>��oHLL�'�|�\ �
�{��k�z�VBB�4n���sA@��qyd���2~�|I�uHDT��%5��p��
� I��z����C\9i�6��7����MH�l�����*��[��+��y�L��i\?���-=^X�|���1C����qB�&M��SVVV�E�z.s�����A� tu��].\h��Y��G[F]���l�2�P��Y��M�6�g*�����yN�Um��'c��9����LK�?��|��gr��'�z�F';9`������s�^����m�@��-�6��y_����M~�&�\�GDJt|���d���m�O��4m���
��������	�(m�$�~���m�v
��������{����}�I'�����A��z��b4h'
6���Xkk�"h��6�o�),�{����]�4��
m"�$�N#���'hC�~��
JC�@I�m|�4@Py���
B6J'��l������TG0e���a������`0��`053l��jR^��l�{�kz����PM�[+��wN���mL��mp@�e�
7� K�.5W	�o�^>���"-�l��h�W����[k"�g���}�p%��m���.����LB���W����6	i&}����������J��
��h�m���z$'SC6Z�F�w�i��l�9��""�%:��DF�Xe*���P����
���:
U���#�<����NV�����V�����_|!����r�F�O�3�V�6:��lZ�\'|tP�|}qA�?�|����^��qHt|=��'9�;���s����d���
�A���
���A��7Nx�kM�����d�������+h���_��'�X����l4lS�A����K|��� (��A��B=hSV���d�1m��v�+'��6m�g��4m��x���y?��S���!�Rf��*�n���o���&�RU:���+�&��l�e�lt>�;�#�$:��8�5T�����#9i�����nb� ��A�iE�k��� ��W��y���	tP�@��������]���@>
��M�&���M;l�p���>�6���*l���&�bW�y��G��SO-7�]$0����k�:w�\�gGFFZKU�������ll��6��u�6���*d������Zk�RRR��W^1��K������7O<��]"�7o��S����K/��E���V��a�"��y�222
�A�C=����"*��n#E� P�A�|���U�����d��Ar��W�:(o��F����������/���k����z��>�C�E*����O��S
���nkM$99��Z]�W^�Gr*�����!l����B*���*������$%%�<��'��W��<s��-�)��
��h�F+�8����$x��m��l��H�B\.W��0�-''�Z�O��}�����o��*f��E*��P��*��*��i����l�D���`����
�@������{�*2�5�+���`].��j�������H0��~������%[�l)R�������(k
���y�OC/9���Z��ll��m\�����|���B���/��M�����x�����n���H�'�t��Q^{���}t��s����k���9�H0���_��[�Zke{���e��m�������*���QP3wC/
�y�<)�I���TY��GFIt�F��I��{��}:�n~����>Y�A��
j\FFF��2��*;;�Z+[��=��tZk"������+�Z�~��gy��'������Z<���vyy����~K�k`�	�DF��pM��kT\�D����?�����;��������Ot�DF'Ht\���Gxo���Q��{���Z�I���
�Z����������M�2E��y��[�����_~1��������=66Vn��F�F@M0�W�$7�#��=���K�A0r2vz��^	��6�g��j��{��i�0�l��L�>��}rd��������6 ����{����G�������e��!����T��-�U�V��y�d��2p�@����� c���C=�����0��\��d������3H���&,R��kG~�G���%�����n�Xdm@�#h����,&L��M�Z[�M�8Q���1b��~��f��K/����[{��l���.3��/�\,�Qt���6={��{��W`m����.~���^�������}���@��	Gt�w$0�F��;���$N����[�n�`�]���O>)#G�����k��)&&��Rq{���;��C��['��o�E�Y���[��4k�L�<�H9��#d����-�/''G6l� ������'��7���x�V@��6m����o�>�^�NS�u��Af��O�.��/�������u ����]���
�z1V���%""��@u�qyd���2~�|I�uH�#Fb�6��p��VBPz**W\�i��o�xrEZ7O��w�"��'����/h��3fHTT�9Nh���8���P���%[�l1�z.s�����A� tu��].\h�Q�>��S'3�x��W�W\!��sN�'6���/� ����L�<Y���j�su\~��r�=��SO=%o���_�l $��IXx��ExG����}.xG�G������_^~�ey��W���^3�������q�'/�����:4|3j�(9����������a0�C���@�#hTA��@���6@�*��
Pm�
 hTA��@���6@�����!>���i�G�n��\.F��������P:����B�n�d���������
�t^^�4o�\����[�n��i2b���o�Y�S��L�8Q
d�}a���2|����!11Q�|�I<x�YB��������29!!A7n,��S��#3g/��S�KB�C"�b%.����;��������'��T�L�"�\����e��H��	��o������e��e�����j����4�1112p�@i���Y��w�..���h ������&����!W�C�@H�p��
��UL���8Txxx�(i�P�<�������:
�ZG@p�u�h���P!y������u����y��1BvvvHW1�sX�w�6��mRRR$22��5���-�CN:�$�xm�[G�|��
7�6� h�ATA�=O����Mjjj�m�����/���/�����$))�����P�n]�����n�6�6@-!h���
���E��G��g�V���_�T�����k���m��m�����!e�y<k	P�6@�*��
Pm�
 hTA����f����%�AP=��$/�-y��������z���<��$lm@���%a���g�n�I��;��!N�WwN��)BU�������HNV��d�e0�Y{�����$!kAP9y"��y��������#nO^�}L�$,,�=��J�7�F�8��>W\��J�>y�����L�n�d���������
��r%//O�7o.������@7m�41b��������SG&N�(�
2��0}�t>|x�����(O>��<���@�^v��e��(IHH���KDD������������\"�������R!-/O$5=[���0�C1Q��a�8��O�GK"���G�C���Mw����j�;�Z�J�|�M��x��������^�z���������5�6�����F��m��xr%=���o/����=�����uG����}�L}�	��lZWn����O���5*d��8&\�s��Q���nP[�@�����
7�6� h�|��2���9&Lrx��2��S$�N�u+��6P��A���T/m�����Z�j��<q�C��
@�!hTA��~L[�����&�[�Nm?&�W��7�xC�}��J�3f�?� 999�O���6l�`��w�yG>��#�7�v�A������r�W�E]T�q������ce����O����,y�����t���y��'�f��n x����M�L���JLL����O��n�:u����K����d�����^m?�g���m��Vy������g�U�O?�$'N��6T�6���LFF��&R�n]9���M��.��B��3��O<��Ug233��w����W�������6�[�r���������+|����1C�}��
��?�X��������\�2J����8�6���s��$ck�����W��A��?�\�~�iZFBA�O�\.��e���/>>�|�e������/� �V�2��C���c�%m?�t�R���_�5�V�Z��Gm�������S�g�6������y��f�PB��SN�S����51-��4ib������8qbA����>[F�i�B
A�Oi��E�Yk")))>
�l���H��6m������C�E@�
�6����}{A%u���K���M������?�,�|�����{2s�L���/��m��������r�����W^�o����GFF����+}���}���m��6��*�Q����z�jy��'��n��/�\�9������.��O?�l;v�	�dee�����3_����+��w�����um5|�p����k�j�~h��-�t�RkM�A��b�
9���������~�I����� ��@�n�:u�6L&M�$;v�����
�h��Y�f���6��2���������u@����D��C����e��Ykb3�|��,Y�D"""�n����I9���LK���kO1����;��{��Gv��]������>}���=��n��>k�+��`�������y��z����%s����`0�e>N*L��_�Gm��<����[7Y�`�.�999�a�����`������@7m�41b��������SG&N�(�
2����m�c�
����������:��j�"�6m���s8��M��=%99YZ�li�7�-��>���st�����90~�x�_����t/���\����x�����/&L(R�F+��9R>��s�^�^=y��e���f�6����]���pQTT�$$$��j���8]���u����K�#\��H�n�)�R��c8�?��I�z��������5�g��������+��w��Zkm�!h���
���o�m�?���f]O|�m�V.��b�i����^��3���6���imS�F�0e��Q7�|�|���f][F=��3r�)���=��%h����u���@M���+W�.�&/|�J����I����0���}=��5���J��t�����)::Z���c>�����"��\HR]�m���C��I��h�Fi��7�xC���~�)���%�\b�6�;w6�v�D�3s��1�%��Q���J��Qz5��2*��*��3�O��6�V�>���V����}��Fm��NE��h���6�N�w���������+?���� s�UW���
�M*��ny�����;������~���^*o����WQ������2����3���-�l�T�F��h���Y+�|�p�Lz��:�u�:2f�Q�09��Q��;iUC
[k�;&&FN=�TIJJ*5�
�M_�t��Wh��6���*��m��IF�e������g8���0m3�!��M�N������^�z��;%h����4h��T��d����;9N�|2o�<������-S����I�D�xr�=�?������M�R=^�����/���d��	����+_"h��6����he�q�������-"G}�y^w�q����L=z�<��3f]+�<���f[i��@
�$$$H���Zp�?{� IU�
�v�(�dP���E0���1�����E�����	"fQ8�)PQ� &���"���J�a��m�^m�3S=;3�3��i�S=3]�=U�^����&�]s�)�<������Y���ty��V+G��ie�U��'�����w���u��_��_�����6�K�(���7���6�m��Vn�����<_��W��?���Z)���{y��^T}�/d��� LF���
�Pk��Fy�C�[+���k���+zk�\|������|���;���z}�A���_�Z���.��1	���=��3��
�P�>*K-��: ���9�����\��Zk�[n��|��_/���7�.'�|r����]����T��2;�h�{��~��O,g�qF5}�$�60D����|����o,��zk����\r�j��s�=������u9������X�6�������o_��c����<����g>�����/�����N����'?Y����W?���>�����,^xa�}�)m`�$�r�e������)�sL9����q�W���?�~���n����������r��W\��z���.�f����v����~��|��3��
�SN9���/({��wy���]-]tQ���d��_���h8�M7���&��L�4Yn�������@�����o^6�l��Z)�\sM��w��[�&!�SO=��6��9s�C����<��IOzR5�������K��z�S�RV_}��yb���e�-����������e��w^�g`&��!���<�l��&��y~���4��%�\R.����Z)���n�����z����<���7:/'�xb9���v�m�{�y������|��_�~����v9��#�t�3%+A2	�<������s�9���������{������+7�|s��R��f��������
7�P�������i�n�������Xb��W0�	����5kV�e�]�c��j=a�[�����U�������^N;��������~���C.{������[�k�?AB[n�e�g�}����Z�c�����������;��w�y�O�S���+�o~��r�Yg���:���U�*?�������-�����xEy���T=�f`�m`eT�m?����*������{9��C�����r����������}�y������_~���U?W�l���wT?��2�T��'hCl���*������[�\������������|��_,?���m�����<k��V�y�;������
���{�Y>�������ee�-�(���NYj��z��?k��F�f�m�+_�����}�p������{�w��[+U�g����5��m`�%T���<�|���-'�pBy���W��w�����VSH���nU��?��?�W���r�1���w�}���Zy������j��l�9�yN�p�
{���K���%�X�<�Q��F���G?Z���o�o~�����O��5���OY������������z���>��r�)����e�]&}���	���@�����SE�:��m�A�@�:��m�A�@�:��m�A���K,�����{��#h0
�u�]e����^��w�}w��������,3U�����m����s�9��w�qG�����=��S���������VX��]��c�9�������o��W^y�r�QG�=���Z�
�w\�o������VZ�q�e�����aq�������Zr7��+�X�\s���RK�~`r�y���[g\Z���e�YK�G<l�r�[�VV_U{00qR����k�e�]V�\r�c+����k�-\pA�<�g�.�y�c�>�|o<�{K/}_���GT�E���9s�����[��)#h0�	��p�5A`*����SO-?��O�P��H�f��Y������6�
�D~7���v��<�q��=
0}��6���F(N�8!���0K����)SF%x����.yMYf��a�t�FbY}���C���[�_���z��UW]��jfSG�1u��f�(��5SGS���n��<�&A���_��0��:
`H�>��a[�l��L�:��m�A�@�:��m�A�@�:��m�A�	��K������:���
�}n�sn�������y���s�5����m^���{K���w�s��l�[�V����Z�}U�R��f�r�9��K`�q�����*��sO���{����[VXa��w���9�������o��Z_y���QGU��s�j}*w�qe�����VZi�r�G�����Z��A�^����jYb�%��+�X�\s���RK�~��������/��,����Q^���%P���_S������K-YV_u���'m\V^a�r��U����s����+-[��j������`���3��{���5A�2�63��
A`<�v�-�����|��K�2���H������=O:`�������#����V(o����3v���(���1u�X[b�Rf-�T�=k�*h�0��YK�%�{�L#u�}��w�s�s/��gY���,�'`��k��^�l��*���X�<j��\��d����W/k�j�t�E��Q0EL0��:
���������o)Kfx�!t�}�K h���:v�k05L�����P���C�<x���M�,:�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�`��ZIDAT�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�0a��������7��������'?���������.��_{?�������������m�z������6,�K/���}�����*�{����	OxB�~���e�v(p@y���V�8�������������;����[m��������g?���'>��r�UW�~`�	�0nw�qGzy��^T����O}�S����u���p�	����|y���\������.�����.������}���3�|���,�����������_���7�������/,7�|s���'h��$d��LB/���/{�������u�]�Z�[o������_�by�s�s���
7�P^���}�����<�m�����G�����\�����/}�K��{��=
0>�6��������^�[n�*x��W��y�����p��G>R���'��7�����J��,���/��}���z���������:���z����6�lS��w���:����s�����_?����?������0�����v�ae�%��=R�&�lRo�>����~�
�d��g=�Y��3�(��rJy��X���~c^�&#�\s�5�G�;�����N-!������s�9��#�X`[��vZ55Us;�����;���r������
���>T�\���f��UN<��������:�?��?�~���@@'a�3�<��}���G��?��,�;	�d���|���MozSYu�U{���F��?������
;�����K��������G��Fw���V��4�3�������s{k��2JN�Yf�e�������#I�f�=��B@��N:I�7Ar���W��{���Z_v�e��?���3c�����2{���Z)���A��f������e�-�����j��������q�a �~���M<���/w�qGo���7��SP&�����X h��g?�����]w����.�����F��E���dD�W\����
�zF�T3�3�3�<s�i2mT��Zj��z��-��4G��TTw�uWo
�;A������������=�q��/|aYz��{�����n�}5���:k��g2z��w��[����2}��
0�6d�
6(�}�{�a�V~���������0�����3�����Q��o2�N�)����r����o���@w�6L�����SO] h��.��
�4�|�����J��z�����?��t!h��:�������F�y����7h�P�����[+���i��6��s:��d�{���-��b�X,��2�L���S�<m��6��s���0)�������<��6�j��V�����SB-�����;����r�AU��Y�z�t���+��n���V�)��R����<����{������k���e�Y��]&R��r���r�
<
�xs�1e����?eq�G�:�����{V�S����+��������J+�#�8������:,��r�u�UK��W\�����S���0g��r�����m�"�������j�����f�=������oCGo����Z)�������G�im�����=��c�m"�WMUc/��&���/�|Y}�����T��� h�DjmL���C6���/��l������������9��j�����������B.�Zz,��b�X,��2�L#�0��!��l�?���Z�m2��N;�4p��=���\<��2{���#S�=�MF�����#7y����Sv�gDF�`"�:�)���s���j���
6���x���Q�z��w��6���������,t�������A�H�6i�MO�1`r�S}"FF�J����
I��)��?��
��G�y��VN>��������
+�Pn����Z)_��W����>p�H;hs����7���e��Y�G�F;h����V���A����
������Xl��d���d�5d���J��y~��_V
%k�-���L��	s��wVa�=��c���2�,S��k����|�s�&���/�Yz��{_-�5�!h�������	'�P��z�r���������G}t�d�Mz�v�i�2�om<����45�5N���� m�BK������F�i�C6��Gz�&��51���:��3�,w�}wo����g/����
����Y}��*d	�4G�9��c���s{k����?^�w2�DMA,^m�:d��W����<l�A��?�P!�x��x�i�n����W�d��(8�.�l�+���0.#�l�����N:����/�=2~����k�js�Ygu�>*SD�t�M�u��c�2k���@w�6��;�1ds��'�M7�����;��C����{k�z�o���6�o����3���Yr�%��{�i�(`\m�w���)	���9s��s��\��W��[�UW]U����/0���^��j����a ���?�a���tN�/�|����7�!��`�
�[l�[����`5������{dA����s�������l��l�����2�T���a Gyd�����H1	�$�s�y��s�=w���s����j>g�I'�T�����J��o~Sv�q���O|�z-������^[~����/��������2��>��S��G/��
9��3��a��>�������m�����i�%��jF
���������*���j��y���7�������mo+����^����Ua�v���xE���?^�Zj��������Z���i"��5��U���[y��_]6�l��#��s��\��'?Y.����#�$d��W���`a	��Y+s����M�.��w�������~�~���#����:�����[n����k����������H6a���j��L�s�9��K���_�r5��D�m����*�i���R���+��F�9�����w�Y�Xb�*����?�<�q�+/y�Kz?9<����r�UWU�3�d����e�V�}����c�)���������W^����s�=���p�q�����o�kXi���GQ������\w�u��6�W\��1����G���{noM����
��&h�A������:
:��!������}����������/�������g�����_\���������o���^[n����O��'hC����ty��WM�>�e�m�-���O5�R	���7�)_������v�}����;V�e��������r�A������+�(��~{��`���!�����\~�����[i����K��x�����~�����~ty��^V�����_�����\y�����Dy�[�R��n�r�	'�[n���]��m`�]s�5��M+�@�{�����2�M�5p@���;z����*����^����:e�W�=:O�@���o9��C��{0���K@��;������r���6�����1�yL�e�M7-�l�I�5kV�Y�����.�rH��w�S�Xb����������xF9���G>��j��7��
e��7��7K-�T�s��sO9������L#�L%hC���.+^xao�T�4��?(��sN9���:-����#��F��)��RM�f�V(;��c9����I'�T���7��<�9���~vy�{�S~���VSG������]w�U�<��r�a����E���_���r�
7��J�b�-����?�4P�8�����?������=��y��e��wq[�|�3�Qn��v��#�F�9�����W^�{fAb7�xc��y�����;-�����\r�%��RM�����6����l���[����#����F���F���E]TN?���Z)���^�z��{k'SK5G��c�=����:��9u���}ly�_X}�������`���!u��wWKm�5�,}�C{k##�4C1~���lP}�u���V[�������yf����
fAR��/���������VZ��61n���r�-����*���_wu�=�T�t�60����?/0��V[mU6�|�r�]w�?��OU��K.)���/��W��U���7��9���6���:�����e��w/O}�S�c���w�������{k��5�LG�60�`iJx%#�|��_.{��W��~���c��j�z����?��r��g��s��~sd+��By���_�>������|��_,[l��@A��.�����<�-�\�+�9m`����F��������k�)��/�R^��������6�_}�'�s��{������������[o�}w0K,�D���]t�E�k�����U�f���[����N����|	L��������l��W.�g���`���/Y�����;���Z_f�e�.��R��GW�S��.(��r��a��wx��~����N8�\u�U�zF������j����Z�1&�����*U;C���G�IH&�'�|r���;�X��d8��s�;���r�W�)e�-�,���OY}��{�L����v�m���a��Y����o:+��o�C&��?��������v���[�������b`r��3
ci�J�����[5�03s�1e�����
���QGU��s�j}*w�qe�����VZi�r�G�����ZgzIHf�}�]�1/�\r���;��<�1U�e��6����^N<��r��������.��>�7=��*|5�>����w��j���,x`y���]�Ouh�^����j������T��_���^z�����3gN9��s{k�60emf6A&ZB3{��W�����EV]u���g>�Af�
7�o;�����K���;wn�X �If��}���xy�;�1���=�i����G<����jm���u����09��U�Tim��C(SD%dSO����=�|�K_*�{l��G�i{��__M!��$ps�I'U��M�W>��O����m�l6�|���7����H�oQH�&A��b�X,��b���eQ2�
L#��lF�a"]s�5��/,_|q9�����`y��X�+����v���3�M�����/}i9���{?5>����7�<������tv�}�N�q��G���HX����+�x���J5E�T1u,"�63��
Sa�������7��|�k_�=R��;�\>��O��<�!�G3R�&!������}�{�"o�hm���_~���?x��@Lo�f��}55m`���m6i�8�������{�������/w�a��#��M#!�8�
���y��^W>�����g�]t�A�W\������6�K;h�d�_`Yf�e�&�l�[�g���UgP�i��I�%7e$�a	��d��j�u�)}�C{k����.����zk��p�
��#��?]Tm��7/��G�{��^!�60C�}���R�4OY�J(��/yy���\n������l��f���55Z�f����
��o���v�m��;��=�]�w�R�P]%d������t�I<GB6�*j�]w��`&��!s�5��.���z������l����V.����w����������[+e��Ye�e������|����B6	�d$��>�����/���K�X3��A2_���V[mU��������5��,�]vY�������o���k{k�l���e��9�����l�y���W���/��F�����
��7��
����������������_��������v�m���n:��O#�d��W���p�	e�
7�`�&hC�!yHy�#�[����c��*#�\|����R�^{���[T_��\z��}C6�{���>��jT��`q&hCf�u��F�i���~VN<����������T�����7J��[o�[[�_���r��/�Yz����lf����IX�	���Yv�e����ZM�T���[�I'�T~�����������]t�E��4�j�}f���+�z���f�m�w���}�{�����s�r�QG	�@��
��<�1��/�S6��'?)/|��g?����?�������/����W\Q����V���o}k���v�}����=m��4����S��T���z���������_�z�w].�����?����;���03����6oy�[�{���*lS�4s��W���+;��cy���U9�������l��V���~������C6�������VX�z��O�S���Kzk�$����6�`����[���dD�k�����03���Zj�������*l��L�3��/��y���#�H����V�����3O~>!��?|���~��_U#�L�W\q��3�+]b	�$0s�����>��e�5�,���J���+�\M������?\�������4O����{k+�F����L!hCn�e�)�y�s��'�\�^�i����g�G>���Q�zT�t�M�N;�T8��*8s����W���#NU��3���7��z��Z�|n�a5��$K��T��l��6��s����$���;�UW]U�����n�u�]w��.��c�9�������o��3��QGU��s�j}*w�qe�����VZi�r�G�����Zg����%�H���'m/�]w]��3H�(�ed ��9s�����[3�
�8�k�&��
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
����s����n+��zk�d`�I�G��1������=����;��s�s�~����{��}K��TW��l�M9��s�%�kn��������j�9�����w�]}���>�l���e���+���J�7��w�Q����r�=�T�u�u�-+��B��Lw�sL�����7�X���������*{��g�>�;����~��
+��R9��#��{�]�3}�u�Y����6��HHf�v({��G����
��[n)�������_^������.��,��e��W/O|��FmT��e�]��[�^^�u�]W-y�+��bYs�5�RK-��	�n��9��s���	�0�$L���?��r������Q�zT��7�Q�[o����K�]�mf6A&�
7�P���w�O|��G���Oz������G�K�E�/r��[������������5kV9����;����	��0��ASG1-���{��^y�3�1f�&.������/~�����n�=
0��]$����P��K����9��j���B6���>�����;������G`��aZ���~Tv�m�r��g�)�h0�*wae��I�S���/yu�W�9������T�7���TM�����Yf�e���h�����&����=��k���H0x�������%�j��Vw"!�����e�]w-���w��`��a�e�������q�����}��?�a��_�Z-W_}u5��Zk����y>����o��c6$���)�k���~y���T�>��r��GvZ>���������;��=Kg�qF9���{k����N��_�r���K���,M8��j*.����_��_��+���`&�a���j����K/]N=�������l��V�c����%��7���N��y����������0}d���(��l�M9����{�Y��{�N����[�������[��,���_��|�C�?�M���k_�Z�y�����kk��v��g?[��?����<���/�P�T�6����w�{��hrg���n�[��
7������~p��y
Eg����]�n������O/?��Ozk�<���/p@Y~��{�,(7C���o/�rH��Rn���r�q�����G`f�a���]��}5O��k^S��=��mrV�����LC0]\w�u��K.����l��(��~��7��[�g���+[n�eo����g�W���h9�?�������uo
fA�ZF��<���J�U�6^q�{k��.��?��[~W_}u���+zk�����~om�d*���:��V���������6��<�!e�]w�������.����"h��:��s�!�����S�j��.�����g�)�G�En@j�ed������n(�_~yom���Z�1����_��?��������[��C������(A�%����f.�fX��3�(s�����L�����z��G<���	���#A�[o��Z������A������<�����m�u�-�l�Io�������Q�H�6�W|��w��J�y���K,�[�����Q��f�L-	��t�M��/,'�tR9��c��\9��r��GW�����o��������Kzk��������~to��f;L���`&�ah��'?Y ��'<a�i�b�v����:
������zk�d�.��<�iO+[m�U�c�=��_����~��2��o}k�f�m�k�Q�<���������s��o~�[���4]�w��v`&�ah%d�l�H4�n����6?����bT�zXf��b�X,��b�L��]�����{�������*`�����F�w�����O~r����{���m4��^�b�V(m�Qom����/���kom�i�	����b�X,��b�����yx�W�;]�9��|	Ca���[`��|���Y�f��f��W��S�}���-;������,�������P����V+�.�l��L�\w��=�,��2�G&�1�S���r��7V��=�����{�Y�O���;����~�_�J+�T�8�����{W�L/��MozS9���{����������4�K/�t��]w�Un���*d���|/�����j�|��^�\�G?��������V��[o]���o�}��n>��O�W��U��Rv�m�jZ��W_�����{�����%�A�_VYe��-hdX8�/���O�9s��s�=��&h�Kc��7��[+���~w9��:h��o}������$h��?�i~��n�`���+�.�>g�nr�a�}���,���O�����{�T�Y�_e��<���~p��?���3��L����?�����g?[���������5�������>�|��.�m�]��n�A�]v��:.�Zk��#S#����_??h���E;���A-����&_;hc�(��m��vB�1g�}�"�:*wQ�`OCo$a���b�X,��b�L�R_s�tv�e�-���<�)�����B-n����M<���nT��W�Z^��T��#��y���h�]���:��j<2u���^�[�:9�@�(6�
[�a,��b�X,�ef-��mZ�mN9��j��A��j�hs��WSPe�E����g���k�7t0�r��r��TMeD&R�����V�P�0�������
��7�����cy�[�R�nj���?�����o�=2O{D�g>����8�N���f����^�;��{dje*����}iQ7�01r}����V7WLSG1mLV����*�������6���m&��K/=���0n���r�������wU�� �9�v�i����+��=R�^{�U�h��m�?7G��b��6�i��`�����j�6L+��b���[zk��t�Ie�]w]��M��z����H�6�<�6�L=�����|���=R������}�<��O�=r���n��V>��O���Z��H7���c�����<����=�O;h3Xb�P;s��g������6~�n�muW#�L���V7-��=��_���j������o�[��A������>u�����;a�6����[�<�!���r��W��/���6O�����w?������e"n��a"h���;wn�������5�j0������j�%@������3���d;F`��ah�������z_u�y���<�m�����0���l�v��H�	�:�?���������6��k�]V[m�����w�q�F�SO=u�Qn���5whL����v�m��[n�B,7�t��nBJ`����o����_�l�����R����r�E�����8�Q�'jp&�6�e�Y���<�f���������i��Y�_����C-�~�������������q���vw�e�������V�C���������I ��^�`�����Z7����%�\�[+e�5�([m�Uo
fA��^{�UVXa���<��zk��nN;����z����n*`�]s�5�s��\9����/��r�������w��vs����K/���6�:��S6�x���<m�Qy����[�g���������_���6o��v�fA�������1��&9�P��������6��K{���.�[+��o,^xao������~#�<�a+���Jo��$����J9��������9��_{k����Ze�M6����!h�P{����@���3�,7�|somt�y�{�6j�%�(s����"fn�ay���[���?�i��~�[�)��R~����J�v�m��^������in/#�\t�E��������O���3R��;����{�Y�_~��Z)��sN������.���{��U���\�������F��={vo
`xe:�g>����y25����������/����������K<����v���V�
7�P>��O��|�;�G��(6��G���j	����/����"h���c�=*�����B4�]w]����{��j��k~����v�i�)�f��U^���V�LO~���v���}�k�����A�#�F���c'�pBy��__�����wJ�~�������QG�y��^6�l��Z)�\rI�����'�\���[-������.�|�;���[�$\����z���,�6�c�9���/X`���m�����<�\}�����G����_����U!��e�Y��q�F����2��7��<���=R�Q~�����M7��|��������3	���U8�e/{Yu�Rm���/�y�k�����#��w?���/0��o����^���o~���MO���o�q�W^�����~��U�&#G�
R0S	�0-���~pYz��{��r�y��#�8�����e�UW-���k9��C{��'!��N;�<���_ �0<��O��pz�#�{d�K/���Zj��V�F�I�f���*g�uV�'�I����//~����aF��h���-%A�O<�<�y�+�-�\�Z�������������>�����+W�0I0-����O�l���������!��j�v(���'�[lQM��������Z�j��4�f*���a���Xa��;���j��l/a��~/�����%/�F�I�
�d�6L	�dX����E/zQY}������Ksr�T����O~"d�;��c9��s���V[mUV\q�ji��$$�3�vF����^��t
���<{��g�o{����9s���/�w[Oy�S�I'�T�:,d�� W�����l�Mu�����[��������{�����>������9��c����_n���j=7�u�QU`a������o�kH�!�����Zg����[�����?T#��}��e�5�(O~���)�'����*P�pM������V[��03��s�=��&h0!mf�v���Q���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h�@�6���
t h3M�{o)����aZ��?P��r�z/`�uoU>��O�	��]���=�����eQ,��'/j�kXL5�Y�e`���}Ku�f�m�9���/B���r�_�y�I���"�����+�5�RY"{,����5������{���G��
��V[�,��u�K.�{`�r����������;�����F�W\����*e����=
�Lt�1�������x�����+�\�:�����{V�S����+��������J+�#�8������:���;�V�=7�t{YrX�z����zk>��W��uG���+�7\S��Cp��}��R��+����2k��X��s�=��n(���?�C�1��}��UW]�j�`f�3gN9��s{k�6��o�����A�*w������5��{���>�<f����B��-������;o���ca�7��}��e�7W��)K,�3X<�t�M��_�r�����RK-���w�Qv�m��".h0�	�0�[o��|�����|����r�>�r�s�����r��v(Xq����������U�~�\z�!e��V�=���3����M��-^�����{`����%�\R���/���}�)���S�O�����1u�4�,��K-Yf-=$�}�e2�5�}�e"���n��~�������b��g�%�[��0,�qp�t����9��%Etw��w/��+��]��sh�cS�_5��-��>����g����u�]����w�9�y�e�������l�e���h)W��A��y�����u�c�Y�x�q�8����'�������/�e:�7��R�r��^�[��#��YK/u���E�,���\S�e]{I]s��;eI�9��%f�%�������I��*�L�zo<K��<G��,�+�k��g��k�3R_�J��O����������O�����4�`}=���������,�e2�{�wh��]�O�>��.���}�)���O4?��cS�Y��.� ��Y�v��{�m[�3A��*��b�d|��
+��w��?������;
�_~��m�IOz�"9A,j)�5�O�~��T,�����/�������A�����t�A�]�zW��7�YM�2�����cQ_d�.���Zk�g��V[�+���������_^��G.������}w���w�+�m�Y��
;�f?My~����O}�Se�
6X���z������r�e�Ue� ����T��y?��*\4��fQ,����~��A��;��S5��!�R��������l���E����-��b�g���������.�����U#75��c�=�������~�<��������K/�}gx�y������g�����
},������k�]��>��S�����w�!���^�A�'��?���e]{I96Qr�{�|�%���'7�;�����'J�Cg����'����������~�� K��^������������.\pA��-����C{h��{�����2#������}w���G�4��X��S%���'?�H�=�S���[�*�{���|2��s������|��Vd���_�R��v��y�_��]`���3�d������K��v�u���g=�*�?��W���F��5�8���<_������x����SNY�3I;�TJ�d�w����'�]���>x���9�HR�?��O��;?���z�a	�LK���W^�����S�<`�yWx�h�e��}u��&��'�[?�0��T�;e3��{����2d4�����u0�l��e�l����a�.[�^rr��$�G;>���/YU���?�����UCL:����>�G��x�3�������F��h�O�z�/��]�������#�H������_���T���R�����7U����}l��������;O:>�����6��l��&�^�����7���:�?��O����42=��:��rI8.��d6.��5����d�8��������}o�r�-����������-/���i���{d��y�Y�9w�M')���d�_S%�D������+^���G<�������su���_���H����r>K]*A��4AiT������BYa���
�>���2��������|��'/�:�9!����~zom���@l[��Sy^	������O�=�x�J���%��W����}f��j;�m��+�*�L�5E�:�������|���*!����GU7��>���W���R��_\����g�n��L	8�9�Yg����E-��&���=�<�9��_s:�S���U��>�:���=b��W/o������������W_Oy�k��f�v��\��=��=���v����U���7����L{J�
V7�|s9���}|QkN����<9�����h�_�/�7�f��E}�K���}�{�}�{_���}:����V{���<��,��LSK,�tYj�e�t��?��/g�qFUi\X]����g�}v5�����*]9��]/�-�dY~
�Y�0��T�����f"G��(�x�+�;:N=��*�����������G?��XMc����7��
Id����b0K����+����Je���n�=:�4"�.��%i����������|����%�����y5a��-�������G�CFVJ������N#~�x������p@y�_X}=��h���~��6��=�0p�#��������tv�}��%��K��_���-��%�^&
M��0�����W�.	3/D�k�������N����Z�
��k���e�����\�
�L7_|q5r�W����#�#u�~��P��������M(�/2���_��E<I`m��9��T�CZY����O�5��4:M~?�j��&m��I��a���L��v���L��}�k�>=���w��n�v�n�9���z��<i�X)�32���M`iK�������Qm���k���
���~��	����M�eq�!�Sy�\0�-�\5e�e�e��.D�rF*��4�RFt�����e�C=��92rL�pZ����M��o��62��>��j��W���UCKS�nM����R	�e$�HP�;��Nu��t0�����%
=��������7�lS�*���3��������i�����U�/�,#7$��0���gV���N���c
)�	����;�r�E��YX)L�f�5K�X?��#���}��	7E���<2s}���a�%� ��+2
l���4�MF��MU��	���;$��J��'S4-J�O�Q��]k9������<�j���?X���wWS�4o���A����mr�X��"}����.������Le;�To���{�2x�%es;��~�p����lr��u��x��'SF���.�����R��>�2�AB�#�5��9R�����(>j�L+�~�BO��]�}.����"`a��$��]�Y�y��]w��U����B����]+1z3SB)i���f���r������#�D}�cS���/��0a�W���U�-����Si����[YF�I��C��j=��,�##x\~��}�����N:�
a�ln���+�K_��N��d�17�#�12
SF��G9z���Wu��z����T��6oy�[�v�mW�s�1����Q�^^���rcU:w�B����uo
���~�#-�������������2������#�N:�&"�������>�����pI�eQI;O�{��.�f���&T�N��
V�>���5�&�.��dT��v���9��nSH�t+��r5]p�2x�%#�%��/��/!���P�0����ahwb8���Jx��xG���J(�=e�x�.����W}���"�A�Z*yM;}T{�����wl��
�<��������k�;�7M�T�"`Qsl0�/}�K����X�[o���E%S����N���}���&�L1�Q�iJ�l� ��;�\6�p����N�2���l�i����/�������Y�\\��P;��C����h6)��}z���=�)��R�?�	O���j����.�]����N��6*u����N���1�������O}j���>V��n�i�;��x��C1M��^.0�k�����������Q�h6�,�NMo�q���?^]�����v��2j�X2Ux�	�������t�����&�\/��N��~��������H2�z��;��<�Q����U�<��2�-���������6���S�d�ex�$�k;}T{���k������
B:��>��OTSq�a�0�M7�T}=���������Mip�r�-{k����Z��4�;�rg���_�[]����_�r���@�����2BF���"�VF��G�4,��3��6�2������T_��h"3����������:�Q��a���_�����~����Q�q�����S%�^F�h��	����=�%����wF���M��Ls��o|���_�9v�u��������Q������6�?������gr�A��q�H��}sU�~�K^�[�����i���[��������*&"��6��;��:�<����?���6�t�����]N3}T{��T.m`�=��������j�n	����:P2��dx�z��ZB6]�?	�6�.�����.r�@B�����s��4�=�IO���3QA����"$
��>�MF��3uf���*m�Q��������~om����0=�h����+#��f����Q��n��f���K�n����S)�g:�����o�UiI'��_\��h6	��}����y2uP��?��L��������v��\������]7��|�����d�/�Us4��!���g�h6�2�?�A5XD�#�,�<On����Y?�����JQ�Zz�9$�;}T{��t��32�x�w3BN�;�J���2��
:�_�h��6��.�H�2���k���|���<o��&�5�kt��<_�:�4�U����`�y�c�w���]�����8���*�Z�m��������wQ���>�|my����hF�N��Y]��D�6�|����-K&R*��h6��?�)O�h��������[F��<�;<w�����5�[W�}/��lg,it����S1���/.Bbz�h i��]~����������>��R��x���<W����D��]���5���A^[�mg*�1��%�}�whn'�X��r{4�L�7�h6����?_��A���x�'��'hS{���^�F��V[m�����e��6���gF��Y��tn���G�?}T��jN�����V��,����9�}���9N��p��dy<������3��s~����:�3����4�k�v�o�g�Z���������zc�w���������Lo��{�������b�}8_7��W������t~7�W�n�A^[W�mg*�1��i�-���,�H�3���f��vq4�����>�����{����_�Z>�����A8�������l����momd�g��:��qr�f��������n�������?�aoml�oG*��R^4���NS},��O�mwQ���w�e�����3��������3��]?����6���4h#i��|>������]����I�q�Q��H�&#Rez���=#��fx�0�Rh���������N����'�4���n���SX'
�_��_U��x{IH(s|�������v�P�v2le{��<�i�O��h'����I�1	��8j����E�Y�g��~�j>���B���������h��,O|�����4pu=1����R������[������J�
��n���vd(��:�s\d�����2MD�^���zI$��[�����8��H����������eFI�]S�8�������??����"�1��f��5�Xc�����~m�xn^L*�J�0�F.���I9�m��4��A�����Tk����x��_��7����p���N�{�w\���<��d��o~����e*��G�{N�a?
{��7_���#�_�
�	Ox���}�y��o����?����������N�TV��������5a��g�S�3C����M����{�i���/������>Uul�9��o�<w��W�������uu��T78��S���nZ��	�������������3��h�1������3��4������O_S���d��S���{��X^xa���)���;w\fy��m���{�g�}�O�1����L������~�����^����tbM�c����x��e�'?������m�nW�����y2�L����9eT��:u�:���r����:��y#���0�r=��/r>��>9�����G"�\m��&�y"���~(��P�c���^>����r���O�~o��e;�V�vR���>�md��W����=�yO����>�w��s�w�����=m�)z�A��D��S�GqD�����%����et��k�y5������}���W\Q��������R��t'8[/�����������������Eomd��������~��U;C�!�}�^2�j����r�G���s��WW���F���o{��z��'m����lo�)�r]������@�1�|m�F^[��/|���w96��k���\sM�����{������t�	'T?;���zio��Cs�V8�e�=����ba���5�m��f���<M}��?i�XX��l��6�~���k��kO����^����.����=O����O[}��s����^M>����k����L�a0��M);s�M���;�m ��y���[/�O3�H�ASV�T.�}5�=��s/z��z��'�k�\N�:V;M�`����w�[��R�n��8J�.��1h�\�g�k�l�~�%�w������0�e�q�)�T�^�m�e��GY���n'���k������F���+���j*��H�^�!��H����<���������y��h>O��]�5d��Z^o��	�����*�j3\m���G��9hh%��f����x�69q%���
+T!��PGj`Oh ^F���I:M��*A�$X�v�u����w�������SN:���
���*)��N�uE$'�$�s��[^�{�H��r������K��fy,��i�jV�d�tb	%����J�hR���h:�rg��>��2������)�<i������e������R�>��eH����R���e��-sl�5���L_�@��_���r����l#?��N��4���,��<�F��m�)�?����dt��\����� �Y~?eE�b��c��w��F���)�l�o��^{-0uRSFyI�~S>���f,��n�����
{s�4P�������^��W]d��vh)�i��4�5?�4�w�;�����!��h�F���)g"I0�_x �<��<��c�}:e�H��^�"]�5hB�o�q���3	!4�<�kKYS7�:%Z^C��4:e;)+��E>�����4�4;�Ju�U	e�;�#
�y/)��z��<��|P��J�b>�t`���4,��I�E��X����1�u-u�v�aS>���k�L��bP���eG�n-��)�2�U�3�F���C���"w
rw"���:����k<�G������x�69G��#�+���l�o:��N;�����}���(a���+M9'��Mo�^k��'��������C���y]���A��r�;V{X�!ym��%L���\���u��s���_��Y��k�5N���CK��dB�	S�Z����N3�5E��H�,��isL�h��4�~�}4�A9~��t�{�9H=u���Hy�X�f	��A�Z�d���/g��W���������g�Nt��}5����&�k�A��"�=�F��\?4��0�%��	�
�6
"������i;jw��<M�/�����i�}e����G��\F*oRh��;�mzy�����v�l3a�Z���2����5t�k]�5�������O	C��������_�h�F����%��������9���"�o|��r7e�H���4e��LH})7����}Rv����i}�����A�c��M8(����m�����kN�hP9�$`�m$�~����|�y��N����R�����k����`-�����@O���*�����vy��h.�d_�g�4��hSv���e��`,��hO�k�����$�}n���i�~�ZS�K����=}T
�A���SRP�G�L`��`��C.���UP�`���vGE?9���(m;y�Ikf��~����|���ve&���gI���I*SI���*��#������\4�"p��T�+���'�X��m��?��T@���7Mi�H�a?��}���_e=�3���d���f������G����4�)�N���k��40�a����1$����M�u�0F����~��U:�k�gdd������#Fz/���lg�ae�8���Y6��z;ue>�K���s��0�R���6��\���N�(��m�
�cI�H3����t>+er;h�;��A��d4�f�HE]�a�W�����)�1�����J�O5���h�r���~s��g��?u�2��|�z}���0r�`E������t|��}��$���5
I��[��F*�RN9�������M;�4�{����~���$��0_3(T�M����w��(��d�)�&C�E����Iw$)C��J}�[�h������4v�5�X��	����Jg�DK(�y�S:c�\w,*iWI��y����G���:��bFZ��Q9W���#�)������`�m�;��tDfd���lN�}n�hm;y��H��������������Po�]_�������7P�T���oFn���/�4����������~}Y�>"#����~�K�����f��;�%4��D�����^�������ks��5�?�=-��i�n�Z��m���6m$	�t�1%�Q�l����=:O}]��3*K����*m*U,��M#��H@$����J�(m8ii�W����6��u���"�q}�C/i�M�9��Q�'��]d���5cn����*���=��K�W��Q����$�����l��2�K�u[]F���{����4U�h�r���e�H�1�`�����{���sS��)�	��H��=�I��^��9'p�>��R�����������~r�&��U������4�y��Y�}�<���w^���m�y�u�������
�)��/p5���v;[�������3I=�=e�xB�c������b�����t�!���T����N�o��D��rwR��"��F������k
��\N�)TS�7�#*
�Mo� K*�9�Gs;		5��N�v.N�jm��2�d~/)����-�K�M��%�R�FFH$?���s��se��<G^_�r�C;����T����E^K:'��W:�����M�������X4rQ���OH,�E����W_��E���/R����V���rQ��M��,��i��s�Q5�Mi���?�<G��-
���>���1�jy���\������M2N��-��$��F��1����K�.H�-w4e�Mi�y�{��i���l� T������%�W�{I'y{[M!��.a��~�P=�M��%C��o��<
f�#�1���1�s�0�x������+h�pR;������������_�W��L������4�D:���Eu�N9��G�K��N�F�4���v���n��0�Gu���\��N]&?WK9�r$�|S����r�O����t�����O�#�~��G�)�����k�]�y�y})��������.#R%d����W���{���)S.g���{�t`�<������2��{�y%�G�K�'	�5���G�&�f,���}����y�7�M��$�t���?�<��S!�����]/)���h���h�pO��c5�G�����4�e�������W���Yn�����9��� �G������H	`��C���B�q�zP}��N���R7���|��������9^�
�$����4C6�vmn'��95���y���-u���%�R��/u��n�����ym���������R_J]#��zT���r��hw��m2bm�7�H=	�g����r�n�9����������Oa;�\K}%�os����L[�}"�e��L��v����k��\;��:b�h��*7F5��T?��w���r��6�fx�~
�>vs��>G�H���e�����zr�E�����#���L�Y���^-��W���N�{)��\���>r<o��F��K�dR�5����}.#��%m6�dn�������k��'���a�\���8mDu3��6����v����-��	�5e��������et�~���v�����j��3�� r�m�+�<o�Qr�^�w<��3zk��o��[�i�i�#�D�����~�r"u�������kiS�����,ez~����X?������M�i'���1�}�H��i3J�U��:�m����mR���H9^K07}�>��s�Q�Ws�:[��7���^�����d����se>���j�������4G%���lK(����3���=�o.�UM��m�7��sh���i���}����&�M�Qt��6_�HrNL=��>��0=�wj���W:�(�L��zb9Y�P�
2}T�i���\.�������xs���\����d:gs��0��4��F�LO��l�I?����4�4���TRyh���=�F.&swC.�r�n������h����Q-'��C���7�'�1Gy�\&Q�F�\6O�ixIG��
_���]��;�����L7�� �~�����a��M�,�������g���������	��x������?����9C[����s�;M�xWI�����~��9���,��t87��W������<G^_,i�iNc�� �uCG�/eS��|&����F:���i�h7�����P������<o�K���������oR���a���6�t�f;��9�@�C��������I�[�VS)����%��fcV?i�K�����l�w#I�X�o��Bf4�#����t�a�=�L.X�
��?�GM��(���C��v9	(�������3CZ�N�;0����4`�=A��q��j�Ki����������?xL]#����~A�:d�7�q&������8�1�/C��q&
M9We�����"ucd��H����������2V�e��)���4�e��l'������������VK�'���]G���\O����3K*�i�_)��x�a��w�L�������X�r�3!u�Z>�A��J���M'�h��D:��U��)��
�W>��g�Q@1	`Q�t���>���Q�7���zz�0�i����k�LU�F���R��y0���ri���s�hA�tl�^PKgU�{������rr�Yn6j��gz���Ns�����&��Z��H�'�=	97?�ZF N��)�	��H=1���5�<�G�z�R�>��a�A��y���&mE�	 ��y�k��~�Q�W�sL5�f�~w�g_M=�y\��7�i�>�}"�E��t�������U�R�o���&�������j_7d4����3�hlK�f;d����W���3����r#eF����F�p�������k>��1�|LH����r$������M������#��oW���My/��h�K����o~f$��d;�6���)C���){�u��k��W|�S������5Os���c�N����'P�P`-��]:����Q�ina��'K�?i�K��}��������z?�������j����"A�Z~67����;��)7��M8u���9�7%���8!��w�N�r�}cUB����>eR[��S�i�lr�M-y,�Ru�Q��RWl�2y���D����9������cg�����)�e��F��`��i;��o"7���u�Z�S��$�����/M���fx�-�%�i�l�w�vR�=��������M��e��Kt����h�{y�A�B���,�r>+x�k�S����7�������c+�i=A��Tb�A�\h�#-��]���Q�>����hr���~{:�4^4;�k���b�|o9��O�M�IgG��;'�\�t#r�����f}u-?�[c��4��".��D�2�d���T�R�hv 0=�2�}��K�����R�k�lB-c%�sGH:3�MS��i:�TL��:6r'J��f/��T�s�{�}6����v#O^o;�4���������O 1�QR.�T6���4\5_C��{Ixd��/�M����4$�Z���5�KCE�7����D���:��K�'S%e��:
p�N�~��7�����u��46G�I`�=R�S��� ���������3>����X���>�rk~^L/	X�G�����c#w5�����������N}&����n�{d��G
=�������t��<���N��x�����l7���u��4R��M9������t��9��T/J)����\B	�$8�/�i������.�G��T�(3R�/
��Q����R~�1(���L��?Y�#�$�3V}<��M��Y��3���$9��>��;��a���N��|����2�����;��s
�0J��l�N�K���Y������E�2�]��-m�����sM���O=���)��i�o��R?O'Ss������&��R���h�#u ��Iu�~4icK@�)u����b�O��������um���D	V�be�e?�u{�n�c)��m�@lv�F��sG}sJ���{�)�zjSB)���'m;��|F���t�6�OF1H�oFci��"���x�+������vj$	��s6��fx��w�i���D��!����d����%������v�~�s���]-m����f�|^���e;�My��d�\�N��{�k�|�]F�I'{3d���N��O{������d?N;i��s.H+��C6�>M9�c�k��T-��7?/������
J�_�+�f-r~�
>��0��/u��5)G���O�ry�r:m�������I�>�r���TK�&�VS��f�h4�����k�����I0�����4C4My/yM��s��~I�z�N�~�4���N������k��NF���#�������N���~�|��sK�&S��Z�� �'u�~�F���M���yL��Q�����y���Z�m�6�M�/�����f'y=���rQN�u�$'��:���iO�0�k��7C+)<3��X��Tr�y�d������@N��������2�O��w���rF��Fs��Va��g����^��TB���N�������;k�`L'Q��O�@��~r��nO0������H�E.�G�F������]A���~JI�o�m������!���Z..2BMSq�4x�,!�fgl�z��~R�o�iP�!-��EK����$�����=��H�:���H�7R��)��fX%
t#5��&I��{r�K't:����I�s�x����fGsBt#��.)���9������)�����m)���[��i�n^��&a�v�G����:�hO-)oPM�C�s43�Q���g$�`Jy9�6�������]���#�D����YF�r(���ka����x��N���~���`�THcS�o�s\����wmv $��e��v�'�u�|�n����[o���jJGu����t�)�����;ps���x�'u�f�_�	�*�������$���������N3R�E���C�����k	������WK�}��k���o��r>I����'��f�����&��m��������,�L`�}�~��	R��zp�x�c#m��pF���v�n��o<r�L{���$����c4#*�=����~4�\c��:�&���k������)!�v�'���K>��7;�~����r]�~]9���k��v��m�v1��i����t	�G���m"m-#���r~��i�R�O@1��������~��	��#x��X���oji��w�p���,9V�����9�_@9�H���,��e_�"�G���&�->���Hb�sG�)���w�i�#���)�������L{}S��F
���J[US��i[�"�B����_�������f�`t��MU�&���������V�kM�a-��]���1R�J�[��&m���SF�Gn�,�����+h��M\��@*���rFEI�6�\�7&wE����meH�4��� n)�s�BSN]G��I/�V*i����.rriv����o}k����r2ov��$0������D�&F*>1#��hK*��hJ=
���mS����a��
o����Sq� Y���7z�D��t����H�^����:,S�M��+H����4zF�s��T����K�n�T�����&ee�q;�����Ocv.�ji �:�F�8]/�]*��f@!
#�"QKx�Aza���g�e�-S���8��H��Xr.l�����#21�RgH��r�]7��\Xr�������I�js������LFK��<u�.
�#���O����m4iD�("�2&S���IX��y��^��v�-!��#n�<�,���S[:�#���A�kJ����)c2�C[f��O��
?�Rt
�.�~es��:��;3���4�RS�i�F{����]�1�SF5%���$��`�e��f�U��3��}��6j��J?����U��)Gs}��Z+r��7S[�W�sV������r�C:���2zf�����.r��5l-�����v:�*��\�������I��T�HAw�t�~��%mz�[gd���s,�G2��/�?F���o��9��i�KP��.#��:�#�����9BL�.�v�&����<�Xu��'�8�"�o���uE��(�#u��y��%�����<g��s����Z���'i�m���0m>)��a�\K���^��A3�����7��e{���R��-5�R}s��R�7o��
{�Q�:u�!��+��%mBiI��pL�MJ=��K0e��L�\�F�r9�L�W�7q�&}8cM�3^i�J[Vs���-u
��/+u�Z�����!��x�����-�q���L����6o�J�H��S#��t�y�~�E����������$`:��GS��:��L�U�s�8K��A���g��mN�Y�M�9�����S�{<���zk,*�6L+�N���x��FE�@��c�NMa��A��������3rONz���U1����ICS=��D�S����@o�-��s3�y���`��zBe��!#�j*t�-���c+��N:��~������;Zr�S�����?���7^y�v�n� PBq!+��=���~�����g�6CL	���0�7�O8-7����Z�&j�����fx/#�R�f���V<�ost����|2_z�=�.������i��.7s��.�3�k]�4;!S��,:	jeh��?���z���r*!��h���7��)����$4������7��A]�E��.�s�����F����<#}���#,�Y��M�ov �����_i<m�t�������<j�%9��f3r@��K��k�&��2���	��9��)��O�Ws$�L�������Q	����z���\�9g
:��D�ta]�D����G���y.�9h<�FE���2���L!5��%��t��$p��f�����l�0p}W�h�����4�������_���_T�s���RG����Ig~��dzJ #���k�~K�1LK�*u��l�:r�+G�����^�v�l{P�y]�� � m��'�c8��d��t��&��ZO[Z�4���������;���1H}?R�5G�L���*#m���9�I�OW	Mf����6��N�P�X�9����y��ms<�F5��=Y�m���1��\�������T����e��
Oi��W�Kn4�1������o��Ln�)<�p\n�I��0�xFH��r9��AF4�����|N��.�����������H�\3\������.��NS3��l{:�<�@�3���~��Rg�L��GgI[���L����k� G�m����F�f~>�D�f���N9�r�������aZIe�9*�X�G��l�$�;m�x��������ER����Q���gTw��1)�9'�f�e��_{�D�cy]���^o��]�G:���	�_�O��0��L�"�\�������;V��0%�����k����3�r��\�$����~�����q���m��-���)
�`O]�#g$�8�]�����:��k,�,h�d��>*��f�)!�����GF��EuF���ti�y�Y��Q,!#
/=��#���dkL�te���p��].O�t�5��K8s��VR���0Q�T�������p>�6��I��v]+�?���
�u�;���+�Q3<���h��y�`kB^]�p�|����>�~�G�6L'As���v����-!�fcY��S1eT-�A;L�A�#�L�sWs���}y��T�_s��z>Jr���\'e��L�q��'W���'�/�^�W�������j�+�-�����_o��m,�� #�0}$����}Rn����~��DIh%7$�����t�EU7��]{���u���J���>��]W�����N����R�ls{	�4GrI{G{z��������`���>���h����V)��G�>�=mT���3mT��&D���~��:���O{O��#����e�T��S�tmOdx��J�=��DHY�%��d���KR-�	�����<7���O��k_��Q���'�����vy��\�vr�[4��.Ks{9/6G����a���A�������i�r������F!(�i��������)�7�)��9��G?��M����P�ry��`�It��8��Q�i�R��p�����
D�4����v3|X
�$��`�%P��Lr�)�t��C3a�4�g��te�]�{<�����;	�4���ey���]�+�������'Y�����R�%�a*gix��I�=.�_di9r���r����0Z(oa$i����3Hf<&�b%���(S�\~�3����~�u�%�T:kG���Limr�����t�Nv�&�G3�������)S�u�>j"������8��,����;R2WwSF;������E��9���H���u�\�Sf�1y�
��'�����)����}2�H�g�caa�N�����Ed"��4;����X���#-)����y$in~f��b��FF������:���A��A��d��������)��O��d�����	�6G+V�L��z=mT�-=�9b�Di���>S�`r~�����j���o���sH������	��Q�k��d��:x��M�5`:a�S<e$���3*t��E{��bF�����1�����l�I�N��i]�zx}�������,=$7me�M%���&R�t���3�L;x0�������`s����2JE��������f������L'ds��.����b&C����ZZ�����Q9mT���\O�Z�
oxCy�;�Q]�����8�c'������}1m=��Q^u�\/��O{���l�����>�Q]S.���=��D�������t��naLF���]{���1����iI�G��M�r9���@9O���-e2���u'��k,i�d���#u�6*�^]��S	::
��j��YonL-A����Q�0�p[�d�r�.�Sxe���=�!�F	�d��T�sQ��|�>������y�"mD�f�&SD%t�����s��H9�y�������v]2�U3�y�|���I�}����IB_c-9^s�f����.�����8�(���y�\H�B��@�IgV*U9���e����k2�����:���4V��YK($�� ��;���*0���������$�+����:�3�� �G�<lO�����c1�����L����O���:�2�������r������3�]*Q7gI !�r:[R~d$�4��\Nx,��)�'�q����M�r9����D>�t&�~�����o���T�lvN�g6��s�lI���!�UB����*������i������,A��]a�r`:�:}T�-�u���2���Q�k���?����U&
�i���PNgU�0�Z!#vt�������Q�9�'���M�r�y��1�������$x����+FZtj����w~mJ��d\���!�K��	�e_��������}�����;m>���s���Fu�e��u�w2
�_��j�K=w����1���TJ[Cs��t������}<���m(mo�Q~�h����M��[��#��%��u��|���S��Y�i���HE������{d�����?�~��cj���W������RNf��!�m!�����r�>��)�tN�.S.%H�m��3�b�zh�N��>8]������|�	Y�n��i���>�9�=��D��S�97_�xA�.�G���G�Il�������UKy��W���6u��gE����Q�������J�e3�3���9��)���y
�\��$X�Q�6h%����|��G>R5:$9�����4������k2+�9	�u"��������.��EkI%1sW�����t���
�����!�$!�\���K�&���(k�����g���9M��OC� �G��R���Mr��w������/���c�=v�Qm2Z�d���4�\q��+��-)�J�y:{�l���t����4���1�����uU�(
|iTl�r�gr/�'��E�<�����Q��m�4������_omA	�5�"J�H?;�2
c�=�Q���2�L3���D�f��pnx���U�93A�<��C��G]�����d��\#��/���V5��'t���L�R�n��n&���o��\���~�����������2�o���~���\����*@�Q�3
r:n�i��j�|��(C&�\�'������L��6*�	�v��Qc�9������6�	1S#w��7g����Z�,��]�[re���6�Q�s
<��!����nu�T�;)�w�u��-)a��FqR.�mQ������S�SpE�������$�6�4P9��!���2����d�4Y8J;��.�G�s�~,�hzwv?	���%a�t�����Q��m&��q���w�|�+��xB��LR�~��*+u�&�\~x�t�c~�%'�A?�G{����;fr!��DoF��m�:�����a�H9;�zL�V����j]��jO�)��#�L��mv�m�Ff��d���J�d*��L����3_��wR.�,IY�s~����w�x�`R�NT���KO��B0����Z����Q�������6�_}�=eT�U����4���D; ��|/�r�s�B���a	Lg]����	��rJ�u��I���w'@�e��{w�PA"�@P��@A���DP�SX�E$�D.E�KP	��KQ�PT��CxaY9D�Zn��w������VwW�t����;�stuuUV��O=9^T"�����[o�u�g?�Y�;c���]y��K�~m��Oh��������Z��|��I7$r�����~Y���#�Y�1T�
�/�����`�X��L<�x��AE��A);7'#��W��[���^cB����A��|��t�(b0��(�a�"�dL�DZU�
�����p<����bG��UV�i�L{@<���A�*�2z�.���]�3�I��*�GkI����M+kF���.��'�l������{�����j�3���Ikb9����i�(��N;�����	,�.kM�b'���,	5<H���0Y�4�t0�m��������rC�&.����.���h��J��*��<����dK�}�?H�j�D���o�9d���@�&����_������-��"��bb,��~�����-���������8]�4�����;m�3�(oM�(*[>*_6�D�~U�IQ�=�HC���6�b���a���SO�T��!���T���X\�� #�1w���I�K�m�0��`�7�����u�������W����}F������6��l�|T�l��~���gc1��.E�K�>���V>_�������
�����n���4�uZ>�l���.�G�H*J�K+�~
���������g����{�
���No~��C������nY*����Sy��?�����������C�1:�U9����1�N��G�x��'�A�h��v
���v�i��
UL�Q	g�O��O~�� B�������\���Hu;�].[:p��O�Y�����N\8��U�&��i$�EL�G�:!�~�E��IxN�	5T���S1�q��,�
��2���c��O>������3`���n���v��������b������`>��CZ���T�wb���#���!.�f2�D��L������F(�����Lo�T�7���9}
(�)�w�5���
%wUQ*�����w2�	��D�V��'�c�f����w�G*�R�QI�5��  �d�:{��HP��e����D7��n���N:WA{E'->��{���?�me;h�z-o����f���a�d��U����n��F�l`;q ?#��������45Q~�~F?w�(����Gj��ty**|`��&�z����j���]��$"h�V��z��v��/=w��,[J���t�o���c���Q��Q���j�c�L��Gu�1e��d�l$Mo���b�&M����QL6�,T����njH4����P�,^���xp�a��]��g^w���s�BD<(�/�"��;rS�z?R�P�J�[c�5�271���<Uo�pc
��@?}P��[-��
�2�1Wr,1��V�a���~H���=&by�A!���#7�D��t�(����Q���S#1V���qS�q~������WL}����9|��y�k��!q��X.���<����~��vP�	�3*�����<&���|[>���Cz��^��_6�MM���:��l�{N�>�4����V=$�#u��!��R{�}MZ��Q��'z�lY�����h�P�`�|I'UQ�.�t�;�^Z��P^�;�z�l�c �M'��%v^xT�/���Sp����g�F0w��L�N�	����a/���<P��5�Y�:�B���Kt8�}_�;��H��u�\��|������u�������61����[n�%|
�^��9�4TA�f�mB���k���������n��.�
�i��l~������+�Xd�y�,�w��g�>ZL�i���o/�$No���!��vy�=�w�Z���3�	��h�c �i�����Zk�d)�A���s}_f�eB��89�.��_�{��e��G��k�?"Q*Ee����$
�|�(�18����F1�O��T�H��176�J���U��3\����H;T�!�EB
�B{�����W_�|��|L�u���F����n���Hj�"T�
	GqD87����V8�����u�i&���`TA�M!�o�1����x�q\Q���K+����C�7���j���I�Q�|1�4a����!Q���_��WBb*04�ST_NQy!�ij�����bnm�]v	cM��	5���]�=o,�s����b���W�������
��V�2U�����M�l"��n�#�IL\{����E|M��t�(�T���a�u*.a�^�Vh��h�I�F2�����X6��C]�����*/���W����L<�H�:U/b����[��gD��x�y�'M�u�w���"������<�w�4���I����?���M~�
�U��Jv���Bu�
7��1�,�q��(���6E"7�}_m�HH$����d%A�|���_')��.�*�[G>p#���r|+��������>{t�(���XZ;-��R��R��������es}��Iu���4yq'��w��|�K0�U��~�&�K)���f�x�	�2�����v���N��W�s�~D��������:&$��z�p�"L���b��I��~���@E^V�����3|�����4	�~7�+3����&��}�J�{���(��R�<��UU�&�7)I��/�Z�tL�G�X6��xL���	�j�Uqm�R	�k+�� 3&���t��v�4LB3�7���.��X6�r�O���r	q,��!���>1�U*Cu**v�����Ci>����<!�W}b�lOD5e�u�-$��{,�C\��d��G��� �������hw�VJ�g�-��:�~D�5O���:7-���~���e���Z>��Tz���l�1�K$���i�����)�W���X��c�e�R$���D�����-&O��h7�YH$#�C,��+���S��&���D�7��$��]N����O�e���Thc�&�[z���OB9+���=����4���!_��L���}�x�N?S��������/Ep�lD2��N$���66y'���:�-t,���V��\���=d�s��*�@3��������T��[����#�%��z�����������e4:���T����E�'E��x��/JGT��T
���*�h���u8�r������,��L��MAP9}���:�����S�Gi�xQ�����,�i�N�e�@9�UVY��l!�U�w����$�:�T{H+�P��N�&��_EG>�QS�l����wX���mU��?�,�&��A��������P;$%�o����C����e~����K��:�*s�K-������� ��57�����}���]��^�@$	.�s�>(���nU�����:w���~$j��Iv�GIv��;�F����Q��<�4�,TULH�%A"����G�|������|�bCi��J;u�[�.X�5����u0Y�#�9���W���0y�5H����d,�V�"�r�Q����IK�G�_]�]w�5&���^��u�V�����g1N�&����q���=�]vYh�����U���)�S1�-�D�%g���U1VH�]���|wEU�����\��~���Ku��c�N�W�,�����kr��e�0������s~�8���{����h��sTE6M!y'OD,��H�T�':��Q���IG���7�3�B�,��b����\�����m�W��.s$�G����);�5Hho�}��c4u����Xc�7��:7���I��8_����e��&�t�(����D/��B�������.���.���C�4��F�;&��bZ'1�*&!��r:$U����t��l���GS�x�?2��D:�u��N��N;��G�r:��h��:'��`�L)��2�u�dI�����'�4H'-	��`�w@����n��Yi�>�n*�Dq�eG�3�~�h�kvZ6*�
M�����[�.1&��;o�Xpyu�^���	� L�KK"���+�[�s�����|����7�-��:�LH$H�ILi���u'���)���JGY�/" �kUi��Y�OY�����&�	L�h����h9>�X|��lT�$���}�\����4������Pm��_�)���:	1����y���r�SS\O�`��t������I�N��tS�.�9n!a>����+�l>k���K����n�BB~l���s�G��,�w�d�N���U�h���[UR���=����X?�!��?�����Of�~�
6�u_����N���������=����
��i���|9�:+��;1Gb��"
�,���f�Iq��O<��l!�f�YI��.���Fr�e����z�����F�V����M��~^�oP��������TE���
���}�r,Gq���/~Q�zF��W��U� ������5=]>�����&�1���8O�o�G��D�I�j�|��?��1���b�(Pv-�;T��4����U��S�@w���m6�h�1��V��I6\��T�a����Ag#�.P	��R��sq?P~-�gp@O�E�A���� �:����p�$�x�����������
���2��J��k�2���H�`��@V~>�9HT�D�U8~�&��CtTID�� �TG^�h�=�wN�k_	�P3_��j�wf����D&���gJ!�w�ycm�,���W��m���rr����d��V=��'��Nk�Qp"������FK�NPb�SK<����K^���P�SR�v�m7�E&�v�a�1w������4�B�D�wL"HZ
��U�#��Pa,�n�����o���hO�$�4���������q�C�b�\����>LTv���t�(�ID���L�;7�[��`���k�YfD�d2F{���G]z���f��1�k��|��4�\;��V�f�Y�*��p
N��������T~6O�a��w�)y���|�����*�=�I�Oq��O5�L�3�?Hv&�9"���D�����@��E�X(�G�K��@A���.�2$�PMd�}�m����&}>h$�����q�� ����~h������m����4���I"�s"n�.��X�E���c�4��8V>�^7��cFn@�f�t9�*�FEl_��C{<g����r|�|V�\rI���Oo����w>�:U�8��}��]5QH�1Ab{1�Nqp�q����e���|���8�������4�V�+C���������3Q��K+��so��7_�k�y?�a��F�a2'�W����h�=b��m���T�V��'�l��A����f9�w�|�l��7n>��l���xHco����ib�h3I-x�����������=�����h��QTH��N���+�0�cr�I.6�f�`R�LK\�;
�r<��$��N
�����x�|�c&l��������/i���j�@��9kD���tL8��w���}�],qB����6e�6���r�x��O���K�t�'?F.�ad)=���.��e
��Y�e�m��&,9�1�����L��9�Vl�����b`3>xN%J��K���UY:�_X>��h:K ���8��y;�8���=P:�U�w~�wN�d���
��D���F�,�K 3X���n��Gq=����|�_$A�	��&pC���8.I�a�>p}���3�P��V�"�>��o+����4g���
�'3�������~��G� ��\|���E������:��e�uc�����Iq
�]N�-L��B�4����/�����D&*��#������������;����N ��L����#��"�$E�b�����2����O��Bi;JbI�gUe�G�	����6�/�H�?O��4�f�\�4��|���c3=�����#�����@���i�i��^,b#i�������@>��Z�5�R���&{�&�����g�0�b)��/{=�u~&]���v��eK[�wJ��(z���7��N\�I�Mq�/��T�C�m���T4%�(�v�m����m2�k��KE���c3�x<3�Z/-(F>��FL1�i��q�����������Y��Xgi�([0���/e�P&��3��dr��SOm���>�y��qI�a��"v�y;Qh3���
	��[f���X�{~�3�`)�|��OZ�&E|#_��q#m
mR:��k��l7�
1����
b*��������	�_�I�')��*M�!�p��g�&��q���?����
c�K
�����i%Zn���P�a_�1�~>h+8O�bSA�.�o���dx|����Xg�n��U��S���������kl_�[����c2[���yq3<	�)��;eKm�z�\���%	��;?��?|���-����[�~�$���
[���l��3�un��I?��gK,b;�S
���Q���L���H��<�V�g���4����5�y|�@�>���Q��I��J���+T��� ���\����AJn�y���2#��24��0a�Wz ������*��1y�V���ZU&�P�MtD���;t������!��m����~g�H��3��E^���#P�O`Ov3G���?�����>?������xy����w
����s�sW?AI�;&9wH"��~���-H?��?���L�����``��������6� �Af;�9�!����lHd�}M4�$]��6�s�:���q�A���&&Z��=����{^�&�n.��'I�!Y��
m	�C���pL��a�Rv}Z����lTD���J�m�S�JBc$B~��������*L&�����9~[]3��u��7z�c��-��A>(�M;1/��tI1�B��;��>��2I��J������i�$G�&O������78��x���U����
��'�N�6����&O�a����F`���N(2YFp� �����?�yB����}Oq}����c[O�~m
�x�����g1��=�v�NU��Q�~.5�S��-Me�o/����3�����c����?�����������p�	�j3��^,�/y�	}G&=�CPu��^��p�����I�&7pmju}�@Z��^��2�%)>}=&��>�)&;��W�	���u�]�:���{,��b�>\�/�tfb�q
	5l�
� ���Mo�`����'�';��������a��h\�_^���_l�@k"���/E�d(1b+�����q~p,��m&�_z�"Z��O��s^�s�����1{^����E]�H�
1(����N���1O��u��N4�eLF��6,�K,�8;���:�!b6��R�7����.C�/����L%.�w�����<i�D�A�|z�c��

u���H���4��	I��x��������q,����K^}�����i,�!�D�a�vRI�,.���������mD�}L"
�%I�
���������O����r�X<?G<4Emnl��[�I_��b~)O���
�G�6������
�qc�D�:�{I�m�_T���E�L�f�����s�������}.��G�(���l�g�52^s��L���A�����"�C������<��v��']^c��X��E�.��Xh�M[��H�M�28��)�e�L������������R��7�8fR�*�D}�;��D�6~/}=�������w1��N��[]8�d�|��|e�����E!��$�4a�#���2=[���.����+w��A����@FL��>ws0�L�9XZ'O$K1�Yu��A��	9������L��Kp���R�d�3Xt�	���mm���~��1���$�yB_�;w��gXZ����3�49����M�5��R*$=x��3O���,���6y%����H����(�������{T*K�G#X�&4��A?�2�{����e�h9�h��s$.=�9�mJ��w�0A�.e���W��A�����`;�$
�������U��E�6._:s��d��� ��;rHL��'��?���i��C�5�:e�u��=iS��	�$�����
�8L��7��j��@T^��XO�1�&����S^�0���c��w�K��	�^,1iD�%�$�;�{\G���'?	���W��u����E��	7%q��>�D&���������1���L���I)����FL���6����xM��������-�{P)&{d��Q���!���r�]��������:+|/��.����I����S���z���OG$/��6 I��6��6�U\�$7!���N���-��"��j��y/����CQL��o�� ^�'����kW�&�t	l����f���_����;�,uSiU����Xi��wy�
�<A2�����M7����B�'�$����J��I'5�-���j�z�^����[�f������m�]��#����qp�����]Bqt�V+��e��/1��&�D�?���]�mR|~�m��+�O�1�mY��RA��q��K]�~b�
7��C���U�l�4�5��`z-%�4����>�
��{����W����L��t{��h��h3Ip�^l���Faf�w�����Nd��Q�,�V�"��{}&w��r�����T�p�	~.H\��@2�'��^��qQ$���t.d��V�����d��$�������jR��E�D : $��L ��.{���q�$�1�RI6#f�Z�1s���C�(9F&;&�8v���9������D#�%�	�r��HZ#c�	/�{���'�$MXH1�L`�At�mI�^pwJJ4�mH�!�����q��b�O�P���l���J����%�C�����+M6"�>�	]��y������4
�+*"�	�*��E��v�}����N��C��<L�6��az�}��M?�[OHd�A�V�����w���F{�/$E�K`�x��&R�
b��Y���/�0T-A�mIc�3,	��$�n>/��$��3����w� ��4D�
I�L�lB.}
�$\�����I��X ��1�-�
\�r��^N��MT:���C�<I�4�3g�(��L����,����TD��j���E�TF���]�b���T�$���'6R����R��e��q���k"^o���krCB�>QD�!��]"�f�@���!n[����F?�m��d�����U�f�Z|���D=f.:V����2��2U�?�nV�U������j���
+��������>�u����{���m��d5��$�Pij� �F<���	b��GH8��d�H��	H�+b����G����X5��h��
4�'�;��/��e��U�l�q+��������H�&��R@�$;���0,x�����T�yJREL��t���i��G`�W�e�).s��������>���*	�mYL`����1k���%�&b��w���v�����\C�����NI6�/	Hq)���r��-7v����W���IbE�o��/]>*b�q��d��N���}��I���x�;�1�9iX=��3��>R�~��<~��b�����H��X|���%�X�i��#�8��7����[o]�\����4���GY��N=��b��^s�bR|�C*F:�������}����>U|���,F.��u�k��(���o����F�����v���<�7�y(�z���w����o�yx�����~��|�#)�>����/��r�7�������������vad�����F:H�)��Rl���c����>��b�SV|��_/F:D����?�A1r��3��/F�����|��?���������[���s���q���g�],��r��|�����{���s�9����?��x��m���o���H��/~�Xz��G�����4�[ndPZ�hG�w|������1y�y��s�mp���}���(Wj�.������}8�ig���������x����;���j�#��^�Yf��}���`�
��?���/��B�7�s�w����o|c����}�{����c�5�(�����}���7���Nox���}�5x�x���s�=��n}�������}�m������K/�4��|V�/����v�=�,�9���_��W����5,����^��������x�>�l���>�`��CM���x��G���as{����?������7o^���s�UW���gC�
ox����6���.���_���?-�|���o,t����.��h���������-�����x�[�2����l��V�=������h�v�a���+����me����?_d;�\s�5�<����_w�u��v��Xo��F��V��:��w�1��7��Mc�L��~����O<��������,.����]�z��~�5�]w�b���.�����O�n��6�3��������z��G�M7�4��q������x��^����6�����������>�y��������}���+��Y��X�>g�,���3�l~w0x�t�r�k8=������(n����^��m�q���O?;�~}�c��1�w�|�;+];�����������o~��;��s��D���
+��E'\�hO7�d��mo{[x����?�?����s]{���?��sMf�|�	'4����w����&c�����V�����s�=�����m�:��3�7�{���	}��/��8��B�%�.�Q�A�W������6���C_�>�0z���g��,���	x<u����O��o��������g�����8�>,�D�������Z+�qiK��'n��������!~���9s���o����J�CwB�t����J�2n+��=������n����������~�A5�����>������*��{a������z��� �E{��y
��1���������2�����_?���W^y\����Yg�5���K�%�B�~�����y��Y����'��Q�����}t����(��c��w&s�d�)��y���O7��7���(��[��1���G?�����M���&���N:�����������hO�y~��*�y;��w|��u�������'���V���m�md~���N�����w�����)~��C����q~�QG����F�s<��s#���n�����?�|jlO�kl�����/��
����x���y-|��g�����s��Kl?�c����c!�3K-�T��?����z�u�}�g�8G�|��Y����E(?GV�$I���y���R�qi�w�1���]I�w�q7���?<�i��B�R���A����J��Y/��;q�)Ub��q?�����[��������2�T�c9���g�����q���a�]w
�%I�����y��b`�*�L,�>]=���������SA��m[o�ux.I��*o�q�<���Z]`:`U�#�<�q������f�
U~�
j*b���n>k�t�$IR;+��R(w�b���._6���I6��r�,A�w������.����$I�$��%�Xb�7,u��G7�MO��Q,�h���A�9sfO������M:�b�-f��1�F�$����?���$�<��s�g�����|k�/��2�kI�
6��1g��p�%����7������tt��77}�������������$I���k��F�:bE����'�cPEb�
7_K��,����������}(<�����^���������6|=c���&�l��p0�F�$��W\qLU�M��_��|6�0����������U$M�bU����?,�3���~����}��~���6�$I�����������F��{�ml����g�c��z������w����3IJbU�4~���7�?���|:y������+^����_�|�a`��$IRT�Yg�u��a-��X���n�f���;[5A��y�{���m���T��7o^�z:���[F���h����X�$I��^^��X��w��|6}P��1cv�a�kI�X�&M�I�O�.�fs�����g���8��c��&�H�$U@U�M7�����K��Tr�;wn�z������=���
+4�I��m���!����]N�����
%���%I�$�GU�~�����_?<���]%����r�-���g>�|&I���b�5������I���/~�����OT��f��k�{��|�aa��$IRE���{(��n�����UU����.���Xm�����4Q�����Dc�e�
�/���������t�V��x��������$I��{�k_��s�=���?�|��+��VUm�j6��=����$M��3g6����56�p�������:*|=��l��c5��d��$IRE+��R��S�T�������S]Z�f�u�m����V��4���3z��<0m���v�i��l�>11`5I�$�>*|���l|�K_
�z��iS���w��f���+_��P�f���
�%i"�6_q��k��p��t�jC�M�f3c��Pe�j6��DI��6�l����n�Xj����MW]u���js�=����b5��\s���$
��}�k�}���^�������~���P�����~cJ
K�$I�����ME���^�jC��;�h~wjz��������zkx��*�L�e�$
7������_O��6EQ4�����j6$@^|���k
m$I�jb	��W^9|��/�����-|=U�q�a�n�x��������kI�,�Lc�M6	���js�)�4n����w���������k��;��C�Z�$IR�O�����sCu���?��O���O_��������4,XBj�
6]*��@��S�K/��8��c�f�j��U�j\w�u��h�y|��w�c4P'I�$I��zHN�g�}�����^z��	'���={vx>g�uV� ����W��q�q�5v�u��\�$I�$I�����l���n>���$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR&�H�$I�$I�$I�$I�h#I�$I�$I�$I�$U`��$I�$I�$I�$I�T��6�$I�$I�$I�$IR3F_��u�k�����KI�$I�$�0c���SO=��7o^��g�	����K7N8��������A8���{��Wc������K.�Xi���~��EB@�$I�$I���h<����gI��$I�$I�zgm$I�$I��[.%I�$I�$I�$I�$U`��$I�$I�$I�$I��Q��� �6���hIEND�B`�
figure-3.pngimage/png; name=figure-3.pngDownload
�PNG


IHDR
~tB��sRGB���gAMA���a	pHYs���+��IDATx^���]�}��!�H��$��8C�4�J���hSd%�K���5Y����M��M��]�p�i"{!^��Uu��M��N�$���(	7��N;�li���3�HJI����
�%/�=��s�����Yk/�}��s���������g����\��p
�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���Phf�]�L�3g�d������rv�����}�����C���{��������{��gh���wqq1;p�@Z�#����'�c���V����k����L��ZYY1���*;���?u�Tj��9��{����������]\O���8�`���|����*�;���_`���QbPl�TA�(1 8�L�#G��0��"~k�"�3j���o�x���W���{��
���ee��f�����F�W`R5����A�Lc��Q��^��B���{N;'`p��`�4��	�q=����Z�$x:�j�cX39����������j@�	��20	"t�3�����w���b����5e���c;��;~�x�U����0�G0���H���B��`x:�������Q��Am'���{�����@������s����=�#q1p���q���U������J�����:u����8p =zx��t3���'��_����>�����b{"d�z�����j��$��'|%�U{U`c�k'b�-@�D�&�7�.*����:z���������0R�A$���7����c�9i�X�?�#�����#��jP��q<��
��x�z�fYZZJ��v���T���z�j�:	�9r$��n�� v��������r��"|C�ccpk�R�;�tj��A�@����S��	E����v��;-�S	�W�\\\�>����,�������$�;Uw��a� A��Q�vTJ�����b{�V����h�,,;;�%
������Z��G�������S��
j���3,U�[
:W6kQs����C���NX�����:�Z�������Tko�����a��HG�I5����as��j�33�@���P0�"d]5{P��<j���H1��8�Su�,..��Ue����r��&|#�j@k���Q��9�k/--�Z{��O5�L�9�6����?�@��)P7����Qb���#G����Lai�=��
l����h����Q���y��mwc�Gi�{e\�[�>��x���&I�~���V�:�z�����g1�=6>���"J�����*>��Y�VVVRmx"�/����I��2��Ic������3e�|�y�C��l�;q��Vt��++++���S�M�N�l��y���������%�W��W�^#J�����P������.�,��������c�l�����e��[�x�n�1�W���>�^��N�mS7�l���V'�[�z��f?�s:�/�]�/���s.Jl����~��Rv,U��,w������2U�e|^��h�K'������}���;=&�T������$�l{��n{c;��P��F��{�+��Q;��>�v�U��=3��Y\\L��w�;�<x�r���b��XW�E~�w�o~/��R0��n�}���?�k��x����q�o�m���f�y�}�p�>�c����|�U�������9�o'�\h>������|
�����,���)W��2KKK�6NT�~d����N~��a�'��r��\]]MK���������V�>��e�H�~T��zO�FL� �a��|;|^%eb`z����������@�*���3��8��X_��1���P	�����~lw����}��������^m_C�+�u���{pP�!���q�.�0,U�^#�$�g�����1����>�	T����e�\u�R|e�g�>����Z+�?�=�#������K����~���Xlw?����1�9>�A���q�o���&E|������~��<Vb{�y���M�?�8F�5lX��
"KY"�c�n���~|�Xo��c���������_7(;����A����Re�8�12���������v���@E�~Jo���~
x��>��g��������0�?�j��Gc�|?��^l�����������>��E�8�UA��0��T�.��kv������<��=4Br@*��+�Xv�
����}u��U�$������`���@�d�p�WVV����VK�����w_���e�'>����}����m�TZ5�=���vwbT�?��n0���b���F���T��
��n.U��o.�V�|k��(��������c��5�SGl�N�w�ilg���8�;��x�Q�����1�8N�4���R����e�D�cU �7����QcqN�+�8O:���B�!����T+������oU�� U���a���d��=�-�b'N���n�veee%=rp�����Q��F��=�����\>��g�������c:U���R�=T7���D��6���M��:J�����}W���2��z��N�Ws��YBlK�66J�zu���g��������=��{��u�����M'������1�Pg��t��u?�(�*��u��A���h;��(+���R����*;���v�+_�u;9W�\���|������%^7�9�5�-_b�T�O���������j=���`���F�(��p���T+v������S]�H����:�������[[[��C�9~ee%�����Gy�
���Q���:�Q�(����4������/������wU��k���������c9f���������d'�f	�����(tr����P�����j�N��AkX��c���u���5��s��ww���>Ob��F��%��1Pt������Xv�:t�P��������������g�L�FD���T�M�=1�r����W �����b�l�`���A��Yu:����g����j�k��[�����'u������s�5��wzN�����N�Au���sLWm� E�L/>�����=���zg���V�{1ts�k�����������A�����T+����e���T��
K�~�������q	���>�Q5�9,--��`T�����3��b�w�aC<�j�x?f;��@�P�����6j��������
k�?���N����U��N���]v<�4h�PuL�3D������<�f�����q���������;	5���H�8Ov��U����<e�������S��wU�j��?�#�`]5sJ���;���������u�c�`/�?��U�����3
����VN�(]W/tn��[����t'�t/��U�}/C�U�t?����
���( E k�g��t&���B//�z����O�k�b��������wS��{-��n�w�����	�E�(�_gP��g����)8�k�n,2�����q�=j��,�����WA&����V�<���e�r'����^���|���0C�U�������{��a�Q2���iG
r��Pu]����W��j=;
[���������n�w�4�7�W��`@b s|�#gffj�����*�z[[[���d%��1�����A�u��@�Q@:������^���L�A�o���X�������= '�^���8����q�0��f�GH/�<���W��j��*e��A�����x�V�w�N?c@�J��D�E�u���(������]W��}Qb;�D(i��r0l���^������?�A�eFi�U�5z*����\:�����x�@a�,^���(}�����Z����3{�x���X�a�
b6�^�V�^�������~��V������O��#q�`9m�5�1JH�a���0��6��$����l�z/��ye��L;	�T�u����VT�WH�j�����;�j��
}���Qw��x���r���>�wI���M`g��p=�A|���t�j�~?�XU�F�j�|��i�:���}��TC&e!�n��e�uT�c�f���o��:����8���5A�X%�k����R�}��00N���5��cR�O���4~��Z��S�8���,�&����U��W�����#G���)�#!185f;:u�TZ2Y��F1�off&;x��vd�h�!�
������3��X��A}��rLs���s�j�~(�<ql�{h���MP�����Q
���zW6k���V���ez���*pe�#���Y<q���LG:�gha�F�!�|���?��0
�	Z���2j������Q��0��u���
C��+:�{U�Z�gp'~����{*J.--���U]��>�nT�������H�k�d�8��K��j5�6����$��1������^��q�~c��5F�fU��^����1���`�	@������P/J��%�F�8
��Qs���4�!Q��	�~L�q:��=��aU��������S�����F��	n��>j���:6z�j6�QWf��Go�������]v���
F5v���T�>j�Az1�Q�>|x{f�2U6�LU����L%u�U���R�:�b�v�w�(���~�>*E��W��"`�mmme�N���i`.L���DY��MY��*�����j�:�j�a�gKuM����
5��A�(46qo\��Q"��t�
M�.��*(R2�dE�� ����\\\L�VU�����U�*
���{F���J�h+
�
5�F�A��!#A#&M���p���T�=�e���g��x�yGUh�W30�ZUP���bXv�y�{pT�6n���z�f��s�q��~�2q8N�>�#�P��4�#�L�Q
|L���t&���*<1j3����i�mTuS>+V���,Y5{� U�z��7nT��RuM9~�x�U���Bu�F����j�(���:��9�Z�^������_U�b�'Dx���#]�������x�	��\��Q����c!�6���w��f�����@}�G@����0��f��4�c�}�V��1������G�Y�:��333���c��� �i��jU��������kC��#�o�:��^^S`�	-�J���Awg���hQ5��[��U�~��5��>�~��U���0KUU��W�����B�����s��#���������t�����_{�]KKK�6|U��^�VW�5EP�>:��A�uO0J�
�8�
����
��8Oa�(������P�y��<�
z��L:�t���Tk�*TG�g������?�j��R$�W���0\?�z�kF��c�����ko�@������A_�����\����a>Zc�s����T
��38�10�L�KU��_��I�Qb�}��n�~��kF�~��I�L%�vzl����1��S�n�1�S�a�A2z�;z�h��WP��}�\��q��]����}�fD��[e�A�8N��)����H�	U���2�0�
�c���
c����C����k{U��_���)�P�z1J���S����wC���N��h��xN���+�A�^��=��7UUP��}T�*���:���xi��� ����EFu����('|t���
kf����1C/��iv�*���AU��j��~�������z����j�����vv�8����j!b�k����vw�zN�f��o��PG?f�
�
r�^(��U�u���"��o���{�	���Pg)��0\5x}�3�L�9���;Wu,G��W�-�S��3�M?U}�U��Nthg�~�+����u6	�R/B��~��^P��}R'�����fU�]��q<W��7e����� ��~���:�{���D�G@�:I{!)��EFY?^��92mA�IPgf�^n���0���Be���i���!hu�����~(�:^�*pV&�s�����������'U�[��n�M�x�U����z�P��e���u{P�f�����UA�8��~�	��96�s,ZU0 ����t4�:��I>G�)3�8�����/--��p�	U���0�W}�}<�a�NH�Y���6@A��	���5���b��:��n��~������3k��n�s����s�����8���Y����2��'|���x^?������:������F�/
o��8?�1U���]Rn�-g0:9�;
x���\�#�3*�:��8W�u]������aT�X�T7�b���o��:�%�G�xl7��^�B�pD'���� ~s�9N���5�����^��a�tf�����Tg���u���(&|�;������<���}�V�A�dp|�o��K,/;?��TTmo�3�kC��g`V���{,���}���|^��y�V�����3H�|�^�G^,k�[�~�LT���N��!>��'��4J�Z�\�#J�c�H��W�Q�P��b����F�s�U�r��8N��3�'��y�����f�~?����@�t���T��`U��	l���>��;��l`oc�h���P���U:�X �Q����N�zc�����qmh�>,//��������|���Q�ou�a�����G������,bY'���V;��6�5��Wb��"�Pg��������Q���~�.V7�����rU����~'�A���F%(U����	mu2��y@o�:bl��2v:8�H�oTCe:�������������8���<��������E'"0���ZZZJ������A�NH�0i�J�}?J�������G
U���
b�#h%|���`�4�<�l@�@��18����FQ7FFK{�8������N�uOZ��������W����J�����B�<��>il����x��y?��\;U5KU�A�~��s�h%|T���X���V� ����D���c;��n��\�����mX���3����1�� A�/�;�0D/�����h|����86wr�����%^��o��,H�^��!�:�����Il���S��1��J�gV5s� �9�Gq?U�Wnx����Zz5��1`�ll��@w"g�4������;�Gi�#��(���N#H�8�w�q����5<��(������t�����e'��Y����f�v��J<��Rg�
���a_��7�}����j��Qz�����V,��(��r����V��t�1 �*�Cx2��}�A�V���������w���7*�� y�kF�������?���4��N�}'�W���f3i��t�o"X�n���5"H�Sq���1��B���v��}���8�����qL��6?~�4�3��QGY�-f��A�������}�fu���533�j�G��\�������Tku���T`bF�����1���v|��$|]��	�9�=X8J�L�_���S���C�R
�a:u��v��������@9������27;t%�G�������b�{����v��b�P�������3 ��Je�`|�}���6�#�����T��c���Z��_�����J�6�#���'R�U��z5���#GR��`|��G�O�N�k	�@������j��?�j���Q��!%��S��f7\ZZJ5����zW����I��VVV��V=1yyy�4x�Y70�}a��wu����:u*��G�v���3���S���'��-�(0�C��Q0(FW'���B�v���#��B�$x�m'�#���(>�d��d333�V��y`�\���@>q�Dj�O���0����R
`4@�=z4[YY�����b�[[[����N�����h>���13Q��LH�����`r5����Q4�=�@��9s&[]]��///g�O����5fG8t���`4~}�����|`��K����PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���Ph���u�
0�����E23]4�k�}3e����hC�($|>
��[��V�����`b��D�����2j��O�����6E�!f>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#���PH�($|>
	����B�G@!�#�����<y2����.G�IK;���6��(����3�Q��u���/�M�D<'�3��%�=b9�A?�4�n��Z}��V�"�\tX<x0��������S�R�Z<����������b��������iI�q]7��>o��!��=��8��G����Q_D:9v�Xj�w���������7N}!�m5�1<e�������X��#�������N�G�tz4�<3��`���7E���C��v�tz����t���,�y�TQ�u���V��S�b��f��q�ebY��%��n���T�,�����~��~��n�x|���h(�#�q+�y�#J
N�Dt^4??:=�wS�;���|����c\�
���
R:��R�3&=zt{y�c������#�������A���Z����`@(�.+�q�4?��s��,����n����������m�#nr�npHQ�(/�������w��+�K�����Q����3�%�/������C�|D[�i�N�(���t���Tkoii)��j7������T� ��|p�*�o�j|`pVWWS���8�"�����r�@���(�[�g��3�Q^�����S�`�
p�u�u���T�<Z\\L�z��Y��R�F,n�0\�/};���T�`zM��B�S5����Zg��K���Tg ������4�(����Q��w��^�����E�M�<xkk+-�L��3�	�#Z9r$��n���Rv����Ko����H�)4������j;�mO~�hF_~6�Ng��f�G\����!�����o�:;�u�u4�H3-#4��l�����mB/n>��>�=������T����j�]��`N~]��n�*���=�Z�k���N�D��`4���(���|s���1@��x����S���w<���YZZJ5(v���lfff;p%C��N	�-�z��|���S�Nm���5�����X^^N�V���@O1rw��g�����l�6#uL`�;vl{V��	vB��k��{��	!����7�1#5u�M�����+1�'nn�,BHG�I-����e�j�}�Q�Ct,4�D�C���N�f���aee��PS�u�!:E�=�Z���Q��K/m`��������5���a��!��=N�H�|c����N�������|�(��8�A���;�����q��K
����<����)W�b��)6�w����~�u����f����g/^LK�a�����Sm4���2(��������x����J�_���������~��H��O�����<�>�R�i�{mna�=��3�7�����w�y'-�!f�n6�3(�*}@]Ewe���o\J�V�}hw���7�x��`��oD�|��Q�������������\\YZ�(.2�/f=j�T�)���"?us'�6������7���Q�x����z�������`���;��������&��={6����H5����a�����0��=����pSc@I���J�s����8���x�����+�����/��k�v��k�����/d_=��u�{��~���`j�}[�5N}!�mw�y`�=�����#��{�s�8?���������7��������]���S��?��G���j�U�?vqq1���������6?>�KU^y�r��Q����jS�}�S�G�E����,//��U�u������C�����n�L������C�1��7��:y���Fq���/�����YyVVV:*�����;u�T���N�8�jYv���T�L�;������V��|C����=z4���A*�0�3P��f���C������6f������t��������NK~�K����3ms<������u�u���j�[�S����3����*�Y��9~�x�]Ug�k%3���K5z��Mf�c^�L�n	�7������?&���q]7� 5��7N���J�+��h����������jaV�`'�7���&3E7��:������#G��V��[���������%����
 ���{����Tho}���G{o����m����>���(UcYB�x3\�-�#�*?�����<�%���wx�S�N�Z�q]7� �g/��Q�k4DG����x�)@k��G��{S
����Z������233�2���G���}����jWE�Ff���(��k���?��q]7������_�����n�����������T`'�&1EcY��<Q��yn2�N�w���� �"��N:<�u��35�g@j'gfg����9�#�^�d,K0��^>b �#�D�F�@���hl���c\�
0H1�����v_F��X} f<:��������;�u���f��z�1�������i�e�~�Y���w�����b0y��9�]�|y��K�w���P��s����t���g33�S
����=�jY���G���0�CFm{`����z��'�+W�l�����l��]�u�f��3?���e��/n���>������Rk�����q��o����=�����#��{�s�8?��������K�����l��=�u�*�<���C�|te}�B��z�����3�#�+k�S�����T������}������S����L�#�ckR����5���]��3�#�c����G��{S
w�G@��7.�Z���}��;�#�#/��v��o�V+3��>:��y!�Z}�����n�!��q'|td}�b������j�$>:���~������L�#�������{���j%|�E�����.�Z�����>���S��G@mk��Gs�R
��G@m�R�����T&��P�;[[��f���C�G0q���Z���]���Z����]��>�I#|���q1�Z���K5`���o^H�V�sf=�I$|���Q>�>�I$|Sjff&��=��k���/�V���}�L�#���SS�U���cL"=@�����j~no��F��T>���j��>�)����j�=������z+�Z=4k�#�T�G@��Y����9���=�L�#��c�S�����T&��Pj�d���Y�#�d�G@[/�z)�����������L"�#�R[[[��^��G�Cv�7�0��������j�f��0������7��|4?'|�N�(���wJg>>��'|Z�h?����wg��}sj�J�(���>|d�#��G0EfffR
�����Tk� |SA�(���>|d�#��G@�/?u1{��+�u���f�����L2�#�����Tk�`�#��G@��6_M�V��G05�����g>��>�i!|\���_��_|;�Z-��M5`�	�(��������t���&��p������j~v_��@����S��Gs{S
��G�{^x�����H�V�sf>�i"|�gm�B����]7ew��>��i |�g}�b��2�L�#�=������0-���m��q9;�����j~��G0m���m��g=�c����>xSj�B����y!�Z���M5`���6������0M�����w���6/�V��Y�#�F�G@��y1���Vj]��wg|��������o\H�V���G0��������T�����o������`Z	������e��q%��53�e�G0���`��o�����{�f�w��0m��`��m�������L������`�	�{��7�^~;�Z�����L���Y����-���w�0���`��o�=4�7��i%|Sl�d����}�L+�#�R��x){��7R�����L;�#�R���g=������!��i%|Sj}�}������L3�#�RkS�����T���L��.��}������93�G0��6.�Z��n����}Kj�L���zI�hav_��N������Tk����T���L�������������`�<�����K������}]6?�/��i'|Sf}�b����<�'|Sfm�B��>�	��Y�(	���j�G0U�������^J�V��f>�'|Sdm�b��z��{��n��Z�G0U�7.�Z�����p��L����C��>�)������{���j�0�/��>�)���������9������J������Tk5?�7��'|Sb}�}�han_��O�����/gO<�Zj�z��G@�#���S��]���}���R�}�G0�7.�Z��9����`
�o���������`�]������|������b�G0�fffR
�t���������]��5��%|���.�Z���}��J�&����Tk� |�>�	��Q2�������L����T���g^�^{�rj]kf&����Z���`��m\H�Vs����fR���L��6/�Z���}�PL�&�zY�hN�('|����^x���j�0�7��	��Z���j����[�[o��Z���`B�o^L�V���R
�=�#�Pe���9�#���L�����~���j5?�7��>�	��q!�Z}����~Cj�'|h}�b������j���`=&|���L�7���}���������PN�&����Tku��{�{>|sj�>�	���~����}�PM����+_x!���~1�w������_���o���@��f������T�&|�����������O_����N����g����H�����Tke�#��G����gO���~�W��j4��Gq�"���.��=f>�>����'�#G�d333��X~��������U��k����n����rg��j���o������k�09��!����7��Q���?��g=Z�5���#���;v,;}�tZ��X~�����u��vPL]�-�M�����u�<�v>�Z}x�
�0��o!�f��!���o"��c:�o)Z0���G�sf=:#|����k���b�(�mbb[�l{����u�=��S�����L5������+�D��P���.�Z��93��y�l]��ok�e�(xt��������uu����[W�Xi^�����C�m��X\\�8�Z����?u�Tj�n�C/�=,+++��+W��1�i��]�u�9w��{���������w���T��x ���{�����;�Z�������������������Q��(x�KKK����lG���}8E����t�j�����������v���]�{��'���������\+�4_=�3?���j������
{���S��m��8����������|�����{���9M�����Fv�������\�g���:p�~�jE�!���GD'���>�l����:\�����w"?��l��nw?�=L�G;#|�3����s�����/-��������c��Z��Q4b�t��-�������(�Tu���S��qaP
P�@P�k������>�sO�����������S�N��[�����K?�����������S��?����\���,��[&���m�H���Nf=�R7�o�����������|��Tk����T_1����$���A����Eq�2����v����rk�R�����T�O�h�u2��!?�%�������b��L~PM�PS>X�n`N?�
�w��W����zj�z��;S
`|��AU7�	�����S�X���o�L������y�#��GS.�D�0Q/g%j���u��j��n��_��E�\7P����Tk���n�����0��F-�7��cV�~�J
������}��������9��)��([[[�B6E������������T����{�$|����To��}2�N�����FY��_�R�im���G�|���{��@}�Gt�yV�^����;�����s�@{_}��l����V���j������v�@����>|4?g�#�;�Gt���#�vUU8�y��cc�L�gff����vv�����u�=��R����]7e~����.�w�|c�P6+���j�]��uD?L�o&�k��&`����f����Q�y@Js�(f=:z�hj���2��i����R���I�'��~�{�lmm��������jqa�L��'�}&�w�lee%��E_Nc�S���X�7BH0~.�~9{����V�3]>���]o���8q";u�Tj�R)�R$��k��������eO>�Zj�:���0�7��~�(���D��l�����/�!L�x^���Q6��?pC��;oL-��Q�,��[��(����u�Qb�L,��A5��yt��Tk�mw��}����0����S���C�*�G�D?L��4������iA%3 ��X+	=�?��#:���(�~[R���,1[���G���b�L,�A/�A.������:��n�&�k�S��#�O5��3��He}'E�6���0�~�X�6������������@���h����E�5BH�HKKK�����21�%��,�������s�m�v�����$��3E�E�I� �H�����������D)������W�������j^����w�����b0�N"x�P'X���co�]uc`M�lI���8�,�
�0�u��x?W�\���}���u��t��7R�5�z�\G���~m����]���5�����q�d��qQt�?d����7j��4���J����1>���/o��qe�[����I��g��>��^N�k�r�u�_��;S�n5_+x��T�[��������'��v���]�z��'�;G���^�4q~@{��K���sss��={���Ug��M5�<������t,?;Q�z9;���b�]����j;��Y����^i�Dg��(��YiV�wEQ��4+�{���|+=���go(|�8�n� ���D��O������=�����(�������(Q��]Q��<����Q�j�����tV`�	�����4[]]M������y�%`r=�����������g�O5��P������C�vU/�~��{���}*�~������iPZn���=T���<x0���0���GSk���t5���^YYi	,�S���\�(�mlL3��#��f�C_��W��N��'>Q8�$��;w.������O����?�Z��{���7f����#�&����)����2j��x8r��5��^3'O���;�Z��������'���\��]���{�]�vm���V0	�����7��z����[�>u����[�}[<�@���q��o����ct������+�s$�����9?��������K�����l�7&�fg��M5�<�������X^^N��;|�p�������...�Z�~�{����(��tV��]QE)/�����~>�����w>g�@/�@�~�~�����(J�4+���(J�fEW�Q/��y�m�h�u3��s�
��tV`�	M��w���������z��DT$����������k�W�}-�Z=��?�&K�0������Z?�f����y!�Z������Np���)w����v2���C�R��8�f<�]:��m�_w8q�D�e��c�R�3�fT����b�������;n�����d���4�f��+����P��f:
6��D�dF����Tk5?�7��'|�{:	��g�Y\\L����N�M�u=z4���n��4�)
5�����VK�Gf=�I��,���+�n�����`S~��7jF��F��G�G@M��;�v���,�u��l�v�E�������Z{���5�s�@����zv��WS�����0y�70�����|_���R��/���������0:����K�N�V�f�z@�h������U,
	���1�����]q{�����,�
����	55�s����|��Tk���7f�k�0��&���BBE}E7������FC��G������v�@����\��e"t���k�BQ��'�pP�����P�����C�4������	�N�J�b�\7p�G���Z�G��j�+��F?D�=4D�D�����������o<S�������[���j��go�E���7R�*�Gl�e�!�c��m��b�I�����s��61@�j�E[��;
�i^w�V~������uW=����g^M�V�w�������CD�C�"�$�����n���N�_wc�yU���l���9�#h�����~�g��ZT>b[}�Q��c���b�J����-u��s��"��N��s��U�~��Tk���7f��w_jL�N�!B'}1�s�������#��og_{���Y���@;��_����k�E�#���A��`�hG�����xl'��������x|����k�;3J��������jqa�L�F?D�5D�C������ET�;4�}�S����uSv��7���o������o�u��[��V�`��9s&�|��v=���{�@=���{�w��������O)�:{�l�e�<�=���|�7��V��-d�������*�f��m�:}@]O<�Dv�����}������k�����q�?��F�W�K�k��e��?�����}[��8����������|�����{���9M�����Fv�������\�g���:p��������|=������#�FCQ����3_z1�Z}���J�o^H�V���j@�/=��k�G�'|�P���O�V�<|G��������_{5�Z���K5 ���z1���2������o��}�d��#�O5��7�����{}v��nN-���Vv����ZtC���{�d���n�>��}��y��R�����T�7/f?�S_N-�%|��=��S�����L5��o�����L�'��z���|<{��;i	�>`���J���_M�V��S
�����l�,|t�������~�'�^~�RZ�N0P_�|;�Z�y������m�@�cO��]���Z����]���GL�W^��}�s�g���fZR�~��S�*�G�om��G����L5����z4'x�t�ty+���=�=��kiI��������}�E�#���\�6�y9�Z=���T��ZI�hav_��t����d�>������;�F>�>``���v�����������R�"e3=4k������W�_{�[�U���������&>�>``~k��Tk��`�#�2O<�Zv����J��4�t����W��[/�V��?zk�g~�m�.I�����^��m<)�Zx��T��c�S�U�zt�C�>��/nd��_=�Z�>z�M�_����o�IK���G�l��;�]����oK-��o\H�V��f=`����������R���}�o�>���:%|�@|q��Tk����T�������������g������V��n��<����i	�>���{�����/�V�G>ug�P��/��=������{S
&�?��������Z����}0{�}g;%|@�=��R���i�����R�"��g=����~������+_x1����WS�����f�����;!|@�=�v>�Z=��?�hg�����jan_��d���V��?���j����g?uGj�S�G��s/�Yzg�G��|�*���G��Gs������#����5%�����9�����[b���������O�����7������������/��j����>��LmP��W/e_-�Z=t��T�_"�!�c��e�O�NK���o�"��0��=�Z���=�]�����������;���#�R�!�����Tk���?�hgm������
�G��1���E���"�+�0��}����.�~9-)�'~�G��y��{�G���_z3[�h?8����H5�yl�b������j�C���3�8q"���z����l/k�����r�e�����_�nY\\L�/�/^�>���g������~���?����&|@���z���{��|������
�#�a8s�LK�(�FG�M��8��,�H
j���<y2�z/^�n�m7o��N��?�x������b��w}0������E?�7�k/�Z���Ty��;��f���f���v���T�*�GU!����T����c��s�A�C��������{���y������~���E���;���|������l?0�����@;e���7�����-�@�5�}bF�:��cN�8�ZW�J;�_���b�L���/g����S��w<p{������������G�t>�Z�~�����@j���F������T���=����j�]U'0������V��Rj{����_��������>�/]k�����@��6_M�V��G�3
�lGQF���r�]��	`R���������7S��=�y;xt�����M������7�/=������S
�2��e3�M5z-f:u��v�����=��@����T�g��Z������/�����]��0�G�����Tku�M�e���H-�����7���Z���k&��3��(j��(D�i�N�>�jYv������<y2;r�H633sM��1s�8���L�w�-en�q�v��c�)-aP����GK�G��u��:�7.�Z���}�L�0:"��:|�p��V���;v�k5���n����q�K������Tj{��A�g��Of���[�I���z�[of_|���j���R
�2�=U>2��H��z����j��@S����!	 ��������'R���|�'�����R�A>��V��~��[o�.������X���j������"f��zt������X��'������������ ��G��g'����j����������0�S�)���f��x������N�V�f>%��Q8u�T��L~6����=��\A�X!�xL���I���s�d?���S�����������+��#z�����~��WR���goH5��o^L�V�����w��V<�P�,--�&j��D)�����c��3gR`������g>�x����������-~8���wK���ix��[W�\������v��8w��{������lf&~J���<��O���Z��������{�v����/K���0�CFm{`��@���/o�c0������yO<��{}���w�>Q��k����������7S�Z������#���p���T��������*��G�g1l1�Q��N�8�2[��5���}���u���64k��1F����r�F�7�u%�>������IK��?����}�-��;���~�bE�
�Z�gV���Z�O�^�jTy�d����}��0�r�(,..��U����6�b��(��fEW�i.����(�\��]Q�Y��z%���/W�~��7e�w�\����#|@O�����o}���j�)�#�Z^x�����H�V�s�G�6���������ooe��+��/_��������?�xkj1*b.����4L����r���������]����s����~'�������������6R�Z���;;��>�Z�,f������2j�����3������1�f����u��'�x��>����O�(P���Q�/~������WR�Z����'�;R�A9{�l�����q��oC^�K���f�����F*x��?�G��m��y��G��@����{�����s�8?��������K�����l��=�u�w������z�����O������S�?b,h�~�bE�!f>�'];�j�<|G�Pe}�B������jZ����S�F2x��:�bp��(��fEW�i.����(�\��]Q�Q~���\<�]�~{�����������#v��W�.� X\��jTY���j��g���T<�x�(�������j�M��C�{��|�'S�Q$|��=���R���7���{^��������{���j�0'|0h��<yr�����Q:	 �s�:t(����'��/�V���o��p�x�(�w��G����y��T�����Tk����c�)��a�x����jW����Z����T�����������?Z�Fj��n�}`����Q%|���t����^I�V�,����������Y�-<:q����G����v��c�j�~�9}�tj]
L�����e�W�M�b�n��}�?�d��;oLKe�G���k�S��-7���{^���������9�#�A:r�H�]�a���f�* ������j�����\��SO�V���f��G���[�F��;��_L�V�3�����e�m^L�V�G��<sP8t�Pv����K��P�mff���"x��\���uG`*<���gP�����������j/�G���k������~��+��jq��Gu�m\�._�J�k�|����?vkj�o�Y���c��*���i
������Xw��L��T<���S��?��?�����K�����������R�q!|@�];�j�n�q��@�7/�Z��f���(f.���'��j+++�G@��u���G�l����SiI{���D����;S�q"|@��|��Tku��;R
�:�7����������AhT=zt;T!��LH!�������K����/f����v�+_x!-i���?��?�o~(�7�Gt������<�rj�Z\>�����Tk� |0P��U��P�X^�qy*����F������`'N����|����7^OK��S�?����#��8>�+�k�S��M7��Y��ZTy����W���Z��g��
��/md��}5������m���?�Z�+�#�r��/�Z+�#���o^H�V������I-�^~;���X�~�_>�������c���C���8>�c��x)���/�V���j����>|�0g�#����V������1C
7��+�o����{�|<-a�	�������t��l��GY���j��g�����+�f����Xv���iI{1�����Og����HK��Gt������*�G33�@��^z3���o�V������s��Vv�������o�%����C�g�S��>xSZ��>�#/�z)��/+�Zx�]K:��q!�Z���[��n��Z0_}���>������o�%����7�/��'Rk���y!{��5��"|@G]{)�Z�x��lqA��e����1��������|����>�jZ���w�����|!�#�NKF�M�}>���!{���~ZB�#:�����������u3�@-k�S�����T�������N��s��+[iI{�=�?���t�;>q[Z2�.�[�n��������l���i)uP���^�~���J�V�<l�#�ND�S�x=�Z-�����;���/������+��%����~4��?����[v�%�����|v���?����i	�>�������p�u�3P����Tk���7n���8�r����b��%7%n���]�_��d�������~��W�/��>��G�^J�V�,��]w�LjP����Tk5?k�#����/��~�'��^~+-i�����~�/<��;����d����.{�$�����������ow�t�l���9�#�ck+�~�g�e?�K�iI�#��C��������oIKF�����e���_I-vB��ZV���Z��\�=���@o]z'{�����������_�~�������o�%���?<�����O��xx���;����Dj�S�G��hI�hqa������������v�������]d�����G_���iI{w�~}�W��|���[IK���g~*{���Xj�����M5�P��k���X���#f=���F��Gsf=��~��>����|e{F�*������~:��nOK�����w'�Q����Ov��a�F!aT(�,����Z��v�����A���W�j�+��v����n�.��j��@DPkH��'�}O��&'2���f&�L���w��9.�HB��;������{�;?�}�f��+4��4�(2"T��h>���R��GS3bu�LEu���c���������o||�$�Gj&8����R���5�W5�=�'j�466j]���������d	��*th>��`�z��Cr�����{���t0{���:I>~}�f�G�?$u��^m�{eO�l����|A��Qu]�c�a~.S����h�44�j�YDx�L���=�����?~\(��4co������g��9��	u/�@����F�b�~F�']!/^�|E�����e��f6�,����_NS�h<@O=�\�<������#�xd��M���J��_hd/������������?h>8�Rh?�(?7�5n����|��+�?G���=?�-}��f�}��Ys����T����~�S��Y�9>z�l��]3��G[�u-�u�}����d]�{3����7��]���X{��)���Ty����	.5������F�bV}I����^{M3������>�+o���� o�F���3ur��I#o4�_Ol8*_���5�h���I�������)I�	.5|@��F#{�����
�(����Aw�|����~�Q~n�D���e����������c%&*L#�YUm�|�������i��{��o~j��&Fi&����~i��;��E��+��n�:tH3�	���j�[��=��G������GL=��v�T����K6�.�����/} G��!S3������a��4rp����X9q��&�S4,m�}^W��BCd~n�F�G����iq������|���r�\�f�e�����!+���L�������7�����U���'E���n��:u������

���HT/)�u�B��<_��7&�+?�]�|��b��-�5��x4y|�f�O���Cwo��^�������Jmm�f�M�0Af�����N1��������d]�{W����������9��L;�����qv��t��N�C�����4���F��,�y�h����j�^VV���S��G/�w��
����
W��[.S�`�����{o����R�����������i&��J����������#�|Mv���2l�0�<y�$'���_4CT[[��o���G�����0���=��� ��|����|���RU��{3'&���.WNM�Lji��������5a#4T.�_����6�!))I�-[&qqq��?h>tR��"[
�5�6��G�b6�:Q�����l|�%�O���-��x\3��� M���\�<L3����V���K���j�Zk�pys�}Rr�Z3����d�����?�E��������|���e��XG$D����`�{��3�����%�����sS�|��,��Sk]������u�X�>Z^�~���X�{YYY�x�b�������#@'�w����y)2,2L#�cO��F�i���
���Vy�7�\WsK�f�M#���ty�U�5��j.H������oh����ym��r��A3��M�&W^y�F�	��o�kh�������sSt:5e�|0��*��O?��k��/�!?�{�LM��Lpj�:'�O�%��vk������17HmC�f���3Gf���z��#��

����������������&{Wi�mZFpo@��j�q��G����Z�8�}���O���0�����R������>�X;6z���T��",���'k���#`1�H'�w������d��b�@�SZ)���;#b������`(9_�(���}��_�h����h����r����	^-���������A�X+�\�$^�����hY�t�L�0A3�-4\�K�}����������f��
C��o��;|C^�S�g�����;Sf�$h&x��?"�ONZ��h�����8n�F����d��e2r�H��7�|p�RX�8�x~n���/���i�45���T�����n�������}�?>�#Q���r��T>u�������!;�n�c����7f������8����|p���~�Q~n��D�i�)���7�����5r�v���>�g�����/��w\9J3����~�z�s�z��f��D$������4������k��F"##5��@�@[]�������
�*:^-�u�u��55=�=����WN������9lP���]3V�{W�d�����|b�T?�9i�<�o���k����$���6m�\y���/�|�-�e�������\������G���=�`V��*��-��=W,m�[t��!_����;34����)UO�%��e��v:6G�M�Y�C�i���9sd����Q�8N=���,1��4������7�|��k���r�w��_?�g���G�8S�
���������?'m���v4i��1�i���e��2y�d��h>�!���U���?Q$2B������8]`�y��c��'{���:�8���r��S$9>R3���d�T=y�����S��@��Z�����hY�l�L�0A3�/4�WPX&-�����i*���������MM��`�9]^/���������q��#~6O��x�f��C�H�S����z�x+�\�S���^RR���h����A���BBBt��nKa���]5-Y���*<l?�(k�p����v��O?������f���b���3%/+^3�C��M��Z�4�YKh���9�8]3����j<��� ��B�a�M��ew�F��1��'����������`���J��>(�u-��"��<Q����6�K4�{I�~y�[�������yu��r66K3������k����H4�f�5�X��7��&�
����~���Nj���zS~���f���N�G�8KV�����q��R���y�=V^K��TF����i����W^��G0��O=�rZ��F�k�W5��3�y���#���������)��O�vw��q������Q��<j�?(��^���3q�d����>��Cy���#3f����#`ik�?�COSs�l�m�|� /EW��=�U����2LF%Ei�`S]�,���y��%�q��%_��er�u��L���o�����5�v4i���.i���e��2y�d�h>�!jKa�4��7���&�
����RW��e0� Xm�A�|�
yi�y�8[8=E~��r�������U�|�f����f��.����id/::Z�-[&&L��G0D�O=2����p��{��O>���������W~�W��7h�����!���)������?��'>-�gi�[���R�r�F����\�G#G��	�G055��&����#��ol���G�2�|LN���/�d�<��1�8�3\����r��c53x4�%�?�����s�xk	����#��k���1c\�Gqq��h>�!���\�Z5�� /EW��=���G	�#$ct�Ft��~N>��7e����q������{g�����LS��G��g������V�(���Y��fi�^VV�\s�51�&C
&4��4�����$.&\#tG��J]y���W�`����e�3���E3����d�����������r��T���R��i������m��R=Z3��M�&W^y�Fd4����*�ey�g�@��=l?�hZf���L}����?����9���_�!���jf��/xZ*~�i.}]3���Mr5��w}���9sd��!��|CLAa�44�j���#���Sj?���#�����O��?�%�'j4������o~j��M�����RvT���[j�?�{S���������T,X �'O����#b��������$>&\#t�9������P�����������O������f��J��o|b�|d��
�?/��,�7i�Zmd��6��R�r�f�
6L�-[&&���`G�!����G����@w�9�4���#�@���r��w���w�i���Y���g��)I�Z��I���H���!m�
��v2~�l�����t�L���$�^{��9R3&4��y�yihl��[~�]����:5��
��4���������6>{c���'Ilt�f��7�*?|�4�yQ3���Z&��\'-���������x��<���#B
��SX����@�s�JW�r3h>$�����g�4r6y|�<|�y��4�m�UR���J���%mu��V=Z
�?(G�fj�YVV�,^�X"""4�`D�mm"[����%�
�u�t�\�n����GKk��}��T�5k��i8��=�e��X��{��>���?k����Y��#���������W^���G0Dl�]&��-y[���+tWai���M�+�"�gz�@a����Zlt���'�go�������$���T��?����f���F����d����q��v����;��0Dl),����S�$a8��zj�C�QnS����K;�kd���y���d��T�MER���R��7��W><]
2?"'�j�YFF��\�R���4����#���

���s�u���s���hZ&�G������?���E3R�?1UF%Gifp����T=u���?�{%)W��������8�3g���?_""8y�����-�����E#o�4���
r��A#o4����d��EY�8v���u�F�C��R��[�~�����';��(Sh�Yrr��X�B&O��64����LW�.��$Iq������O=�0*Z��8�` U�4����4a!&*LV��#������n�#R���K�����w&.G
�������q���#+W�����`0��^��[���		�t������Q�����QPX�+oL=��bj
���Y�0�	�����7���iL=hk�9('��id�4e�
�(����'U�}L���S�8;8�j�9�i���	����J����f4�m�5k���
6h��_�p��q�6����_�������\jZ4�6?��#��`j3��`j�Lm���c��(d~M_�@���U����I��@z��%���Y�����78&��o~B*�M��������ym���$��F�Q�F��U�$33S3�h>��i��j��bg6���lcg���������ew������$�q�JG�W�Q��s|�q��/��7�vU��Rr�F#o�h>0�|�u9y�U�����j�Z��J������O3�N$�JA�G�,4Q3��M�&K�.��x�]C	�G�l<Z�v�����}m��IV�X��m����x@W��}����������(?��G0���L����k�^���s|�Rd��Y���{�dOi����&F���4@����k��������5
^���Z*~�4zE3��$D�e�_v������]6l�,Z�Hf���%4
q��O:lV�^��v,����{5�t�	������>������TPX.��-y����(�1��L���������e��gc��Go��%������e��
����:���EY3���u�F����)�z�^������V���=F^��y91A3���/�V��q��iC
�GC�������OW�<p�����~�w��<���@���~���I���` X������sJ��A(���j<�`��
H��nOi���M��������WEr��I3����#�	����q��������}/i��������[��%B3�f���:�2::Z3�h>������s���$c�ss��%Kte����n�K���o[
�������
0P


t����a���7n��7�CR�����s�
�=G���������g�d�a�c�/�/[fd'h\�j/J���I������Z��k
&�r?-{�������xY�t�L�:U3�h>�L#QG3�/M6������1����
.�zo�?���\j�[4�����+�@�?��6����Us�?<��<P��g��CV�����Ie��	�Yc��
�����l?����/+�]9Z���X�w����7��g�#g�+�����������d���2j�(�`���h�3�5�2#�|i��b�)�s���|��������S�f�$HJ|�������ULMf�����Lw�(�9U�4:���5"`(�Xm�x?<B�BC4@_{i�yyr�Q��-�K�O\��Q�hk���?= �����V��
rW��Q���Dj�1v7w�\���+%<<\3�G���>�6��d���)�����@_+(,��7S@'�ID�6uw����h�.V�7%�F�
}���jY��A���)��o��(x4x�}���������q���/����q�����v��|��=�������]sNOo��0�����=�R]������4@02���a����4
�3
���#���GU]������M3�"#Be�r$:*L3����i���=�z��f����>)�������?�n��I�'NNN���G����bN�
		�
6h�}����5����@W�z���y�`�7��6���<sb��H���Lm�\�P��j���M�t�����]��`�Bu����|�?��l�;[���5�N����5
5����P#g!�Qr��5��5]������3���k�F�l�f#s�M-�'��]�V��_��@���\W��1��A�a0�6c.�f"�xd7�@����?A6��#�>���J��=��e�;�K�E��g�Lk��z�.i���8���\�]��r�B�f��=ZV�Z%���|K�4];fsKO&�[[�^��:�
/�s��`������d���r������p]�/�i�)y��'5��r�(�e�8�_��B�z�i**���3�����Rv�J3��M���w���X��h>���)�����+W:6)uW_n�	�{�((,����$51J#@�2S��T$3�/�"3�]��o>Jd�@��~����w%Y����{o��Q�k���T����r��f��f����);�B���M��bbb��k��3fh�
�G�e��t\�6m��k��{�u4!�E�������(?/xFG�Pw�}�u������H��4!�b,��G����@o:]� ��9�����HY}K�F�����J�s_ik�������-#�%���k���	d��U2f���y��jo3�;fC�i<�`6��_�^�v��]����i^�g2�9����X�z�j������|<---���K������3�C�������]���7>�"I�a�/��zKII���d�����d=$���9����������~���Fh�������NKK��P�X`��A����
�~wA��n@��
I21��G���{��I�t��
�Zu�o��yL}+<<������������,�F7|}�JKK����N���)��.������J�����4�=)Q��V����a��4rvr�Wd����6k�,����4����F���U=��������L#RoN?��IJ�z��`��\\\\\�]��������%����[NZ�$��u�q��z�P��_f���x@W�c����
|V�[����:.wV�����2�;��sq�rU��h%vX����
��o��P��Gw���G�OI��?�S�QSr�������G			r����x���~3\�Tw�0��,�/,���� �yN1r�R
��U��7����������
��hd��W�����Q�j.}]*�C��������I��[�DY�f�egg��U�$55U3@��YH^UPN�AW����j$�f���Wk���M�6���|1X������~�������\k�o:���I'N�+	i��r�c�4���W�����k@***��HNN���6�.8V�������ApX�re��#�?3��7��<��tu��f�0����u~~�������������hVV5Q��^���mh�w�U�:����J#3����I�t��
�Zu�o��y�^�<@g��J���y���p	_����Rijjr�333%""��,���yy��Y�jZ����?��a�����_��YQ������)N�����s]���������c���K�7n��5&#y>v�������h���;��sqqq
�U���~��6=;^F%��u�u��z�P��7��v�����x������rqqqu\�������e.wV�������n��f%).���p�^��jY��K�XI#kn
�����������i���{}n<2S��a�4���|4��
)���L��i���|�d�����^�zo��m�}^W��sSt$���{�6c���%K���?�5���*.V7��[b��:Y`��5���g����U3���Be�-92|XO7oi���|Y�_��&����J���&};e��)r���JRR�f��C��gN��8��'S~�6��]�VW"k�����N�
�{}e��RQc�n~�G��a&���L'2<BY�z����F������@s���|�;�>sP�����i<�4>V���r�T*~v�4�]3�j#e{�mrpD�f�EEEI~~���=[3@���o����s��/�P|��d��q���u���@o)�]�+oyY�2*)J#@ ��A��^��A(��;<����1�pv��IW�h>������l)��c|tU�,�5B���tp�T>z�����k�reK�r.2M3���#+W����t�}���!�j�Hwm���{y����q���c<��!X�
��-�e������#T��t�A�����Ug��M=����g�������|�0<\W���n=#�y��F���)�-�Q�ix�WR����VW�oM���+m�����<�O�.�\s�>\3@���h���i���-\��q�y�y�;��-���Ui������I���m������eA�G��&������U-�n:��)N����/�����B����8&t�E��oidmjF���r4
<�����u�F���fKA�G�d�4�8KJJ�e��Inn�f��G��g6�x6��M&����������
(N�[�
.h��m~/�M3����ue-X�
�����yY�2*9J#@ �<���ajLM�\!!!^���a��E����&����_�T����P���H]�g/4��_id-16B��:I���V_%���G��>�k�S���n����8�<y��Z�JF�����u��//ik�Ja��j���i<����l`����3����-�z��b�_KK�km>���0�������N���qm��@���_�U�u��we�MW��h`<xPW"�&f�C����Y	������:��k-�����u��b>_����������]k�T\\�vM4;;��(K|�@O�~�PvUh��7>>U�]������L��6@���8+))y�k$++���
_����Rijj?,%33S""�s2��~T(o[�\:��'���)�W{i>�Oj��/i9W�o��d���R1l�f�EGG���_.������:O���!L>���L�y
��X_7������9a��y�?
<�zo���~��m��� o�����:��o-����?u����������9�e��]7fd�Q�������JR�����|n<JOO������#(������W�6��
&�]Ll.�>s��y�?�F�i����:�m�oO���
[v���[nf��N���:����Q�q�Q�0u���"L=��u:�������}�����(��#������g4�v��1r��4�G������kD����L����G�.��33}f��y���/QQ���2���fA3������z����Z|STT���I999�c%���|�kRn3��S��������cd1����Y	�����7Kss�km�����]k�T\\�vM4;;��(K|�@O-�w��?�����J"�9t0������B��[�yg%%%o�dee�5����WZZ*MM���dffJDD���yw�|���Y�bj�<���4
5|@��N#o�re���������G��9s$!!A3�-�y�fU��	Aj�����GF~^���*j�l�����G%�jd�3��B��K�����hZ��I��w�65�F���UR�����������%Kh<B@��	AjKa���M�����a�/\�j?E�Jb\��*0�[d��ER���of��['I|L�L:k>��T=z�4�l�Lggc�� �#r2a�f�%&&���^+�����G�����[~n���W.T;4��|��xT|�F#kf���	�
�����G���'4�����e���.���E�'O����NRSS5�� m?xQ�+5�����+���N�G��^�@��_�����4�����e���
����H����Qg����������q-.�9s�hL4@*�m_t��/cR�i����|��#G/�zF~���F���N�����@k�������T��JR������b�h�8KOOwM;?~�f��E��-����6?7YW�K4t���Jy�7�4�6%=NV�����j)?&�?�]���f.��L����G�.�����P�7o����KTT�f��F���ERV����|�����*�Mb\�����{Y��A��������Hhh�fN��W���;���.�\r<!W�d|D��fi�����e��U2q�D����#2�w������8���������"9s�A#kkn�$�G���m���'?+�����4�F���UR��RZB}�M�>]�,Y"			���Gd

�������
}��G~�������E��}��L���$�N�����/���%gc�� �#r2a�f�%&&���^+������GD�8T!�+5�����+�5��GI4t���N�_
Nkd�]�i��Ec4 MuR��/I��'5q����e���.���E�'O����NRSS5'�� �l�m?����8�:�������f]yK���m��]�����F�.��(�{O�F��t�T��vi��Q3�.F��+��)s5�,&&F.\(s����h>� R��|����+tG��j������on�O|k����\�����Uj����BBB$��#���&y��b���I&�o��h`4�{I*�CZN�L���y�5�6�6Z3����e��U2~�x����#o��s�y����+���C����)�K*���z9|�V�{l��z�	}Dg��t�-��#�����s4�f���"5��OK���J[C�fDj#��q�����4�,44T���'����Y`p������G�'������_����Ug�����c�Y�.�Xm�|�G������e����?n��i������|Sj�?�Q��	��%�#r>6K3�F���v4q�D���G$�������]�;����+ok�)�7�+4j��|�0<\WC������_�hd��k����Gj���j.H�SwI���hF�)4Rv���������3��O�%K�HBB�f����#fs�����\������U���5���O���4�b�Q,����V[�,=W����S���+'h�������Go���������������i�q���$�^{����j�h>� P�0�h��X?2Z#����QW���z�����44�����G�����|��<_�����r�M����]/H�c�Kk�Q��I�-;��(u�M/�<y��Z�JRSS5n4@���LW����z��+:&N��[k�U��=}���Xe�����#0t�����q�9���}S��L�������Q�~�+"���F^-�F-��YLL�,\�P����`h�����
9{�c����d]�;�*;���eoKa����%r�a�QR,�G`h�SZ)?�c�F�n^2NN���vk~����G�����c�)��s5�,==�5�h��������!*���,��;�����Q1�;.M>2���|J�N�h�-��#05���C�kdmfN�|���������������HmD��:�9?Y3�BCCe��y���/QQ�?�	4@����LW����)0�MYe��|w�\���%�����s����Z���D��=7M��4��.���!��whF�<f�lK��\��{�G�vM;�8��7hh>����R�\p?y������@w��h�|�����OAji�����H]
.8%��������-cG���5l��T����ZqF3"'����	���8���6m�,Y�D4]4@+(��z�3.V�G�h����|���;���}_�$�F�
`�;x�Z��|�F�n\8F��I�������R���k��$�
�5�:����;Wf������!���d6���������
=q��n�t��[L�n�W7�{&1Qa~�{�XW��f��g���Q�jkn���������v�F/���5�!�/����0h>����R�\���"2�b��WV��+o_�9Gb��5j�o�0�%���5��NxX��s�D��V��R�z�i,|Q3o���d���H�L��KJJ����KZZ�ft��TAa�����.�c4@w]�n���V�:*�1����Q��ZBl�F%��j�������Y���l�L��=.M7���O���HUd�l��������c���e�$!!A3��|j�����6?7EW����F]y��z�r�H�[_$1��O��C������G��y���]�[��������
����!��? U���������W_-�u;4@*,����
y[�G�@o(sh>J��t�M#WNMv����[o����`03�G�-�5�������v=q��j_�����[�;�0]���I���5co��2w�\�������������RX�+o����}?�`(8_��|�p�����G�L>����X*{J�4�v�M}�x�V_%���G�_yF3��F�����9����d��ipB����2]y���~�.z�i�������3b�k����I���^w����|R#k�{O�L��Q�k>�O*v�4xY3�v����Wid/::Z�.]*������� ��)�����5�� o���S�/:L>���|d��7��������|+������5��|�HyW~�F��q��R���r�D3o����k�/'r5c/%%E�-[&�F��y���#0[
�u�-{�p�L��=�<�(JW�V��+{��i>��i<�kh��[�����l�z_��_H���H[s�fD*�G������	��7~�xY�|�����T&`����S�Q~n����W\���)%�s#QR\���^����&�������E5�v�M��7-
5|@�^��F�����+�? 5I��w�e����%$�����G@������4���7BW�
e�M������KV�������h>����e����5�v�
����Q�i�:'U��)
���vG�����7H[h�f���3Gf�������B��|S��������S�-mr��Q#o#�tu���x�2!V���f�IJ�w�@�:w�Az�X#k�g��{����4�)�?�]�J�i�����e����sM;�<y�ft�G@�84��&�
������(16B��������$"�����q�h]�����~r���ar�MY���7�,U�}LZ/���H���c�)�S�j�^\\�,[�L���=A��������:��-�K���m������/�"����a�W�|E����	FS����M�9�%���My+���Sd������������*N��)[���w�m|Dj~�U���F$���[�t|�S�F��j<JIa�
�[h>����LW���w]�������G��~ihj���5��_��\��J�S{G$F�������'����:W~������1`1MGOn8���;�K���5�m�R��W��_?�D���q�-�r1z�f�eff���K%::Z3z�G 

�u�m~n����'l6�<��#r�����]-�M��s�Y3���t�F=�Z~L*�Cw���v'����	���8���6m�\u�U�M4@�w�J������N���-���M:Q#%�j4���+���4�s8�%51J��)[��k:��T��vi>�K3�JR��]c������se���m4@�Rh�	��L.�c�k_9S+
M�y;v�^W�F$D�
`��������0�w��-���35�a�o����J[M�f���L�.��^DD�,^�Xrrr4�/�|���s�]~n���������v��}����P����<��#Y���	r�eI�L�����/��Q���H�1�=r4i�f�%%%����%--M3�
�G0���cg�4�6��#�n9t����
i,T�6��[JB���j������5�6wJ�|x�x�����V���W��<��v�Q#d[�-r66K3���+��-�����K4�sU�1:Fr��j���y����M���6"!JW��i<:y�~Jt��p���l����L�T>z�4�{I3���f��n���T���������Z""8<�/4��Rh�|����+������a!���04��������Y����22�g����#�x�r��f�K�.��{�4�Ek���3d����/4�:p�Z��������d]��+���I#��$D�
`p�SZ)?�c�F�n^2NN���u��g����������=i�5rv�UW��i�4��h>��e������12i\�F�G��Z]�oDB�N��-m��s�Y��� G�F�k�<+UO^���S�\�;m���J#{����t�R������F��-���GL=������o�
_�*_r�������v�';�����x&�����������D��=7M�����-��&M7k�]c�p�6��r"!W3�RRRd��e2j�(�4�9x�Z�����[~n��UV'���W�'/�zFj�[��7��O��\�G��G��Gm��7)����\pJ6l;�����7[�����6>"��|A��;�[1l�l��)>A3���/��/���8�(4����~�����2i|�Fp����Hk�wC��9�+��G!���}C���(]>����?_������9������T=�y���O5s���y5���L����.�L.\(!!Vu���# �����?��GV���u1
G%�j���E���<q����~�JJB�����+����q��wgi�������Gn����5sIq�<�9�i
	���9s���Y�4h>�Pt�Z�����[~.�GV����z�aOi�����x�9���#0X��Gf�����������n�#R���������i+�h�"�������M�<Y3�G0�8L=?2Z&���������=��Rr�F#��K��+��c������SY���l�L���k��g����K��~��Kj"�ek�mr"!W3����d��e2~�x�$4���������z`�L>r[������|�p���H]�O��C�kd����e��Qu�q����'����5s�����q�\�N���Q�F��RR�3*������#�k5�6?7YW����i>�[t��^�5�q��/o��Glt�DG�i08<�\�47�j�-g\����l��V���~��Zu^3�'��co���(�������K�Jtt�f"�����O=�-S&�iw'��IYe�F��~q�z$#��L~��R�SZ�������+g��g���wK��~���v������4r���'W]u�F�G��6��o>Z���h;��V��-����������Dn��S�#u�6�8'�{��F�>��,���a����-�?��4���Kj"�ek�mr"!W3����$??��| 8�|����9�p�n~^���i��K'���N/�k�9z�����#0X�*k���+�����#�]�i��{�'R������f.9�#[3n���]�'))I�/_.����h>�~���~����h���)2C���U�rj��j4yB�����Zr|�����W*u
-y�#���������R���]�GV������7HSh��L�0��xd����m�m�|����+x��o���5u�o4��5fx����$D�
 x�q[��Y|i����o���p���������|Pl�Lg��VJ��E9����HXX�f��������S�y��M�<�=ri��%������]�G��=K#�>� ��Q� �i�G����L�������tT������f.��L�������\��3�F�����#���#�'[
�u�m��arY����C����'�v�hH����>���!��+��s��E~��Z#k�g��{�������R���]�GV��������bt�f�%&&����%==]3��G�O���>	�������}G�6�8�0��>:F�"Be��X		��>J���@�y���RU����1)�����4�����R��J��M���8y��{�4�v=9�4�X�B���4 ��|���d����g=?/YW������#�F��w�

�+�Zoz�%�o]���
G��H�F���)[���3�����Hk������VJ��E9������|	��`G���-����6f�0���<������:u�b�.K���O\�����{�]�����<��cY���t�=)Q�vmU����w�����D&�����DB�f��f#�td��.4@?�RX�+o��L=p��p����L�mRQ��#c��h����\
I�wi��a���d�{�|�����r������Nh�Z~^���t�F���,��&�6i���q9�5�6���{����|�rIOO�����#�c%'k��D�F������=\�+o�2|�(�>��X:'U~r�L���|y����w�w���@�y�fY�n����t�V�\����0�������7dWI�f��&F�=7ek��L:���=�Zu^3�'��co����~1
G+V����$�lh>�>VPX�+ocR��T7�U��T�������4������0"!R��"4��L> X��#�\�p�BY�f�f/��a��2�3�H��|��[�;�>$���4���D:�}S�$���F���I�/�v5����R�F.��Y^^����KXX�fF4@���|4?7YW�RV�('��i�mjz��f�����"��z@02�D���4��<�$�����
������h����O�+.k�H�t�e�x�6i<��{��L�������\��

u5��#��G��JO���N'�����;R�+o�R�%9>R�e��#{�4�\G���+V��M������k�����;��g�y\���B9z���wY1N>�b�km&U��i�:��=���������4��KLLt��������G�������i�q������G����V�e8�X�|v<�����������h�����]y�����q	�����������vD3�F%����O������sR���]�Gv������7HSh�f���#��hRR�4%C�G��
����&�
v�&M��#&33�h��
]j<rb��<' �[�NW&/�*��|�M����f���,F���D�<6BZ�6K�#�I������v������4r���'����C�G�G������y[���+�)=U�+o�Mho>���y�Y�b�������SPP��v��w���y6(m��QW?��a����bu�f�������l���D���]�I����������j"�ek�mr"!W3�BCC]MG����D���-�e��6:y��vq"/�Pg6���7k�Yxx�d��4����������+3��[����������3���{g�;�-!����u��#;g�rdk�mr1:M3�e�����n]{04�|}dKa�����Z���KN�������(]��#_�m�,�1B��Be��DY}Ku@�3�V�X��0��������7d��*�8{�������$3-F��,q^-��w�{�'��co�����+������L}�h>�>p�t����[~^��`�T�C�Q�0]]�tv����'����o~r�\{�H}�k���o_mmm��`S��"k�9(��X3���"���"��>�7zE�~y���^p�Vv������4r���'����C�G�

�t�mt�0����vN��7��h>�l��YW��,Y�+��/���!��~N3����q�,��~�m[k����m��JMd�lM�MN$�j�^hh����4@���l),������������M>�,\�PW�����+���~T���=r������g���_&�����{�;�r�T����������bt�f�%%%��+$==]3���#�eG����c�y3���k��&�����9��l ]�`�Fg��+?�+On8�g�c��������j�]��K��������y�c�
��{��h����$�D���-����J����	���������4�Pd�<��w�}�@ xi�y���7d���qv�����{gJ^V�f��5�K������i+�h�"�����I~~����i:��zYAa�����M���7�HYe�F��R��
t�j<Z�v-S����/��}��T�5k�^dD��{�D����%,4D�����mi�pB�v5���5�69���{���2�|W�81��������y��6m�������N���sRNN���x���cg����;4���O����Cs����u%2i�$]�����������D�����1f�Lxx�k
�������fggS`�����T�|��;5�,51J����5����L��6&V�G+V�����kx��<iii���.q�;�=
@g|} 9�,��\)G�z��Ic"��dt����EyE�^~H�v�G.�������������t����C��X����Wk��6���z42)j�6��dY���1��������������rg�~.��|��z?W�]w�����}n<Z1+F�~g��J��_HC��I}������9��r8y�O�G���2e������?�`��=4@/*(�o>��M��r��AW�����D�P1����6��?*����8K*w���w��k��?)!u�p�����O�����:j�����1c$++����bf!y�n��N�6m���������\k�o�����9)''����;W'w|s�F����\�9�'<xPW�,����{| �!��|�@g6�47��
�`�	w��Sqq��5���lj�,��"p��w%��-�4���U�r��q}��V�
�Zu�`o<r�����S�<�����5�����������_���TN��k���#�s��������w�U�oyA�$����h�v�4]q�����`h2{A;P��fU1��E��1/�

4��q��D�,Y�+�����6G���#���t�W�<.����F��&F�3�u�FC4Hm��Xz��u�t�\�1���������{E�����um�����$�g�j�=j[�+�j!�mz�jz���+e��
�[�v��^�Z��G�pVRR�����P��p	_tO�?*O�xL���yC��w���������o���"i
���|RR�\y�����PG��kV�����P$�OGQd��5���9m�O���?��G�34��;��V�ol�y�����go��hh��;I�maK��f#�Q�s�����6�6��
|����u�7w��suu���g��	�}��V�
�Zu��c_M��j<2�B���E�pFs`�����������5�l��X����q��VYY�z]���������#�^@;�<]�����[����5�Z�'�4qt�@b�B����^�0�~2/��Pt���������+@o15SK15S[�n��a~����{������u��u��1:%JW�@����5X��_?+w~g���G��?Z~����G.\�W^yE����t���4����I~~>�Gz��#?uGzZ��Y0^�yNW�F$D���D�=��tdj(=i8�c����D00N��K��`���pI� ������xx�p��=W,��-���V�����5�����������=�����^���R��.22R��'��O�)S�hz��B�*�2���������V6n�����HW����_��}�����~�)����)**z������?j@�[��59_��Qg��f�|��
]�,F ��{| �!��|���b�*��S���3�6/��#��Z]ss�km���S��Jqq��5���lj�,��"0����d�F���.?��L���Am�wS-���o�W�?c�u�YII��_#YYY|�n��@��s�R��|�������9�]MGcF��%'O����S�4����x�k���ijjr�233]1�K��t��b2^�$���-N���k�����?�[/�;
0]m�1�����5�G���G����\����4���/���q�
]�pG 	�M#lbqfj'<��m��}�Jwj'��FWd��s�}������W4��+�_^9�:1���3G�}���?�m��`��P���j����y0�Y��8��������>)�����D\>^>�b�F�=z����L<�����2u�B�������$��{�y�fU1�W�I�9ml1��E��t�/�\���G�-���P�������S�_o����)�q���O�hh��;I�ma�=��+
G}���cC����xlJ�+
�����g9,�y��F�}`�8��;�5����L��6��W����2�n�L���8�����REM�|��y�M��"�F'sM;�;%Q3���c3������Or�q�pa�d/~�D]���|t�:O���!&�U�Hb������v6��;h>������������y���3���c4�x��@h�F��bm���^5��d����=S#�jD����������/�^�����
<_|�DYu�(���Am�wS-���5���/�z:���@��P���\�����=������|E�f�-�5��x}���y�i���J��'��T�/����9�Z���}�h��g�y�fU1���P/�X��;���XKh@��h>������?�v����k�$1�"��w�@�4�&o����n:�d���f��������/�^�>��r�D�F�}��\�����?�m��`��P������e���]����:���
�_�~��<����������\k�����4�����\9��: �wHr]����a��p�o$4~�+�@���:O���!tt�"����,��b6��� p������-�K��z�4��+�xd�������b���u��AW�F�D�
0��W��b��>7M+�3��x��� �v��?����ot��hlE��?���<������U�z5@_���FGq$�N@1��</6��;���`w�t��.���dv���ro:��+��s1��<7}�bu�����^�)<<TF'�0P�W��4n���J��'�k����c�����1I";v�����wRXX(�����M��S?&y��K|���������N����|d�"�8��lr	��vw�O=J�����S4�+���<���!@�8y�^W����z�}5��|��[�S|�F���~ihj�����p��'�m����W_�?��O��~��Iq����#2��?ex�}�%�q#$f��4��A�t�K;������+��T�C�Q
S�@�8UV/���~��f'�.K�o~l�D������R\\���]TT�L�>]��b�d���D5W�{���������|����r��A#o�g��
�0{��������f�����C��x���<�����Kr��a��.66Vf��-�y�{$77W�_|H�c-j����\#�?4@7������6ql�\���q�<����ijn��=�O�O�h����Z�e�Ij�/'N������+��w��]2e�			���~"�G��GxK#1�����Gd����@�ihj��;�����+@ �>�S���FD�
0�Q��@���}���R��R�7��SN���s2L��d��2�|y�;�!����W��i>rb�B"c4��E�Q/����n�:Wg���]�Y�\���	D6�8'���y[<;UW���Tg1�3������'&�N�� ����O�m�.h����:Y5���o���F�-W_}�,_�\2224{I�����aW�_".[��?��z�{���)��Y�F�K6l���u���KS����$�	���{SQAA�f;3�15S��dj6�����?o���<�W��6��O��_�����)������2LW�`���sH��������6�b�	�s���q�d��%�k��������H���y�!1+����z�lH1�����&SD�������Sv��v�\��,�������z��l<qo*��q���'��-|y��{eO�����I���d�����o�7��GxKM������	�j,�c������]9��$k��kf���+d��E��Gv�K�I��_hd-f��"a����2�CVR<O�5E��Y���8����t�m��pY<k�F��`�1�j��?'������G��������v���v������/����M�K���1]yc��� <���<��I��e&����*��M�8Q���:�?����h�^����+k��P"r�5��C�QX��b��,�yM�M�6I[[��2]��
��;��k�g�p���?VS�<k-�gC�yLGm��i��M,vu�v��j8�{�JN��w5"��������#���oG���:�:��o�����(]��j~������C�*W��>\���+2e����d��y�����qV�����l�F��F�H��/j����<��c���4������k�vz����;J�#
``�q�B����c,���+@��zd6��Z�;�IF��1u�$�M/����������{����jy���]k+N����+@0`_
�_^9-?��a��]1�L�#�K����o�Qf��-���l�n��W���Z��/�
�G��y:�)rxnl��y���d�]��t] ����~�Q������?xN=2
D��W��oV�����&*p�m�]y�h@2S����[+i)L>�`����;������:dLQ%Y��Y[�h�6L#��57J����Z��;$"k�F0�h>�%��w��:�*vXm�Y�z���q�.XZZ�����4���#Xf�]��g#��f��T�������U�j�5��������5���&@�b_
�+{���~yP#kqQM� �B#kf�Qrr�F�3�G�e�S�����&�����G��Y������8�M,���zT�`?F{�����!��"����s�
��+�l���6�]:j��}4��G4�W�@���
��'�kd���7H[��>��c���)S4�]������o5���^]@������Y�FW���mg��DIK�o�6��[�NW����F]�����-!���������?���=�_�[�?1n�:LZ��4���z�����}��Y�^�)	�0S#4�!��������`q��^^�A#oKf��
h6n���v+V����T^����8L/��4�����������k�����f�X���D���������8�|g�Z+Nk�-<}����G��n��UA@`xi�y]y�����FhtK�,��7�F%�+s�|d3��'����`0c_
�Bye�������X{W~�L�9�����t�8q�F�k��^v�Q#k1+��+<4u��I+�����%�j�v�Z]y�|,'������t�m��T����)Fk���Ug6l�U���W����c���������WMK����t���%�%� ������E���~9|�V3���|�\�S#g�����a�����_����j.H����Z������S5����n�<i��d\�M/N�������L�g�����29����&�y/_/����������/�
����p����/@1���#����u��"��:MH�k��0���aj�Y����E��G~��;2���i�����cF�|�d0��@p1�G{Wid-?/E>�4Av���k��(**J#������2���g�����k����p��bN����a6��� ��
V�u:��/ulB1�S�
�3�[�p���g�;b���9�{����sRW���/���zd6�������ynHy���~}n��u�;��"��:MH�+uSk�|>��Mt�gL
��Z������4�����u�����.FE��	�2��$�6��GL>��3��� ����e���Y3�>���y���5c-''G&L����v�Iv�M#k1���+\4��}����v?��F���)�����cb4�)���	�<g�BP3�G�����X_7����<6��o>Z2+UW��`6�xnb1��;W���Pdj3V5�	I}��:Dj$N����r�|���$}T�DE�JVZ�|��t�y�8}��;m�:�N��Ta��L�j|���"���~��1%=��x�}�v�x��I)..Nf�����Z+���9�Yy�����.3�����6�������������������~�z�����`�:�rLQ����r3NW������v�[1#�Q��������mN=���|�3���(/���������-��P��EEEo�7'Y�0��������h���kf�����+����I�t�����\�=�`b^��M(]��uv?�m�������>S�0t����'m����������������������6{xx�k
���������������0�����w���[��������_�'������7���j�.�+?�����Am�wS-��M��}5�[�yg%%%o�dee�5���N�}��a�)���CR��9M�*����:���kd��1������q�?4�1i��}������Rijjr�333%"���0�P���U=�d�*"I��U��j�����.�X=��6�x~�V�w��?�7?F�B����}�}y��D���G����_����r�:��� ��L�F�/�Hm��Xz������u�UM������U����u�������{������4��+z���E����u*/��J#g��+e�O
������kn��esFjj[�+�j!�mz���.�����Q���\���v~��#��?�kd-51J��9MF%��/� �����t�:u���9S#�5������Y����~+�)4�{h>�Q���U=���^`
�b��2�[��b
NV�9<s��D��L�q������N�H�L=����A�y_�������@p(�h�m<2��+�@3�S�1���������j.�u
�X�:N_2�+�i<2�s��8;��T��i����c��WV��+o#"u��������"�'%JR\�L�+����4@�3��`�W�������l<����~d��K�����;6���t���������m��
��K}�x}���^b���a.S1o��~c�(�82[:xnn��y������l�����?��z�sC�/MM�MSv�r���/�<�+o��!�xV�F�@aj1��s9�:L��c��y�@�l��1��|�7n��7��y6-����QW����;Y��i���S��7����=C���u_
��������zD#kf��d�L���d&h9�3g���c����k���D���O#4
 SD	��HoN%��W'�zn��� ex��v����/�q^W���J���0�������u��ZQ_M�:�	�vR��|��@�W�����g��sn$2���)2+'Qjkk]S��L�>]F���P��W���/k�-$j�����F<h>��T"���}��N�����+��MV����U���.;�gq�.�w��I_�g�j*5����IW�R�i>}g��2Y�l�F���:I��&��������0�Q�FInn�F�k9[,�/|G#k��(4a�F<h>���0�#���__���^�
8����\/�<�+o#�d�eI�3������3��/���8�����7[��Iu�<(��s��������_�������/*o�D��A#.4�[�'vc���u4��[�NV�\�����2y_vz������}y�@��&{/�8�+o�g3�0�<kv�������_k�0��S�1y�_e��GL>}`��*W�QW�a?~}��s~�����
��#'s������|W��	i*������I��^� ���uy}��)�{����������lz��8��Y����+\��Z1W:�]�����c����{���6m��se6�,\�P����>��/�^~�eimmu��s
s��)**z������N�<�8������G��)�c�k���:L�4IW����{| �!��|��ymn�mz�>cW��f�j��Ss1����u
w��uss�km>���p�<KKK�k���MM�%�W��=?�-{J+5�l���2;��
;@�������B����}5�u�YII��_#YYY|�n�������/�d���h�����/]9A����O�������;��U���R���Y�����~�F}���T���\���L���p�����5�z��xUD(����
���
������-���}i>��G�34C����//�Y�Qg�2�����i���$�6����g<_���@�o����z0����;����W4��+z�C�x]N�[o�������1���V�
�Zu��j�j��<�3�+{|}]�.6�����cgk5c���c�3���Hd��=���oj�-22R���:�����Q���K��y���.~���=��g�y�fU	�����(�2���lB��9�0�7 S���L���y��MC����&��#c�����j<
TV�9���c�1�����������A-@W�*u�-%>RW��h���@���k��=~������Fuj<:��c��1g��n5���g��G���$f��@�����z���t�������r=O�1�K&g6�xnr��
.�4J��/�
������uem��T]�G��Dv5	�xO��b�0����L������L���`sij��2,2L�b8y�*���Z[��kO����4cm��r��5j���������e&����.���c������aq@�2���*���St�j����1�|��/�Y�
Bf����4\x����h<���&���4$�o|��yxr��f�{�7C�����/���_��uZZ�����p������;�(���O��3q�|lY�F���zKII���d�����d=$��O��|o������R}��f�K���tS�����Y���������������	?���+z�Dy�|�7�u621\���{18���4i���]�T�n�}V�������^�1�]��i\������5b6��5\������_���B�D��S��?1U�v;w��}��i�m���r�u�IDD�f|W��K��B��E]�^���h�JKK�����6MU������������c��5��xUD(�t�js�`��u���'<?��� 4���������Z�=��9nN�5��>wA#o�\� 32"5z�i>2�_�m	��,<?o��Z�7t���'<?�����}S
5����X�<��
�:��!_xW�F�������Zu��J�j�34�h����1��}���c�9��M������&Q������S��K/idm��E2n�8�|W�������i�-,y�������C������Y�C�W�^b6�F��xdx�p�q�F]��)X����7��k�t�->:��#4��{���;�����0�����j����|q��GYc����������U�o������'w�����u��##z������iG�:V��F��yB�`��
D�Q��u{2��s�NoN>�����}�������\k�o:���I'N����>���r��u�{��O�+C#tWQQ��Drrrt5��w��	�k�����`�z4P�G�g�$�>��s�q�a~~>��eN?�����O�������������
�4������'�IM��{m�q{.�j!�m�g(��A�p����,�y�������chx�/���/������a���i2&e�f����k�^�yJJJ�U�Vi����"-�h�m���H�u��Q�c����G]���p������{�����_�G]�����������7�������rg�~..������`�xd,��j������Y�(^`�
��xd����(�X}�������Y������\�����|]�n��e%%>���pq��_������c�����x5=z�����={���S�����R�$z����A�Q7
�&�u�����N


t�5��v���/�
��������6i|�L~���j��n�@N��;e������+�PC-=��M'���G5�"_���3\3����_��Znn��5J#��H�_�I�������^		a�>����l����c�v��S�Qw�{����'MP7v���^}yo�������F��J� �,Y�DW�����]�Qw�}Q���U:4�G�
0�x�2�O�W?�}��������Sdzv�F������z���������5�M��W����K������a����Wi��G=�v�Z]�<���
N�����G���s��7�4�?E$�k��o$����_�f���cw:r_��g��������%�F�
h�'���`�����G��sox�D8dv�W4��[J�G0�
�}5�{'����>(�|Z3���������]R\\,G�����9st���
���'?+-�����&K��{4�����0\:�L��7q��n<2<�g��k�'�o$����"��
<VMMV���x/���z4?7Y�9��i����(������Q<����s��Q��4�(9.BW��h���@�{������]^�i������-�g�jtIuu�l��]#k�g���d��%+�%�I��7K���4�l�u��+�h>�����OW�
%�������l(�I����1�����&�	B�
I����������7��3u�fq�F�3���A��L��s�r�3���9�������<G�u��g�����Cye��D���p��
�0T
�}5�;����o��_�pTlJ�|�]r���5����_���f���3F�L����� ��JZ�i�Y�5�������������ln��N��k���)��t��/<��y�=m<�`6���#wN'����g#���=������8����|>[ZZ\k���|�EEEo�����#!!�G)����G���i�Y\L����Wh��p��A]�L�4IW����{| �!��|��U]��F���x}�o=�_����[K��|�:���|����]k�T\\�vM4;;��(K|�����������Qg��b����4����L��6�#X����Q�������5����������v��^��O��:|x�x���	u�o�>��s�F�L��������8�Xk>�Sj^���������,��������Rijjr�333%"����;�<]�����WE�"��
$=e�����?���s�UQ��sc~�������b�����d��6n��uo_�����
�G�34��G�o��<_�Qg��O���'K#�^�#�X�=>����������
�@��X�+�Z�]S��=�o�9��F��������/�^�}��)�����f���e���i?j[�+�j!�mz.�����Q���\���#x=��c��#~�������wfh�Yyyy������''N��Z��K��~�Q���O������6O3���#�u��Y�CL��{7E��E����"Io>w��k
<�����/�G���'�n���{���gh>��/���G#o��'�2�5Bo��;I�maK�u�������Ao?w�������@�s���R�����{E���������5�l��TYs+��1xP��]�T�n�3�����:���
�_����\_TJO�h�7w\�.�,���_|Q����������`���n�]������L��]�	�Y�i��G�3�<]�����[t�������L�)���ll����0�7�7�J]1��g�L_�������1�i<� �W�G�����k��0�3���s�+e������+�P��'���O���b�_�G��!�y�c���������e��9y����R�����x6�2���c�x}��#y��	-����2�3�,Y��!�����a�����e�I%�q�F��R���'��t���$�@�w2��|���#`�hjn���*
M����c�H�,�/^�{85�$�N�
��,�&��:EOk3F_�g�;���/f��ij3�g}���MG���WL3��Wt�w[,�zZ��>��L�q����Gm�wS-��M�Y}��i_
�u��]{|}�g�qL���fu��c��U�����kg������Y������t�.i9�_j^��4����
[x��\�9���g�y�f���d���S$��y��y�.�o�h>����<���Y����4���}����(��[x��@h�F���=+W���@c6��:M��M)|EC_�����{l����\����#Sd�����Gm�wS-��M������8�����G`+(,����k�������k�k��o��\�xQ#o'N�y��itI�������k���Q9��K�5W3���#�u��Y�CB�-�d���3'�R �����#��NI���������UV��+o#�#u��W04�,��o<}@���}~5]3s�<���>7m�����(..N����Q��3����;�j<��!I�������
�G�d�����L�k����Y#t&f��UV��+o)4���`�{������]����f���6\���2���&���h�:;~��8p@#k�_~y����OK������m�q��)q~XbV|A3��)/���z&�g�j;-�mr��I#o����YAa�|��o�/^8"mm���GWN��}i���&k�kMMM�c���M�:U���\��s������v�������E�?���x�f�G�4�|]&/��o>Z<k����ht+V�����4�(!6B"���h��W0��8_'�x��|����T�f�v������#�];^3���}�TWWk�-%%Ef���Z�o��T>�>i:T����2^bo}Hb��		�45	���o7-X�@W�6o��+�jWq�9S������z�d��%���q�����
�������;����?~��$��'$�B@�������V�v�kk�������*m�j�{�oo��V�h]j�V��Y%aHH�!�������d�}g&�$�I~���-�<3R�$3'�<O�F��������������;������!������'C��u�wEEERXX������K��cR��I��+���Ao�,j�G�E�_�+Wu��+4Y�|�F����E��bd��D��`��%��^��&�y:��?�G�����y��}Zzb����C����>��H��72d��$]i���*��G�\q��p([����J��wu���������HHx���0|��
.�/�@�1E��9��G����#F�M,���g	�"�N>JJ�����W�s/� ?��~y�O���D��zw��dy��3��F�J�����Z�Z���$��i�|�G�TW���EM�]���DN���'u��u�4�\(��<ksK��B�f�f0|��4�,Z�H����+Wj��p�����g�:*�?�M���������������'C��u�}������#��	I���R�����Yh�d���q������o.~��C���r^/�R��a�,]��;3�/������3g���7O3�?LQ�����f���0G�M~~���Iiii����R�����W6}pF��2�e��'k��b
�-���5����������,�U�/_�h`q�2��������,�k__�<Tljh����\\�����R`�����g�e���4k�+w�������m�W0�B���}5�u�����K�����#���������UG�t��q���>����s����^������e��\�<��v����
	�I������H�����1c$"�S�g�y�������E���V�Y�b
.�;���c>�WIY���������]c��Y�4Cg��;I�5����>��z��A��)��(��W���~��l�V��~���39I3�g���_�T�n�>���W�y������<�K/�S���6�td\;-Y�iT�O:r���%'O������|�~����������!�S?�+=�G�g�y��������ce��h�.<,T23h_�,8�������`��[G�����i�h��Xy��	��O������={<�7\����4�5e�$~��^3x���#=����X��d������L#R]}�f��'0|@0yo�i��Os�Oo���y�����of�\?��\ZZ*;v�����Sk%��\3w!��w�$�c?�����
h/s���������a���o��9���Z�n�4448����KXX�#�&??������4�c%�����+��S3w?yh�\9��f�LY�@b�s�;�!�����+Wj�9�,Y�������;bSCw������RM455��(K|�h�c��r�Or4s7,9F���t������S-��M��W_Q�<+,,��III�98����K/�S����#��i���M��z�����W����5s7��n�rb�f�"&^'q7}CB�J�RTT$uuu�x��1��4����U=���UD(�=�G�1���/�?�����IQ���^�:o�H�i�&�mhJ�+
���m�������t�M�����k����+�j!�m��E����
����3'������=8��{��2wr��$���+{����]L�y�[���7�����(��Q��;u�wb���:�wV��P�z�����-���yo�i��G���1x@���W&��i�����
��x�����:xt����G���,������H��|��@ga�@����T�U4��b%��#�=��a���&$�6�=+���9x�JW�3�>��r�
#t�s���G�l����Qgsd`��Th����T���s	�7L����km��N�SRd��X�����<��1��i�2wr�f ��<sA��{�f��+�<0^���t����#''Gv�����>5��S��E����/� Q���@ga�����+W ���Ng�ke�����3;�����e���������
��^��Vq��}5�������N3�����3C�N��+�g����o�>����������/%l�h]t&��l,]�TBBB���FLc�����0��z�����3�5���W����n����L}�|n�sz����id�4$
��7�� ������=�L3{f#���=C��a��t���y��w���HW���n�����Q"RgI�������s��������$�%��[��n�>2����p����ALm�������h^@oq��J��?�d��r]q7zpl�4$}5���O���Ohf��������O����h]�<������o��c�t�^��-�V��#���+����+a��:r@�a����%Kd���5k�i���%v�������������}SL�tN=�@�n�:Y�h�f���L��+�
���|�@O���G�������y]�v�M#5�����E���_,����A���of����u�s����[o�%������W�-���Q�%��H����@Wc���)@�6��"E��-�x�Y�����{\�#-�-H{ksK4r��)�'&iT����U�V�@2L���$��Dj82����a�W��f>G��������������b����.kL�j_Eu�<����
K��_���:_qq�����r�
e[L9�Z���"�H���'aC��-�����������m��/\&)�x*��0[���3@o�i�ha�����BL�������"u�6�m���+���I���y�9U�+�B.^,��#����lO<w@��x~���{�J|L�f��������%555�b-��A2��,��vIX�!���@w25a�i������-��Z�a233Z1?<
_�G��������FG�`�	
e��"??������4�/7��{�����W3w�����6<^3t���<�D���5�����������L���P�3�A%o�_j3�:�q%p�Z]}}�#6_��������SPP 


�855U���18�{�e_��dO�y�<���G�G����Q���`��P��
}5h/�<�g�����#)))<G'<?<{z�a���G4���;S��9�5�\�-[�hf/��R�{U���9�>���D��v��]QQ����9�1c�HDD�#��:�wV���V�Hb�-���2��e�����a�:��# 8�������R�Z?�����Wh���w�@k���m|B��s/'�R�����{E�m���[�������y�����k��
�;P���`��P�i�j�V�y���������R��_�kf��yC��I��s���Gv�������R�v�__{���,������#F�0|xF��;�z�md����R�n�V�F��y\S��(h�m���m�������2�SCq>��_�c����;<@o��
'5�w������g0x�E_
@�:x�J��{�f���M������������UG^�4x5�f� �0|�N�x�������ih1WKq���}��=�,���'05�d��A�������������
yowss��~}"d��d��i2�_��`������s���Q3w�q���c5�\7n�}��ifoP��y�y���t�a��$���9b@�0g!��m���}�Q���
fee9�t�����s��94��KeggKcc���Hh(�������^'���Y+	�{}�W�dW�y�Z�7��������;pd1����������L}����A}&���q}}�#6_���pG�


�������JXX�#g|�����M���f����{W����w���_�T�n�_���u�����K�����#����x����b����cN���4���b~�;vLW�
/�)�O���Y���"a�&i��(**���:G<f����p��Q����bV�*"I����#�����f���Os5s��O���W����7�$��4B�64��|���W�����?�U3w�g
�������@Fm����B��\�y�����h��w��o�uP3k��y���9L��Q]]�8����TW����"�%��5���{5�#���><����U=��=BVN�F��#<���{'4�v��!�@���2��G�����Geee��[o�4x4�$�m�(j��@c�@��6��x����Z�74��7�������$mx�f �����'���������w�i�9����������r]�7��jsz�f�"FO����jDz[����g.h�na�����zT^U���[�r����>_�����P��=i���;|��deeIMM��Xmj���^���v�J���{mK��IDAT�x���{��G�������/F��-�������������j�o^-���2��=|�XI�y�8p@��_/MMM�b-��R�<��,w?�)���IX�Q��G�ZM]���~Z3w3�5@�������j��}����@���b����5�vO�p�~���o��=�e�������U�����c�rY�5����7hdjY9%R_�������WHV��k�nXr�\�@�@ �{�\~����]=)I���N����;vhf/���\u���u�T6b�������G����R�����O�#5�Q]� �����-s8��@T]�(O>�y�hpR�<|�X��o����o�>��
�8 W~N"�+u�������5����%���_�����z�*'�~�HBDn�;DH��{�*����7�I��������A�}�])**�{#�����/K�4�Jkq�_B��4�����%���g��BN�Y��-�"Y"�C4��/kK��R�����255Q3�������~[�;�+����"�N���]���ID�l����#Akm�}QmaF���'�&���G�����:f������G4�v�������5����2y����������~�]W�����i7K��Oi&J��er��Z3w��9�����j9r������W#���'���������GS5����b��G����b���m�rf�f���I����l>���~Z#w��:
kh-���G#����h�@ x��Ru�A3w}b�����j�?����,����k���23��=�VW����=		��l>t�dmn�f�fp���\�G�z@`�����y��=|�X6 F3�8p���_�^���t�Ztt���_-����+���Q������G����R�������i��Yn~�F���%j��?���7����7�����k�{���-[�hf/11Q�I�'q�~�+��g�-Q3>� X1| �d���z4c\_��Z�9X.�U�������G���2���E�Y[�1@��~�f����#;v������e�US%l���b-|T����T3@0c�@P9y�F��;��������S���$HLT�f���)��'�?���1Cb����j�7n�}��ifo�����)
���r��cC��%���kvu�������+����������[�k_�������z;�����������"7��F�2�5���W`�������5��	���i�	hhh�w�}W��<��d�;V���/��R��AW����=	�?J3@�c���L�#��Ou��u����k������)����})���4|d���m�f��b�UL���6�Z�Y�|���c��z����Y`?|4#��Ft�j<�����}g5�fN<J�Y�TWW��o�-���{'N����Jjw�����Uk1�|F"'���'�+�OL����������x��`���������z�������fo��������,���G}b�e��>����x���S��;���>v�0Y4s�fSVV&o������������6m�4�*����Uk�H�u_��S0|���aT:������9��
M���i}5�6���CG�L#H��r��wM����D��x�w�B�|��f���O��h�:����q�Qyy����={�L�0�W���4�]p�VBK�m����0|�A�
$+V�����
4j�4����k?|�9}�F���n�h��E������$v�@O�������D��/�j���k�'�? ��M��n`�(y����u����/YYYRSS�+�BCCe�������+_����������}	����'a���
$��e��u���$K�,�Uw�6s�������w��JyU�f�N�x�����i\1u�U�VIff���3�1uW�����xi�9U��;��������
�Wjf����JRB�fmW__/{���W_}U�����<�������N���/lyAj���#���k�:[3@O��Q���b
��e��y�����k��Lhm��S���&��A��z��#����q����X
 q�z���#��c4��������x��0�x{�LO��Y�TTTHnn����K�?M�Mbb�c�h���������O����������1|�K�.���)x��k���i��N���
��h�n��d����.�����4���@rm��Y�������5/�
}5����������Y�i� ���!��������.s��9���|������_/			����J*_~��	�&��}_3@O��Q;��~k�[:"33S�f��\�6�~����Y�����wq���-'�rZb]�$��e�k���xB_
�g�K/�������Q}��w���7G�u��k��5r��A]���#'EEE��H�+�ICI�f��n��Hx�f�����vr��v��9��ks���ey8b<3c��F�i�M�w�u������`�A=����R^e��qFZ�F�}5�=�\������a�����������.���r��I]�]jj���?_BBBtE���?I�.��3���{>|�f����#?q���?R!�G+5s�0#Y#@o���-�f�N=�����.�.A_
�e���B�Ux^3k�5VF�����d��]��K/���[����`'22R222d��Y���.o�T��K��E��[�f|D3@O��������G�F�U�iW/�{\#w���j������m8���O-)��o�z��Y���:2�Gf��d��r�w��	t�Y��SR��c�Y�!�7/��0| ��_�gksJ5r�p:��'++K#�g�{�\*��5s���������(8'�|�P3kf���7����'N��u���7����|]m�����s��[n�q��Ih�{+y���Jc��F�!����5����9s4j�r�J��g��
5s}|�7zo�i9S^����4�F�-�Hd������z�j�Z?6�Vm)��]b\�L���E_
@keu��s4�6jP�<|�X�.+**�5k����k���#��6�F�����N���zGl�������_{����{���1=�G�4o�<��utg\���z��\��t&��#�C�4�F���5����
0��
���zY���f�n�9H#������|���8}A3kf�(6*�������{��W_��7Jii�c�-��F�t#s��9�h� ����]�����4���A��t�f�����p���������k����:��qZ3w�zp��v����������$ ���\,��M��[|�@����4��ke��3�Y������Q}���Brrr���^���\G�V���2u�T���;d���������SR����Y��@b���f�����X�l�F������B�)����u��i�^ks�O=2Ng�z;���kK[�QL-��t���\�d�f@p�t����I2jP�ft�jD��)��g�������a�a��IG���s�|�VIII2{�l����e��I��xW��c"u�'3�&������7b��\\S(1���+W��;SH1�����H��q44 ��v�S����,	���z3�&Sk15S{���bn75��#��1�`���3r��J3w�z�
������x�<��|��eN
�q}�d��5r��A]m��C����^�x��������������N�����}	����7
�x�����d�>�Y���^fw�$�,��������,X ���@[���_z���������Qx�R>��v��=���;9I3���<�D���5����������l�N/j/���j�*�����e�?SKg(����ihhp�������YO�^��S{��]g4km��y�[�5�+j[�L��6G_
<��xVXXx�9b
x�������I��?;%�h���6~@�L\)	�5��v��az���G�ua�R���hf-v��$z��4C0(**���:G<f����p��Q�����������Z��	�,+�T#wI}"<�b�)���Q�'8VRm;xdp����Wz�'�;�6x� C������G�i��Qdd�L�4In��v�5kV�����:x9�C>���J��Q .[�[����4��� �X���#���kdm�U�4���Wz�����[�Ni&�/�V��<#�\qT�
)������"11Qf��!w�y�L�:Ubcc��v����W��Z��4�����z;����J����Y������)����@4���Y9u�~w���>X3�S�1�Sw���o����,Y�DW����_&��������?�g�<�uL����[�[��rs��g���������A�����r�C�%3���>���P�Xk�A����s���o�q��IHH���1/?*�%E�Y���{"a��z;���9������_�^��(���dggKcc�#^�`���2m����uRZZ����h������9�'����_}m�f6yyy]�Z��kt����Y	���'�6�3��k}}�#6_���pG�


���y���T	s��,��Wl�+�o�v�f�-�H��g��&��w�<�O��yZ3wO~i�\��������_�T�n�����:�Yaa���HJJ
��I >?r�������a	�2m�9o���7�F�r�'
8PW����>�~��Rw`��X��u�D_y�f6EEERWW����#lF8����U=���UD(�=�G�1���B���l����.����c������7�$��4B�64���G|H�+���d����Y10FLM����$mx�c���I��{���.���	���9E3mEm����B��\�y�>���#�h�c�hT�Y�j����vLO��I2WBB��v����u��9�����x���`/z��{�R��><����U=��c�����e+3c�F=KM]���#���j���G��?�!��i������'�{<2���@g8y��<���2e@I��bcce��ir�w��3:<x�T^*5�^���S�.�+���\���O�G��2<Xb�@�X��If��$IJ`'�3�*<�������7��SU�b->&\_5H3�/�����| S���������$�={��~��2q�D����[����>�������C���F�|�Q���mi�����.$:^�o��f�f�Br�W�������a���������/X��qD'�w����^'�������~������9����'�����5C0��b����Y	��'�Q�����������Sxx�#W�����SSS%,,���@�^��7�3o����k�,�������+�j!�m���
\Q�<+,,��III�98	����o�
;(	Q���/�
&c��u��~MR����������|:�����WH��4C0+**���:G<f���`�_�u���!f��"B��m���
6����;fp���d���`�:��#�s=�����M��>����c�4C���;I�5����q+W����,Y�z��t_��ES
_1|��������%�
�7'�������/)��0B3�Em����B������'�y�>�������<j~#���;�������J�4��8������r��7��<(��}I3;�����xgUa����������@�FY�%�[�1@#�-^��Q������j�/~<��
��}j�G�nE_
��~�\�
+�y<0`��v�m2k��65��'�� �����t�T����~����B��K��~���+��:��@�c�����R��XfF�F�f�:@��]�4x�">:\�y���+���������`*5@����T~�-�k8��g�g��n�A���t��&��['��~\�~�a9���J�[���#;���<N����$|���w��9e���=�g���-�C���()c����^�l��(a����������_XX�#�&??������4�����������4k-eh����i�!�qd1���������,V�\i�k�?j3��U�4B�1�K����������1�*((���G���JM��@�^��7�3o���9����Lp������gd���r�T�c��u3�7�I��P���?Q���`��P�i?����Wg�y�
/=GRRRx�N���QYY)���V��~��1�2o�8p���k,/�����.��u�O�j�,2m��_�"��JX�$�tEEERWW����#�@3�<�Y��7+n�$�Y���b�
Y�d�f@�a�:��#�s��7�G�m�����)��y���9L33��#�Z�M,��z��i\���w�)��>��@�^��_��]���K��u�|����]��H����������>W#�E����x
�{�'j[�L��6�C_
|E����#�^W??N�<)�do��z��A}�&�u����X�S�N�������#�j�"4��D��Qd�<�����G�g�y�������h�
6h��4�P �nmn����q���������������
W�$h�����%X�!/�h���}5�'1�dee�4x��<B>������.o�T�������r������������$z�H�g�(}��)qyD"'����/>������rK4r7s|?���L�GF�=���K��x�^���`�}�v��y�f�%+7�x�������yY*��M9�|�����ra���x�����"��J���H��^��/>+�7|Y�GN�[����������� ���i�.3#Y#<����.;=�z�j}����ld�����������8Yn��*G^w`��?�y)��"�|�Q���mi��~j�/B��KT���
I��{��_J��{$,i�������n�6������0Y8}�f=���s������*����7�|S>�+���#$<9Cn��
G~a��R��/I]�G�a��I�������Q�.yS�>��DN�A$"F�@�c����,Y�<BBB4ks���f$KX(�_�V�X��j�e����R8��������c�f�9s����;r.F"d�=��;�����5���;*2m��~h�$~�I���{��%|�4�����Q,Z�H#��+Wj�;����j��-���#�gs���Hd����c������pI����l���/����������N%HD�)��[�JC�A9����&�U���B��KT���
���=���/%z�=�������Q,[�L������k����K5r7b`�L���������e����a��S�R9�����d��m����$Id������J��w�G�Gv���<Nb<(	�Q�.yS�>��DN�AB"c���:�4��X�B3����S(|�t�Z�c?|�0��Fx�j�*�DV�^�����s�����,��F_
uuu�����G��6��[Jd�p�������OK�3_��j��E\E����-����"�_|Vb���������B.^�����&�%x`��6;��0;�fff��9st��L��u��ICC�#6E���0G�M~~���Iiiib^Jh�������4s��oM����������5OOO��V?���h�O�1M+��/w�0�-���g������;b�u
w������RM455��(K������Qn^�Q3w���TI���Fm����B������'�y�
/=GRRRx�N���8s��l��Q��������H�.J���}e��'K���R�����Y��t�^��D���T#t���"�`�1f����p��Q����bV�*"I������L���%K4���#����������h�ZFZ��P���7�$��4BK��6��_��ES
_1|����b��2����h�Z|L��s�,�j[�L��6G_
<��x��`��������
���QW�<�<x�/Z~zw�[��?�So� ,B�>��D]q�.]��#�3�<�Y�CB�O��yl�8�Y z�����#ca��������>xt����5rwEj�F7�j@ ����S|<�U�(kHtT�,��T�_��O�Ga��$��O3x�1>���	�e��h�.<,T2�3|��<�y�����5r7%%Q#�}5 m��E�o���g�J����u�O��.	k�#M�������|�$<����+?s��Y��O�-�-��}�-[�8J��u��ICC�#6���0G�M~~���Iiii��J���On������v�����{�4CO���$V?���h�O���������j�*�h�������Zxx�#W�j�����DX�����/�o�n�f�~����>"^3����S-��M��W_P�<+,,��III�98i�����Z6l� ����b��.L����Dy�#���I��k�71�>$1����EEERWW����#�@3�<�Y�C��[E�"�wV�-�0Bq��y�h����g���P�|�����C���q������;I�5����>V
,+V��%K�h�����b��/��{����W6�9�Yk��D�����@����_�T�n�>���W�y�>����q��)��q�TVZo����"Z�����pZ,����)���z!qyD���I�����u���!L��)���p)�����j�npR�G�1�n0x�@��H���������s�W���x���|<�/����r]�SV&������Ai����<�h��9z�����gY�%�[�1@#|�|��i`�y����Nhd���C4 x�W���;'�"�x_Y(����|3��QW��=��|�$<����+�L�	�-�g�;O����#��dNg��~��������f�)���|�`IN��������������w��OB$�(Y��Htd_������#�&���$���+�\��yA
�]����z4%%AF���g�v��msk����\��Y#�{�/��k�I��8Z,�E�L�
���z!q���d~^��>j�%K�h������~�i��-���#@��X�B# ��W��+��������e��8�n����p��	y��7��i�������k����(�2|�,����������I�g���+n�z��:`��E��\�R#����jd��#@{��3G#��K�j�W�;!������m�����])??_��]+.\�{�J�����r�>Ln�xK��{��*��^��$���%|�x]��`���-[�Qs�����5�l�v��#3x���7o^�&���k�W�;�����D�"5Q3z�j@W��}�l��U3��K�������&�F�������!��k��DD�
��G���2�|
%�������P�f�f$k@�97��^��$$s����:�����G��������@���t�^]C��]0@v��#CC����������V�"$�c?�������G�j�*�B�ir1�ES0D���h�.91R������a�X��[�Y�RHH����O��'���������Y���d���r��Y]�w�:B^�?X�����;ey��$=�Ho�6(M>��DNa�;>j'S1���tM��9.�LL�K{.SdzO�G3h@���f�/_����h��t�>����.����z"�j@g:z������1���8z}�9[)�E�%����������|�$<����+�n���
������{�H��Z��-�H���s��P�@����G4r78)Zn�5H3z�j@g9~��dggKcc����S� o����B�����W��b�}H��Z!!��>�i�<�z4~dI�@����q�~RYY)�6m������d��~24�X���\����EH��~,1������#����^��-��]�tN==W��
y��������[��xc�.\�����a�&��+�#W����1?���"��^��4I��S9e��g���%K�����.��@0[���#ca��h���?/�#���!����p�DE��Z@�D_
��m��Iqq�f�J*���}�����-�-�f��%N��V{��o�������t���
�S�������W$K���z�W7����2���
��{��������~���������5����������-��\�����BB"�uXa���<Y%�
�k���i�5�Y����?�����o�����D�l����Ce1�]�,CB����������{!����KL��ux�����)��]b|�,���@������������2#��f�Nmm�l��y���t��`�\�S���L����{a��$��OI�����a�����-��]f�G�gzo�iy�}�MY��D�gn������kf���%�����I�����|�$<���
�+��������34s�0c�F=�?�=���o-��a�;;w���G�jfm��$���E�7�]�,���$���+�WYX�r��������3����r���`�6�~w����e��>�`��^���>����(<^)�
�k�n��$������������C�{�n��.	�O��^�
��+�B�#%�c?�����
h+������Y�[������,o��o�b<���;eee�i������z���/%=�HW��
J���>%�S�
h����<�oh���G��zk����G����b4��;���y�444h����Qn*���I�����|�$<���
�+��>�s4�������6k������=����;��F�>6@O���������Q�;��`j�uS�h����������v�v��3�k5sw���v�n�:�������Y:��������� ��N�>����'_���s�����!��k��DD�
��������MM�;���2E��K�j���E�d��U�yg
+��/���W��5����-�y����w����!;;[��$4��Ch����K�����Ez�-��������K���4Co�����Hzz�F@���9����@�|�=|�C�0�nm�E�Z�Y�b�,Y�D��d�[���;b�� <<������K�����JXX�#g��^�����{��h��������N�@�2����J��
���{��WoA{S-��
|a6vi����f�m��q�5�<�g�����#)))<G�����Wrss5��^�-)��hf-$<R�>��DNY�+@�STT$uuu�x��1��4���;�z�p0E��'L��|�/L�`���>5��������E�>6@O������0Y���v�R�h��ZD[j?�~��} RRVc;xd\��G@�P]]-���-��@��8q�����sx<
�.	�}��#:�G�,���N�N=-�����93�K#�����9%�����3�<��`}l��hmN�F�����f����#g��`j��S���Ex@2���~������I�����&bp4���G����]�����t��QVV�F��#S����3g�~$����y3���:.����4�9�Ix�i	2^W�?1|`L3H����O\�Y6��5����������b>v���/�c�5��o
.���=Qn~�)���M6�`b�%]���jSs0�g�6c�M�����P\o7o������S�z��=l�r���'6\3������~�����;uu_�'�~���S���r���N���_�T(��Y/�!���!�
2��j	mj�w������V��G�
�7����1��?-WG��		q4��k��
zK�rmn�j>q�l�2��y*�8�f5���u��[�&X�'Z�[�����ceJJ�f��sm�
'�qZ�3]���|��6@qej7���nX�u�S���� ��
�g����'�4sw�A���A������3��W=��������!�u���L�.
�M��5�������?��	�������S����r�D��5�@ga���L���o���ks�/��������Y5��6��R�pl��q&X�'2��w��];-Y#���6��
'���y����(eeei��k������MV|�����a�12s|?�#3xt��}�#{=���U�PUw�r���3g�F�o<)���6���#���\&
<��XK=�E�������iwK���4���#\��Q\�^����6��5��c�D�������-d�_|�m8�-\R\7���Z#b�kj��m��G��`C ���aO�8�4�vdN]r<2����$�Y\7��g	���~�i���w��X %e52 �V��:��ZXQ(i��5sW=�~����@gc�(@,_�\��cPL�y7[r-����F�`}l��h��~g��+C�Gk@�2u�����l��Y���_w��e#�S�J���Q3w����`�i�&9r��fAw��83�������������y3���%��,��������I��
�ZDX��]��������'���~�;R3��@W�x55��55�-�Jf�;������jl1�@���U�O���|CB�?�ff0��������-;;[�	�`�	
e��"??�������V?3����L�|�G�k�������3i�A���<�D���5����������(�{m���uf}�<VW6���������V��S����a���zGl�/����\HCC�I�������Y{�W|���eG�9�Z�9�����5L�n���}�'}�������=��B��^o��q����JL�������>7�����E0��|E�������s$%%������jy������S�r�����J3k��</�*k�Z��J��[x~I]]�#3f�DDD8b��a���bV�*"I�9�i�j��5p�o����V�2���(��>��a�p���G��������P�����s�x��@hM#4�Xs}����R���0��o<	�f�����`��/�����B����M����}��9}�f�Enn����W3k���y�����+h�`��P�����jZ6����tU������ZP����u0��S��o�
u�3�+l��e3t����������$cH�f�&�-#��j�Z��+$r�
���`��#�3z�����0y���e�4�Z��L���ha�;��
4jf
���X���=�����-����CL��U��+����:�eU��V��Uw5��|���;Y����k�.��Gf8�lN����7���ZQ�m�b��iLV��n�Gf@���^|�����}��#s������e;,�BB�$����4x��GtG���.���������I�^���K�	����d����;�Z���6��6���9v��Ir��j�n��d��}��9��<1M��t���8]@_M�1;3�E�0�If�$@gz{[�<�x���_����$"W��u2o���%U��'������8����$r\���@�b���k�i8q�\�'V�i�e��6W�H\������z'�T#w����4v�t��;X�R���eh��WKm�����aE\O=bc������������������=3x�������t���c���1��oeu2�k=	���L���]��3yr��ZW��������E4\��'�O�K�>���D�\�+��0|��p�zeff�������+��q�tg����DY�%�[8�~��R?q���Z��}|������i&����������T{l��`C h���[5�7w�\����`a���4`��Y���j:SVV�F������q�2����Brs==	��*<^)�>�O���=�������;�������)'WKlm�S�C���O�J�G\�+�;1|�X
�Bz�w��Jy��q������'��#�@�n�]/���G#������dG���7jfo��Y2j�(�D"o��|O3�����a���#O���i�K�.u��h�3�k��/����.�v��U�&8/����YK+Y/��5k6`�������tt���WSsx�9��9�!�����
y��lvK1E����*$���lfv�un�	��������������x�������~����(���������[/�
��������KRR�f�JV���h�O��-���#oM&���|��3�I��{4�M�����"������A����+�o�9�)���\39F���G3�������~���9R
��������%!�qG��h��O�O0�B���_o������3��x���:��
����m�*�J��}2��Y�^���!��������YC�1r��oHc��MOy~���9??���5�3���[�S(h�z��u�s������;��������������\\��*�l�<2f����8..gV���@�����k����A������(�Y}o����j��Y�����e.gV��^{������a�1\\\�s������Yk�l�8p��KQ���y�	��/zC�&���3G�f�^�q�~����|9��������=U��o�����6x4"�I�;���>5�2�d��w�
'�2�#
��-?�����\\��rfu;Wo��>�Vw ���A#w��!2sl�f�#�k��
O�Iu��/��
��@ ���r544��5s��>j~�@�}RB�O��`��������������������_?;��
�;f�����IHS��Zhj��'VI��k���S������(N ������Ls�nv�-��-�y���1�2�����G>�c���lill�~�|�U
mc~!��:i���G�^�K��!�G+5k��e���j������HZZ�F�?K�����������g�)L[k
�����%��?��=�:L}}�/$���+����\^j.NII���0G ����W�;)v��A�������W�[;�-�+�������e���y��5h��Gk����J��f�q������5i8U ��4���J��Gs4B{S-��
��H�IWp�7L�.EEE��#c���9�.����,��~�|w��a����I����w�^]�6��*vn�f"�����{�������F����J]]�����%""�h��������Y�C�<@+���9�#��
6h���}]��v�����7....��]��n���
W��J��#��i�-?���\��n��X�;��#���uW[����P ��������r9�����+8�C����_��5[OIEu�����G������[/gV�;_���=�e����..��������6x���*3f���qMe������� XQ���
�����\\�����k����3�S��!��e��:q�����3[[M�Q�������Y~Nv�3����z����v.��~�}>�%����#�JYYY5s}�`}l��`m��/����Y�4�a7x��:Cff�FmcjD���8��R�q�Z���4�UdW�9��MO���@bvz6�����t���Q�d��Y��4V���g�.�g�O;��fj6�u�iO�yy�)��C�f��'?����=i2,9Z��9#�7o�[�%W��S�j&�q�����f �1|�X�b�F"K�.��m�v\	��vksJ5r�pz�F�=�9xd����r�J��&�Ow=����5j��l��,�?���r����"d��>�MMM���-�O[i1t�P�;w�f?��R*���4��
tS�1;s��s�e���&3;��+��O����F]��6<^~p�x��g'��1	������i��5����r�r�rOj���I�G�:���)�GV|)~��87��Z���&��:-g�k5s��1@#�V�<��zj�����k����@w��� ��������:_N�Y��e�%j ������b��
4H����.jlt�xTt�.@�r�f��
y�Zr������������:]���)_�3E~�_Se������y;m�E�W:���������������?�<r-X�6�Xq��]�#X �e���z4itI�h�������C?�'<����&+��8<@w()�����=]8|��������9���c�Y��������D������m��Z���������.]���t�F��:WY'?|j�/��k����������+����G:�����Y����>1�|Fb��#����^������98������9s���[�#X ��c������z@��&�0���x�/�u�Tg��b�]7�q�Y�������l-�����u�c��w���# �l��Y<�����D�{���H]�x~�������.i������@�p� �S�����,[�L#���7��O���<��6�����zyh����Ge���Y^�SF��q�1�Ib���#���hj/3;��wp���h�n���n7^������F�������g��������%;;[��$4��Ch����K������~V=�������Y����J�"4����i$�����=�~�w���@�|�uL}�y���2335k�
e\����Z�o�Q�����h��\__��M�&<<�����ihhp��������7��_8���O-)��q�fm����o8)����Z9(V���SP�@�m�6���f����������+"������K3k
���\��el5tT0�B����A���]�+����z�������k����L�i��U��<�g�����#)))<G�W���W6��D�����'h�ZEE��gNM�����U�����8���K����?���I]]�#3f�DD��8���;�z�Yq��P$���
����a���7�b�����G�1�7��/w����5k��i�����4����$��Fhb���S{���������!��)��>�����_�<b�[�fi�����5U����{�cO�����5k��yC��I�@w2;����G3kQQQ���~����H����-�kf�!.Y������Bm���B����x:k���=��/os�u �1\�������w��jf���DY��I��z�x��7�TI�f��k��C����w�w%��;�����c���&���!L�����)��b�7�>m)��c������G����5��4���_��rC
�]N����z���V���j�:GN�9��e�%j�;}��^��p����y��j�/������k��<��bzR�R�1L/u���=�����������m������1��j��Q���}�t=��z1�z����)jXA��NKcK[x����EV�}�1�O����_3�6l��k�]s�����a���Y75��u�C��Z�.�0��i���oO�y��n�I�JFZ_�t��������5�gN&0`�f"UY����Y31����I}������nZ�X�j<-�,�D/��o^)�76kfmXr������'��$����%���f���n�A��K��+$r�-�
��Iv+�s<4�3eggKcc�#6;z��2m����uRZZ����@Ot�����Y�e������o��G#�X���zH�}>@�[�~���77�&��p�_�@AA�4448���T�i�����#���j4ul�<��������]sD�^}X���HM�'�8E3��<�7o���=3x4b��D��=%�o��fvB$��_Kx�,j[~L��6@���xVXXx�9����sb�������
HW|~���+��,�@�6y�\�'�N�&���D��������WTT$uuu�x��1��4����U=���6p�v��X8��.����.�l��sr���)I>z;�D~��Y���g�������r����g�������C>
]}����.l���G�8�����w�y<���<0�v���X�lz�=������+�dK�O��S�@�3�Hn���C�wf��
6h�s��q�����'N>����#�F��-���c%}D���kS5�c��@���t�g����_Km��>x���8�n�*���?���w_�X��<{�f��z�?�=.�G+t���><Z>v�0G�����ou�V��+W��Q}4���;&����f�f��������yE*^��f��?����H3j[�L��6�C_
|E����]���xR�����=��$�1�z����{���������b��S��w~K�G\�+���`������xgU1+n�$�����K�j��-Z$����d�]���#�����T]����m���^\|��1r�5C5���;I�5����>]�zl����ZM)|�����/�l�f����O{p�f�<
9������~Q��_��wI}"��G��@W*..���,���322d��	����Z-/|[3{q��@����Y3j[�L��6�C_
|E����
tT����?�����v����W�����C�e��HA�4]�v��-2��/H��q���x~�><����U=��� �z�jGAf����t���O����^;-Y#z>S��?��]���F#�l���T�4��~��|��;�<{<2V<�'��g�f�n���,@w8}��c�Eo�S�Li5xT��]��n^�6x ��W���{��4x��{�l��
6�������1���~���@�c�(��b	�t���%��39I�'Fj@��@�i������3]�u�Y�����i��EM]��;T������r���5�U��;�<j����:2�G-�7K��_��^��_��Ywk ��W�e;
�������/}$En�9P��j��#�������+���Ke��IX�h]=�G�d�j^�n��2�7[Y�b����\f�\Vc�Znk�\Q(@W8\\-;�������}�k.VZj-��3WK}�J�mv��$��q�n�($DwfWgW�m:���<x�(�t���
�{���j]����&����!��|]<�~Q���I��Ok��������7��>ih��������
����]���3��A7JCH����n���7�%���u�T�2���������4���g�2o�<����]�|������cW�\�8�����������,X ���@[���_z�d~y���vO�:,}��f���
���������G����kt����Y	��'������i<Y�j�f���]�>�������g�&����������1�*((���G���*aa�������s�Of�&��d�f�n��F��m���O�Kq���"_�3E3]���F��������bm��1r��Wk&�pr��?�Ei���q1�>-17~U3k���+�j!�m:��xC�������s$%%���:ZR-K��~CuO�p��5.l|F��xBv�I�'N�Uk���#�Fv��G<?{EEE�N
6��#""1�f�y����0y���tb
L�/��L���N,��e7g��t��\��N=��#SCi�P�k��<�iXqfU�1�3�!to�RV�kd�����T{<
s�%j����~}"8��bf��l��m�h�����Or�x�m�(z��^��}5t�3�k����y}?}��!��m�V��1xT�4���Q��i�6x��G�a����w�[�B���������t�myer���f�2�3|�5�+V�����|�s���)�j�h��e5s�
��J<4KM�G#w����H�`q�F��BE��i�����2vH���&�.����%.�S���d�JJ<��6d��V��7���kQ�o���[�������T�48����k7\9P����|����r������9�7����9ih���������v2
(�G6�����������|��u�����"?eh�����k������\����\�����:CIY�F��M�����8
����]���r���%2<D>�� }�<��t��$�q�@�t�Q������$���sR������HW�EN�Q�nD3�F_
]��O�����5�6��{�%O����������}%g���#O���d��Y�����#?�3���-o\�cV�\��e��aw]�[]}����>�vZ�F��m���	��9���}�A��������&�Z���jg����������l6Yi�*�_������2J#]a���r��������1x��P_�8�������F������
���y�����2��MO�+��?^������?|F����
�?)��-NEr&�g����'�����vr-P��|#3xT[o��c,�`�8��\w�@p+=g�����2�o��D���3�h����'������Q��!���Y7Y=x�(5(V3�m���RT������Y�`�DGG��H�3�%��r5��r���g��,���5~��|Y���f����#?x�}��v��r������T���	��I�3x��o_�@o���K�<�z4s|?�t����TY�F�����9c��O+�UJS������n�`���g�G�c������I'��e���9\������J~~�f�bcc'��]~W<��������WH���	�U�����P�l=���Q�b���KlT��4���Or���%g�r ��
'M�$�Fq*0���~�z���q������(����z�����4@w+9����(��i��wd6'������"$9�y�������7O�U?�#���4�k�0�@g��k����W3k�����D]�xq���]����������(���<����_?$��?��5�y��� �Z�����O����e��O��x��]M�2E�N����>j�9sZOz/^�X��q=n���
�B��}��Z�c�QLT�,���a��E�,]���M(�W����c;sP��:����G��Z�?Z)���4s7jp�F�:2�G����8�����+"��,����kf-��H��Qh���������<��Q��I|L�<r�10FWD�j*��o_�=E�$g�G�&��`~LL�\s�5��#��1|�N������iNio���8� ����
�B
�OksK5rg��BC4 0dffj�l�����k��c�pP@�9UV����:|4vx����+*��ega�f�F��|�����Knn�f�,X ��L���'�f�K�YM�<x��S���D_
���vdN=���U6'����iG��)���ds�9������a��?10|�������Z(1�7�l����K!h���<��0#Y#�������=����q*Z�d�F��\�R�fvJ���G-'���H�0�]��������!�ww�5���d������;wn�������\���f�Bb�GaSu@W���Z��)��,���#L�ic5��� �}L����S�)���9���xdN>0>��U�Vit�)x���b�#�a��@b�6����>�]!h��������0	@�0
$�53D�S#���X
�6����
,VJ���G����.����x�������t��a��q�f�f��%�F��L�������?jf-$<R�|�I	6IWt5�j����N�O�������K��'%i&R��K���3�m��R�}#3ld�����3��:h��u]���b.S�p���)��6����w^1s_�&�B
�V��!��t�z'�tsbaaF�<f�jX��S��gLm�j���1�����c�w�?���\�f��/�
�o���n����A��t�'N��q��3$5���E6�U����f���G��gh���W@�m�+�>�O3{_�3U��>@3��o�V��| �}��hN5?��Oh��Q���U���)n�^v\$v��,��������P�������"3,�i�������_���+-�<�!��a���
�?���^J���@w())���l��M�:U����H���j��4��O%"m�f�}5t���4x��[F�-sk&R���u<TN����g��#s��9��
�G~�R(i������R 1;���6�T#wi�2|�E@�3�@�n�^�c}(j�����*������G���q��o��?-	@�;{��c�����t2c��I��E��K��~����;����i �W@�<Q)?|z�T�x~���F�]�O+j�8-����\;Tj�������t��#O>�S�hir��Xb�k�#���
$6lp<^�})���J�jd�g4s��G�`b�&�~b�(�h��455y�����S�a�v����YhH��
k�0�����j���w���?�
s��9��E�������kf/������7k ��W@���q�9o:�q������#q�����O���Q���aC����,��5.xb�kj/3
�����;�����C�
�����{��mv+	i�N������_�>�����Q���=&������"/�h�DE�g�#//O#���t���a�s�;�!�����6cX�ghD	n�k[__��M�-<<������K�*���JXX�#��_{L~oS��u���;R4k�����'5ka�kP�o�=V]5H3�W]���J��]+����k�y8k�,�D���K�_������K���4�?j[�L��6����y�
/=GRRRx���U����{$�h��X[<k�<|�XG|h��5����p��aN:
���x~���������3F"""1�f�y����XV�)�=�G�1�'������x�f��8s�|��4�����;I�5����
M)|�@���?���'4k�37���3[��������
4���}m����+��`���������bm������uE�K��_il~�o'��/I�5j�9�m�W0�B����:���O}C�|�w{d��C��LM�e���s�xF��u�^E_|�;k�5=��#��=�����xgUas�����y��#��i�t�����
���ei�.�b��X�t3�����u��4?:��-��>�}�h���>xt�G���u�h��~�����jy��?�<x4$!R>t�=b�t=���^��������(Xt�������4�l��8	
u���������W�@W2�G����Y4h�,X�@3���R����x�\W�E_}��\�%���������=g4�6%%Ay`�;zD^���R\��>���Q�d�-���h]h*�@/��[����4�Z%�j5rgu�QhH��
���������c4��{�=9v��f�����<
��n���R�������~iD]y����
�����HV�����������eo�Fy7{��H��b/��F\5M���NW���#?Z�r�,^��q��hG.�X@gjjj��F�;O���:��eNg�|��_�>cUoi���WS�(ejV'c��kt������4��Sm��Y:����}�:�""��&+NK�9���QGn'r��$��e���`����k�<�:��|������u�s�����B��Cw~\����+�����E�.]*�W�v\@ [�a�$sL�������������?�R}����F#w�"%,�zH<m�oCE�Q����m����@3k�����q11�'�5�TJ��_������N��L��s�f�}5���������5��7>B���Dy�r�l��z6>�Z���s�+��Q�	E���u�~�i��-���#@�0�G�Y
=���#�S���aN'��)��79�<;v�����kf-**��^�O�>��
��GGv5�6"��H�=Oh �W���=&^}D3kQ����B$o����?��ET}��!�o{PW����0G8S @�Y�k��q��d�|�,%�j5r7�o�F������;�n�
�x�������}�����<	s��������c���p�f��GO���?��`@_
�^�tR~��A���F��g�U����Om1����x�xu��t�L�������}:���,Z�H233e��9��>������w�}���{����!�E~~����iiib�� ��_���G*4k�}���4�+//O#���t���a�s�;�!����S�V�X����A}&p��}}}�#6_���pG�


�������:�����yD�ZuX����f�|��1���T�0R���o���e|����v�m�6��-\�P���H��K�v���Y:Q�?�+	�I���Gm����B������/���^z������!^�T,�x��f�F$V�������NW<K��'�o���
�+=��^QQ���5�3f�DDD8b���xgU1+n�$�q��n�:�h@�0|��`��s�������G�c�\=)I3��x��@hM#4����=w����57��Z�J3�d4��@�1�W�	���o#w.���_�X �m<�����;�p?��
����h�����3H�1B3��>"5��jf-l@��1�G	�t�{P���`��P�i?��v���
u�3�+z��9&�����2������4�,��B����;�!!�}t�w���c���:�wV�&��4�83�-H���	��e��S�KN�d�T���e��i�`u��V#w�Fid-mx�����]���1����4xt��W�<����^�B����?���G����=��������z�~�)��V�����z�{���z�	�� ��>_+om�>�q�@�>+V����(=�a�(1R#ki��/���I���$�t��Q��a�f�f����i�E��_��-�kf-$.�1x�<JW3�j����R$}��f�F$V��'���g��x�V�A��*I����
��0|��<�zd�4��C@�ijj�#��4s�������$4���G�����bY�n�f��O�.iii��Tg�F.��g���D�J��?)�C��
�~��|y)��f�2����cK$6�QW�E�W��������$1�<�����#��xc�I��-��,���5�zyG*��������pI�r�QhH�\=1I�������dj���>}Z���C��L�2E������uOI�;���FH������Su^?|j���j�QhlD�\?��LzNW<XQ �J^����J���u�k0|�Ns��������5���K��L�f�_5P#���E�4�����*�X�F��G�k��7����O�0B#UVV�<����k&Lp����w�z�4�g�"Rgi X�W���jd�o���]�u���U��	'.�Y�+���n��$_<�+	}��t���i��y�o��b��������i@�����=�9�����G�����K���b������������'"h���
Y�n�TW{n�LKK����Djr^���Vjf/�c?����h ��W��J���������2]i���g���%��+���+d���d����	�AB���-]���X�b�F"����,�+����EM�S��j��%��^�ZV���������##s����yc���wf�m���-:����1xT^^�+�RRRd�������Z%�/?����� QS.�p ��W��W�����}�������O8)����V���?���WK��+E�"�����Q87��/��=�z*�������e����~�z�L.�686P�3n���G-��B4�Q


���-g���k#F����gk&R���x�;����y�DM�M3=}5��f��r����S�'�N|^nR�ckt����
2��?��
_����*@�a�����N-���PB�Em]���rJ3w�����0�>��e���;c�]t9	 �x:�()!R%Ei�;�������
2���&u�����kf/���J���5���W�-��?+K�GN����f	Q�r��Sr�0���-��+d���$��V�iG�s��[������9����m	Z�!6lp����4�dffj�6s���y��i����@cc�#^�`���2m����uRZZ����s*�����_�T���_��4I������4IOO��V?���h�O�pnTY�|����Y�PR{�3���"p��}}}�#65���pG�


'5�����@�z��c��������������������������Y0`���W�����v)���Tw����Y������6j[�L��6�G_
|A�������s$%%��H���Q*��y�f������#�Hx�o�V���%&1I�|��6l��+<?{EEERWW����#�@3�<�Y�C�����$��t�
#��b�
\�w@�0|�`��������;O�(?��Jt��#�Z�M,������.�R��� x�������R�Z������Gh�|��m������IRR�,\�P���O(k8�O�����2Gn'z��$v��4|���+�j!�m�����:���o��by���5�m��#�������]Z�zI=�I"������HB�$�-�������u���!L=Tn�9��#��Y�4�^yG�k�#�5���n��u�(!!A���y�����?�u��GQW�T�G������
��[-�O<���QB�)�u������i�H�~��H=��Oj�.91R2����������4s����rrr$??_3k��������8G�x�X*����?���Fd��w��4��_���}�����ag���S�|�7������?K���{��$��G���c�Br;����3G�n��A3��3g��(a���������/X�@BC�?��0�hmy����fy�$(J��������O�8B>�h�f@���b����Y	��'X�\�R���d��hLm�����������UAA�4448���T	s���}g����@��������3C����
���;w����5�!�]w�$%%9���sr��/H��}��N��%���5.���+�j!�m�����:�Yaa���HJJ
������9������=��$����g��2��mT�/��$���J���z+|���gN2���s�c��q��pu���!f��"B���>��a���/k��W���_�])��Eit
��#�Z�M,@����W���o��W���k�%�w?9N����
���{�Jnn�f���.\(�
j^�������������@������-�
�Zu�sQ�<c�"�������b�D�>/W;��wC���	�Y�T+17~M�������x~�><����U=���Z���F�L��G P����]��x�t6����#c�����.*�����\%}���f@p��_�_<J������y�(��A&�)S��&qiWI����*=���R9u��8��W]n�n���3|t�c���[5�7w�\6l�ff��a�;�I3k�#�h<
�=����Q��������<���6��O�v����<$s����U$��e_�\����[��=��Y�����R����q}5�^'�\���k5s7nd�t�����M�����g��Q�$+^����]����!��G!������u���{d����(�G��y���&��gi%�e��$i�U��?�!QW���������J��W����W
�������]��8�����@g:q���_�^3{W^y����h&R��cR��
����)�RB��t.�J��G{������r���2�������\8%W������;�K��~,!	��/��@����GQ�r����t��#�M[���5�JJJ$;;[3{S�N���t�D�^���l��f�B9N<
�;TW�����1xTt�Rf?+���������Qgsd��?���WJ��_����[��GV�\)!!!�.����>��\�>����Fyc������:�V�WV������w��������r����QC����I�&9�Uo�R.lzV3k!�	���OJ���'%�Y���t��s5����&�����-�E�WH��WeR]������/!1	z+@p���!Vm.���F��q�4O>b�����w���������;N=jQ������?if-$<J���3	:QW���a������3r����[��x6�|��-zZFM�!}��9a���30|��N=���W�����*����z�Z
����VUU%���s�����ce�����\x�/R�����3������q@0yk[�<��.Y�rLf;����65���oJF�~�����o^|S���=���g��x�f�]5H#�������M�E]]�c����s�bm���r�UWi&ra�R��������	�H��\^}����&G��xT'�v�Q��C2���2������$b�l���a����%K�����e��Y������x�j�)��
�%3�5 8X�K\Y���:���#����(�����>}ZW�
6L���<@T��/���O4��r��������@O��[�d�z�7������FZ�{r�����X)1�YWz.��� W|�F��^�����zP�Q�S���3|�Svv��:e���1x�`Y�`�f"������@3{q�.����.O��+�G��aI��v�PS"W��L��!	�}J"Fe�-=�G@�[��X#k���@���\#w�|��{��'����Zrr���?_BB�wy��['�/u�����
��������g^�����+��<�#������^)1�|FWz��� �z����3�e@�(����R���Ykq��2zp�f:b���r��!������1x�����J��;bOb���D_}�f@�(--������H��xU_!��%WN��>��F����[z��� ��S"%e5���i�`���������G�?����RPP�����x��QLL�#�?�K*�}X�����k��j���d��5R�<|�����dax��?��D��OWz��� ����Gi��$#-Q3���������G��@{m��]���4�%,�>}��s����.x���4\�\�%���P^^.o����,:�+��65���l�s�4I�������;1|�G+W����;����]��O��VHn~�f�s���Y�~����U���:�>'����@{���[>�������;���������g�.�����N��;%v���@��W�.�����_}YN��v�QR�!�6�@���Q���]��>�������Ke�����lol.��]td�,�j�f�lf���f����>��U^U/��������?���cC2 d!{�Ab���	�Y���[[��vj����������jvj[m�$�fO�c62Ya�����w�=��������������{�y����5s��#���=*��������i���fl4U���f�'�3o��[��x�}5��RW[#�^������� ��z�T�[�I�!�xJ�b��;@+����(�P��V]�$�����a^���y�|F���i������A�28&D3�q��q��k�f���P��7���������fn%d��y�w5����WNn_)o����[�+�E�������oI��u����
�g
$����K}C�f��3��O�x��������1L=������������Q�����A*��%i8}���8R����P3��j}�������"��E��gC/����d������]@[Ao�-�%��.Kp���,��/��K��E��X�p�F@���a�45�lR^�x������������	
2�J}�����s���7g� y�S�5|��c�4?~�F@�p�s�/�!������G�>�l�k3�3����ohh0c���n��18������F3NNN��f���7wX���Y{��(w/I����{�������v$55U����H�_��c5s�1v�D=�K	�3���V���Zu��c_
�A��,;;������$�F������X�����B*%� _���QI9PW�/������H}}�'&&���0c-��t�]=�Xq��P$���'t���7��h>�����b��b��oi����L�%3�h�^������6�t�qz�O<�Y�����t�����[4���o����:��{��i2=���2��
����<Y�fM��!���
�4i�f"/?!uWk��}��6�8��Dm�g�S-��M�9��Ww����\�=c_��w�����5�������r�'����0]�?���F��u������y�E������-Hp9�g�k�*.6��#@��������Oj_u������0qt�F:RXXh>�Q��i��5U����l��%���h<�e��\N����y�x��$�gN���[���K���h>�!����������P3Wi�q��<���l`�Gr�7�������i�\�����G��[�<y��|����g�v����<h��x3LW���W�-'N���^K�*�t�ZDe��9�,��"�><Eb"���-��?�i����y4�u�T�F�&�a��
�T��7JMM���7~�x�9s�f"U+"5�������}�sb<ZW���[�e���"�
�b-������\��8O���Ib�q�@g�|��������u������|���GFEi�Jmm��xT^n��dHJJ�9s�h&R���R�����iN<�����7���/��W�HS�]�,��1�}��eA�|�~��t�G]�`��Zl��I#�w���@.��k�jy�0���/_����5k4��jnn�c&M��#����Y�~���{�F�����k&R��R����Y6�����t�j�!�p�|���e����20_W��ke��]�������c�sw%�t�G]�p�B������y��0*Zf������K5�?8z�R�5k/2�.c�#4���
���P3�d��E���l��T���f�����8�R5��c_
�'e������#���pP���d�P��@
���2�������7f�����+�
�����g��H�p��v$�\�e�j�*mS�����?�����+e�������6aT�F�1���?��{��
k�{��]����~����{~ !k=�}5��*(����+K��>9x���0���P�W�
�����������3w��A�!z]E�Q7���bHKK��Y��#�#�l�|.�G����&�'�xB6m��|��S��0��#����[������kn���lf^��m���w������-!S�i=�}5��2���i�)���v�[����j�a�y����GXU�Of�����������.���i������kJ����TY� +3�4s��';O�����e��������$�dLv���#��������������Phh���^+���{q�:�V��w���Y�m>'~z��yU�465��!��,%_B�M�k�7����2��K��x]@O�xkn	/invY����-[��'�:36�,]�T��Y�`�,\�P3�gl��A��Z^�-^�X��������������	
2�J����g�7�Yo0x��3%qx�f��:v��F"������~��e=��>�v���O?mn`i��)�����y�;�����36jhv����YVV�466�qrr��� \~e��r��24s��SsepL�f��+��v��-G������H���k�$C���R��G����e�I��5�7�m�,��P��:����y������IJJ
�������o����g.M��"�Df/���������l���o�>r��"��������L��[��%&&���0c-��t�]=�Xq��P$��q����HOy��g���G�t�G�+���9q�J���N$���d���w�_�4�&�����c��.6��
@���^���[(G�/~��>2L�s�p�
���{HW���
�?c�f��+�����/�����s�~ll��7��+e/~�bPk�V��y���s�m�,��P�����[�y��Cs�{�e��wN���%��b��"7�R#����J�q�M�����~)�qW�:��k4�Q����z���8\l�xd�a^�F�������3���j�����v$�\��1/�=�o�j��(�>�a��q���E�.5�="��B��G��#��NT���g��^���pG�������h@�9�w��f�Q��?����|����|�\
&�����z���oh���}�K5r5qL�F�SW�����5��(.��`���R��/Hs����!t����1���QQ� /�~B>����zg������'����]�_~L���$�%y��%d��z�����2���3F�n��E���`�s�0��6l� MM-�\�x�y"�{����?OJIIq;V�Ig�j��������o#�];R3��1�������������/V�X�Q�x���5��1js


fl���v�����,ill4���d��lf��v)�����f�����dzR�f��+�+���e��m�Y3��=���J�K�������fn%t�my�S��+�m�,��P�����[�y�����_#III~�5��ug�o��6���T-������-mc/����k�8���%l���K�}}=)''G����811Q�hA��c��!��KE�"	�h>��������:)��f�^��\����}�p�/��M#lb:�M)�EC�{�����������g��ov���W������s;2�|sS����D�_��4�;j�VB�-��<����V���Zu�wQ�<��
c���tt��ZW��<�\����5��td4��{T������|X����:O���C�<|TzF�F���3��#�'��V�X�Q���>m<|��s��j<�3g���G��5R��/v�x�����#���GK���|OV�=���h��^577��3���x��c4\FT��jG����@�NZ�0�.��6����&��������e����Y�1cF�SX���������H�'Q�=�p�?S!�y��|����^v���$�rub�L�/�ku%�z��_�2� 4�����f����#�y�z4qt�LO���s��FV�����
����\)��e���4��;��|������a�ijj���N�*S�L�L��o_���m��g5�l<

bK.���:�����������t�UTH��8!Oc�t����\�����9o��3o����j��|�4>���r���L=}��L=�c�p	u��!���h������QQ�KYY��xTWW�+�M�8Q�O���H�?�.�G�i��-~�D}�9	
���w565��W��?���i�qQ5r��<U�+����S����
3�t�D��3��e��]kn�t�<��i�y�e����,X .��mON[�x������������	
��m�������������p����y���G�;������5�����}Y���������f�?��F�5��qCC�54��n��,++K�899Yl6����������������C���L����7��_�'G������_��Y��{.���JY�n������{������T�.���#�����{���H����]AO������B��g������egg��5�����_#ol=/]}J
K=7��c+eqb�f�%m������8��I������������H}}�'&&���0c-��t�]=�Xq��P$���
-O<���t���>��8���{h>��PQ� w}+���l����Z� �����`�������6�����V�X!k����+W�J��w�����[4=�/�O����������dZR�f�X5 }��r��x���+p9����..\����kn�oU��
����f����4
K��$j[=��j!�m��}5��:��/7W�;^*��~B2O�L%����2�=�X3�����Q��4���j6�F�
@��	�G�g�y:��B�A7/�-Z���rp�
����|��#�
��4������LO6�=c��k��i�j���n�w,J��=4Y&��6�:*_��x�h<.'��@�����F�����j��:l<

���������a_
��������n<�r���M�2�����lC����4�-�����@�����������2&.B3`0�h:�}�m>/��-���s�U�5r/u� ��������'���v�P��F�Q~~�f������t���f�5�`���~,��u|�j 0557��<��*��,�����C�u�Ztm����������q���H��y
	����'.���+VX���|�r����'�4G	=����q��a��%L?�N���|�yRJJ
�G���*�o���f�����r���������%�~��e=��>b�����Czz�F�5�����36jhv����YVV�466�qrr��l63�y�X�Gr��4k/ed���35�?|����y�f9y��f�
2D���q8f^���R��3�$�#����K���;�m�,��P��:����y������IJJ����_�7G^�pV3k���e��\�+�����g�4'�

�����Z�	4��=_��|ENN����$����~�@�<sW1V\*"I���	5
#7��h>�������8,�\���C���_����x�_�k�F|����6�<�������k�@������kv�3�������'i�q���W��m��������e���f��;�)��?c��D���8&]�z����O��6]���c_
���x�k�����g}�f��I�2}�9ij����jT�>���Z3,1�F��stpE�`��#�3�<sW	�{t�����q.�2
�������G���a�Vk�����G���my���
���#�����������hY�x���Gu{_���(����x�g��OSs�<�z�f�E������2uP�W�G)]�.���s4����h��-���q�F@��gXo�1��W+W�����\�z^��J��U������g{��mw��;F���E�$**������W�2cO"o{RB������@�y����W����B�;��Ku�]�l���%�h�f�D���8&.�������,����|�\-�;LF1�O�.]�z��������4n8p@:��{v��l<8p����(/?a��D��e	�}�f��W�-�p�����f�=��Z<�����
�w��PzPW.����2�F��Kh>����|)��9��#�W��e�|�<5NB��pv�����f�/^,C�5����R��/��'�������f@�kjn��_���U��Q>:�T.���kk�����dPe��\���:�n��k�m���3�4r5yL�LK������^���i���Y-M.9~�����[3kF�Q||�7�> ���HS��[	_���-��f����k'$7�Z���D���S$��DW��������:���_�I	�����h>���\��6m�����r �L3Wi��z��g�}V#�5k�h���i���1�2aT�f������������#��1��T���\[i�V�|X��}D3�}������H�����f��Y*7�?'a���bm��]2��k���pv����3��W�|�
��/�H���������G�vIK��+���V+W�d@*,����i���G����{O#k���2v�X3n�p��x�Ta��f�{�D�}Q3�������,�y-[�K��D��*��q%b�b��������Y�I�
_�����nx��'5b�:���A��n�|t��8�p�[�p!�Xz����5r$���Cyy���Y��W\q��7������o_���3fn%t��q��5����o��vBr��5k1"�Zn�tV&+�k��#c��1����)�K��Oi_G�Q78opY�h�x����5r��Gt�yKZZ�f��wwj�j��!n��!77W#��O�.�&M2���
)���1���[	�r�D����������#%������HY,�R�e`x��X��+������c���#e�D���f�4uSzz�K�����b�

&�h�v����+���k��X6n��YK�qkm��@���*��s������G�O�G�
��S��$M
f�Q��-����%�����b_
����f��k�������&��)qO;2�V���������q�G���>���G]d@�b���t�M.O<��Y016�t�fY��� �
���o��Gt��6���O���bh��t�>�_���@#W#����	5`(**���b�\��5K#���}Qrvj��#q�D��fL��}5���������.{��bI�'�^L;2$����Hh��-lq)���H���A�Qm���,�����H�����1�h��X������3]�vO�|������&�|m��|���v��S�W��EEEI\\�AC/?.��<O������xds�
�'����q�D^�pV��j��I�ez�w��)�d���5s<h��\78����#�����T~�l=xA3W�S��z��=����������d.���F��k���7O����V'O����������W�%u�1c+��q�f���h].���f��k�2+�Tn��'�#j��g�!�2��_%�h���
�(Q���bG�
�
�G�e�i����y���5����MGG���
]�N��A�08L3�s��IUU�f���#��_j���+��0��S��i�@����'�����%����/%u�Od`�9]q�b>�����G4u���?.����v3�?���z��������aa�x�����7�@��x�<�����_�I���5��vM����\�\
<XB��Ajw�KW��lN<
��+���W�e���Us@�Ey9�(6V���1[�+��?�#���B3�+������zTVY��+����q�X����y�RW:/&�!Ki>�16��<yR3W����f�_4s/(4R�>�c����
p�\�pA�y�):wLW:6y�d�fH���zZW�E��q�_���G�e��=O#WScd��h�z�[����z��D��:u��444h�j����mu���>r�����C�$==]���u������^+�vI�k��Uk��}SB�.����#�2y/�L�,��UZj�F=g��b�:o��h��Mc�Z�.<M=RuR��K5s�h<r$���<JJJ��w���{��J�&M�$7�x�D���T��K]���%	�}�f4�Iz��)���<u�f=�d^���7i�^d�M\>Z������3���������KG�#����5'Y�/;��{Q��@i\����zK�����k��k��F���
���SR���tOC��OK���5@������X�B����{_�i�&	

2oF�Y�o��[g>��;w��+s[���@�PRQ/�vX��O���`��7^���}Mkm�����?SWn��e�����-X������S���e�dPt��������}�v���+;����;�-!S�i�����=�{eee�n�:��g��t�|�`���I��X)��cR��u�b-��$��Oi�@b�hn	/invYB��)O?��f�[�r�F�=�������k����M���q�FY�p�{�(�<���u�7��������������'��������/�_�����%K���:)33����)))|E�������������Wg���������c��?^#�o��9���_�x��Q�X�f�f����������E-��u���t3�Vw�����E�����`�F=�n��18������F3NNN��F
�?���	yy������x������_|�@g���m�&999����z4������7UBS�����V���Zu����j�u�y������IJJ�����<f��]^��*��K��X��G����*�x�	i8�_�Z[�Q�X�9����_@�2����f���(�������1w�c��%E��i���S|�H��������o����������|�C�������3����w��X���&i.^������6�tOW^�{�W���|4�)��h(D���C�q�X���;Yn�2^�����Vii�l��U.\��+�]q�5�+�Ti����I��O��z�?�B��t_���A�P�<�NsEEE����[N�>�+;�e6�w�h�F�T���4Y��*|�c��A�����#��G�g�y:�������(���q����'�c�����[��|*����"_��h2���v3Ro�o��l~������6�M>tUo5�������Q[�j0�n��e����UrB�F:b��j����5n���~��#�R���_u��qy��7�n<����������`I-�����?>�U�Q��O�x��H.����Xw&������t�R�8�������>mI����'�Zq��<�o�b��q����7�w_b�t���U���a�v�����q����gi6N
�/��k9A��z�9�Q0j3��(}=��h2Gq������s���On�'���L��](��{��C3Wo<3_BC���{E�PTT$���2����3$!!A�Z;q��=z�|{oL;�.#Jh���,��f�W��z�?�B��t]���A���x���.UUUf���S�t�c�
�d��X�kl����-d������`�D}p��L�Z����G�5&�Q����z�����D�VF�������V�kF�Zi��8�����
@m7����ms����������7�7�_N�UY6n`�]�<��`�Fo[�i����5�6c��=���fm����!������e�F-���x/�l�F�F�E�x�~��Vd<�>s�����Kqq��[�N�9��pe4oc<����(�p�K��!��Oi�K ��_d�`L;������&��������o<���C^5E
���=O�@?B�Q78o�0���SZom���k����������4s9���y���1>=�8�-o����Cm9��V���@����<�\����t�sS��!������8��o[�1n�I�m7�����QWh;����n�q�i�����J����G����[rss5ka4(u���$2����\�UW�����5��%���/������7������r���E���C	�S�+"��V���f�l��%�c/�}�������.r.f7��"��7���i{�nOn,�,����D]������Y�u����8F��#��m�	E���@���$�2#_3W����p�~�U����qfu���������:T��7����
Fm�;MCm��=9���lO�G	�6�����IN�>����[��������{�td�����!'$�����g69B5��#���/������zKN�<�+��4���!�������H�����W5�f3K�?����&�
�������a���m��!����/6��M�
8�����o���N�un��V��+���@�����RQ���+��=����m`����M
��9o�1�L�d��6�x�[S���>�i��G�


4r����l@2N���o��zg����l�2	�\�+��$�
�}5�kjkk��������}!R�s0A�.���H�_�fG����qL���x�+�Oh>�!��b��
.�zbc����{;�����tS��%���1������D_����������Yr�Ui�*y�G�:j>2���z}R���p���3�����i�*�+�B&,���@�W}�x�jL;2�y�9�&�N��9C�������J�<�Y���+�Bg�!��='��S���g����4Y��o��Z��zrcK��[7�w���$((���X������MG������5��5�=��J�Hn�f��R�4���d��S��y���d4���0�����u���Q���`G�Y��G�2d@�f@`����[F�������;����'�k�G7���8�HP�`������k


�}�vsjguu��z50N���dE�J��A%�����l�A]����y�75@E�Q/�v�H_O2N����Y�h�6%�2�-Zdnt�T$r�4���������/��
�v����4����H����]�=��h�2�.F���	�F��X7�74!���=L=J��z����T'��MGS�N���WD��n���c�"�V�e_
����bs_VV��xf��dnj��v V*�.M;2�	>k6��u<9)b�cq��4@F�Qu�)����@h\q�����kc���	�Sj���P^'�wZ71����vR���������um�1����?��vF��_�g+4r�4����@�***2O����U��������WH���������3����f�)4���W=���Irss����*�C&�9r��t�M��A���k�6Sm�������`��g����)	[��f��h>�!4�x�������'���6n�h�93���JS�i�h>���Y�F#t�������c����hTb�������H��������J+�M�2���27�e�5��kF�Q�?����"5�^����uwl��6t�f��@��9sF���������`�3g�,^�X���W7��+-�����G�A��b!�.Q�����M����y���U�n��v�O p��c|����?����0>������y�KOop��1�����3�4r�p�`I����K�j�yR��I���������b�a����Q�q> �?��4Yg�7�%��l���uNLm�\y�o_~TW�3���V�Tj6�IW\9&p�"���}5�=G�����Kmm��x��� 7�x��?^��E���^iq�c�|!���Y�,1{AB&^�+@�����f��auJK��0w��M/O>������#O����6my��������la�f�n`�=������4��s��b5%)�6�n�&�Z�1��?���i�
?��+�]�/�HEu�f�1�����'N��Q{	e�e���Jtm��xr��B���@�������w��]�4���Y��������3��k9r2��4��+����4�f�,�{A�cf�
p�Q
mn	/i6Z��!���hQ�����q�n����sn�q�~��r�8|������}��i��D��;��]�%��|���8
���{���>^@����4s\9��/�,����O�Jl�o�3X3��������X��������������O����S�����a�]cR��9��w�~::H�rj��]��������{4�U

-��=���
�A�_������K5ko�P�|�n^������TUU%����9s���T��=+���� ��7�������k�&�w�B3���+�����P����������c����v3�#���1).77WW<������g���uEd��b��oi&������r�e;K��]!A��^�����~�gHRR?C�6rrr�������p�1�����:�w�c��"B��{�SZ97�����1�;U�����8�]������!�����G�o_�a�������w��t��
��k���+�d��p�@d����
���maK�9�o�����5���Q����\����������f��f����B1>>�M�8y{W����R���O��\��


������)�&M������w�'����J�Q3!M*�~T3"6�t�?�B��tO�W��i[����YEE��xTXX�+���9S&O��Y�[�'����G���E�8���'!����{h���+����F���Gs�:�g�����Oj��qJK[�
gFq�]���x�_9odqW��E[��h��v����C5=��#�8�����,�kF������=���"�������Ko�����_�U&��X(����q�VN�l�qg�`6��(//����%��N�Z~�n/>��������^;�
���������;���U���a���N��#�o^�1�B�A��W�G���������2��\��t��1N�x���;<
��4��.�����|���8���S�����|<�o�|�No�o_�v�����T`������&%''���F�U��$����RU��+��8?N�+I3�:~��F"������9y�q�s�/�!����#��F�j,����\��Gg]��G��O_�s�����8���t�����i���6����@�a_�|�/�Nhlf���
H+>=Ef$�h�+����.���'�]q�?Wq\"�Y!��K�������v���������&�;_���/Kc��iJ��"�������mm�q���j!�m��x��j�=m�<��	�<�oN�8���OF��1���G����=$�A%�����x[�^����q��4���kL><c�Q���C�����Fa�(�����n����v��@������3'�:?��$g��������7n��u��������m��B��#�
����7n�����������1���6c��'��z�Q����|�/�9�}�����[[��s����#�����2�_x{V�G���(���?��rw�������,��j���5���||s���g��	lwH������-Q���L�^��
_�����-0n��}5���C����6f��:u�f�=���vB���o�������e��x�N�B����H�F+m�)FQ��f��X 1N'1n=�;���Y�Q��������<�\�H GGkz�Q�1j,VSz���m����f���7j3�������|T��:7u!nP��Dr
0_~~�F��k%�����)F
�����G����	��H��}V~9]"�{TlC[���������*aW~������j���;d����Y3&�������Cu��o^����}������`����"oJ�>�����@k1�(����6���+V���-Zd�:���x����Tl�Z=��u�Q�r�7�7L{����S��2����6h�Gmk3iii����H�}n�gF��39*�%������H���VXX���AU�5jQ��!������'j�^p�0	[�	��I�S�/��*����U@w��}5����zY�~�df��d����e���2`�]q��H������xd=��d�K��~,��n��{4�W,X�@����58O����Ms����+Wj������|� 0���>=*6&D��m}
@oi{8J��EG�k!mj}��=&O�k�N���J��<M>X}V�
����C����#�l
k����z�j9s���XKHH����N""�_�������|!���b-8j��|�qL�ZW�������4\����4�XMrnHz���5�������|� ����;��730����Q��~�\�p>d����7G�������Y�]%���\���RVV���A�N��rvJSY�f�l��5����sf�QII��X7n�\}���+���Grg��4�f�,�{A��g�
�y4��8���h�"�
H�5�1m=����j����%Oh�k�45����
����7&�R�ipy9��Q������|���ZV^��~bKg1��������������/y������RPP�W]E��Hx�uc�3��D	
���s��qY�v�������3fHjj�f����}�~�o�Y���%1{�|ntG�����g��<�~�h���e�f}�8Q�W6v87m������y���h�q���{���%==]3������oc4,���+V��5k\��7��{�}��
6HSS�/^�X���?�����|��|JJ���
�O��N��P�Y{�g�o>0A3���Hd�xN
F�r�s�/_��������}��&���xS3i�������Hm�|�g�L�r2>���36>>��n��,++K�899Yl6��f��y�����7jX��
��G�u���C��_����{�����m��iv�����T��F��ig���c!��$��������O��6��W��D��n���r���<���+%1�}������k$))���|�)������I��k$��?����=bL��>�������z36~�83��:O���C����[V�O<�Y�0t|e������l:1�����vi�����h�?6ox������+4@��|g����>����r���8H3�p�/��M#lbq���|�����G����|���������K�: ���7S�����#S%)!R^x���s��r����$�]5\3���
�c�x��)�V�����u,b�c��A���Q��Y�T�n��}5�I�y�����s������M�qqq�rI����#����I���f�I��;$��oj&��k4�Q����z��UFQ��c}�ell�v������n:b<�3�fz�}�����������4�`l����a0gl����o8]P�������7���e�|��dZR��:�e��py���0��xd��qm�����t^MM�����^5
<X���z��GmW]��?}��������x�G�.��M.F���.��q�+[���J���Y��6�K��������h�f��R�i��ZkF��g�������8�;6�+���/| YR�@���!��G��?�R���Yr��5y�D�P)Q��M*�
������"Y�z���Yd�j���f�QTT���g/����+����k��~TF�����c�Br�������8�-[�h�7�1�4���m��A����x���L�!tFff����RRR���D�������7���=�=X^��<	�g-�#��K����z��}<�`����0�&�~���`�F
�n��18������F3NNN��f��?y����}�R������_~~�f�*�W���L<2o��yC��f�_u���VU����e���������B���j���� ��:u���������'��Y�4����5�X�#	���k����|��G5��:��gHRR?C�6rrr�������p�1��y:��b��TD�{�T4@��|�Vu
Mr��2���}��������5����%��i�M,@��)��h(��;r�\>�������#SeF���U|��}����q��2n�M���9]��1�j����5�m�4��P�zu��G���]�4�l���2aB�S6��#/w|HV���'���?}�LI��U ��|X����:O���C�<:)}{�e����yqt��}��JF���}�7i��7Mq���#y�H�w�"��x3(`��g�W�G�A�-����f���j<*i����|N&,���#�*���NJ�~^#W3�
���Q�t��}E�Z2c�F@`������j��S�O�Udk�*22RbccEl	7_W��;5p������h���y�>|XW�EGG�u�]'�F����k+��OH����k�����G2b�<�|�����#��d�J��J�\1���G����V3WW�������G4ro���29��u�UI��CW��<y�F"��y��1+���$[<��`���RV�^-'O��kqqqf�����u����}R��Rw`��X;�4N�[�Y9�/��u������N��i��X�B�����9���=L=:0T����-�j3F� ���[������%)!R3 ������UtM�L�_������2���2�����Z&��?_RRR�"����#C�$82Vl�4t�j�����j�*),�~��*11Q���Z	��jw�"e��1i,��k;�������6G�����)�1z�=4y��0$�-�'�xBV�\�����kF���.��8mx�����a����i������5��Y@ hjj�
��4s�x�����@Q\\,%����&\j<j�PvX����=�}Hn��6IJJ�+-l���6p�f���3�:�}5����y��w���ZW�M�2E���J��U���|�{�y��a�������dL����z�]4u�(z�F:bL������<��KK���$����i�#��Y�����}�R]����%3�h���l�\��������]�����E���'��8��?^#@G�W�?9x��l��U3����+3f�������R��OHm�?t����(�������o���h>���@�YF��B	@�I������3�H��P�@O1j,�493j:4 �~����9�0j�'Ndgj�*����LX,��Ea=D"o�V��GG�<�����#��j�';v��}��if��p��W_-)))��^��w������]�V�.��<$���AWD>�l�LI���}4y�Q�d����2
%�Hi��{
%��V3Wi��4=���xj<�T�1te�@_+����.h�j�������{R������7K���}o����|��k����!1u����/�%���"��JBg��W=s$���=[<�G�
������e�����i��j��A�l�2IHH����>//}E�k�u����q����HF��)J�D���Gi\4Y�*pl��Q����[zz�����>��������+3�4r��)�'��w�c�����Q�q���M,����%�
mAAA�d���f�������ni*9+��;���oJ����������;��2t�l�:/(b�8��{��qb���
�j������;���3gt���#����������R)�����#o��_$OU?&����J�O�2V#������s��uc���u����,�8c���;~�Rv+���
��4=�]M����V�1j6�5�$6�_��X�|�����oe�#?�'ZyJ��T�U��f����l;T�+���9D"Bm�����a�|�I���N��y���e��_vTB&.��k"n��F��/}X#�'�����;g6[�Fk���"K�,���+��O�������ku�����'���G�K>�l�LI���|h>���������I-k����*����G���|@�3j.V�W�rW���i�F}cOf�<��A�q�X�*�%�t��yU�|��{������^��O�h����@�����k�.c�Q���H�3�H��?)���+-��K���'���V
"%c�eM���u���l�$i�(���/KM}������%(��Dyo���K��O�c��"�v�'���;�-!���G:�}5IVV��]�Vjkku���3d�����W��e)������)]�v�9^�Y�Ey�a���
��
�����u��h>r�yC����V��Z�Z�r�F�G��M��v��#c�Q�����4��'�5O�k9[�l��o������>[)�o>''�W��{1�YH�|\}�N)��CR���l2j8�K*����������*^��4�H��y�k�]r!b�����4"A�'�$YC��S'O��u'���������[�s����Xb��!1{AB��U�<a_
�@�o�>��}�f�]y��2e�������R��w�'�4��oTY�5&�J������+�z����$��`A�S:b��@`x{�y��o��UZ*S��m���t����.�������Y0���������]5���Q{��g�&���/�����?�F��H��wl�92B3WqeG%d�b���}5���[�����Y�k��V�7	����%e/<(��^��^��Y~Z�1�i���������\-��m�
�
������p�O|��H�������e��H�@O��So��(\N�k�u,%����Q�5R��UN
�.�G�#�>!�>.{F�n6���$g	�m��/�����k��k��6	���/��@�����w�}Wrrrt�����������8�C*��[)e/< 
���keMQ���a�w����a�B�K�����*F��*��h>�K�.��l���:[����L=�'-\�P#������:/�,O~d�|d�h]����?on��?�i6��8qB����u����������8r�T����X��'Y������d��[�4�sM-��$�a��=>U3��b_
_b<�}��w$//OW��=�l<����W���R*��5i���k���k�,���������?}}����h�����f���k�������.O>J)?��tY2����	��fi��������YYY�����|Y�j���UP!����;���I��%��s��)�����\W�M�8�<�*8������")��cR��w���[�W�wk>+M��|�����������[�uC>�Wh>��+��u{5s��:L#��� ����9��Lfl:��g�KRB���oG����C�i��`45�kh�e��r�+��IF���-n��w����I��=k����7Jcc��X�={���5K3W�Y���W���u�Zss����~y��.3�<6Z���d����e��0s
�E4��Qh�<M=2�0/^#��������b��F�^3BF	��P��������dn:s��5^+���t���Z�p����{�O��^qL�'\�pg��M�DB�m��]�����5c���E�d��	���������������
�M#���_����%6:D�;�<xb��A��w�[��������!2d@�f�m;T����I�d��hy����������?_~��+�����#��S�QUU�F�����%/�����4h�����Z������gF���$��q�����%++KW�EGG���_/�F��'�M���_���?��66���UY�G�����?}}��|%^��|8Y��@
J�7�0�td����n������l���������N#\N���u��1cd���r��KdR�Z]u�h����
����+��G�gg���U�VIAA��X�������N�+�mY�M����dd�]���u��/j�+����N<t�	
a�&��`'oo�>�5ed�\�2@3�����2c��Teot�\TSS�.�������T��)y�Jj��rE|��?^���%<<\���%22Rf��-W]u���GM�q��,�&�o����1�(--M3�9rD��[�q�j���D���k�����(�?�����s2����Z��<P��yD��.���d���el|�^��G@?b�2x�y�B�/��UZj�F���n>b������5r����/''�����3���S��Q�{$������@���#K�.�;��C>���m��&&L��h��sa��:��������V"��e���2��=q�|�����x�r�J]�l���{�n�<�:u�\y���]R\^/?{%[v<���t�7b�Z
{'���	Yp����/��u ��G@�����$-u�f������'j���:���#oN.G�2����F��G5�
M����yfL6���,�*�e��?���?������%�$(b�8��n�?[�b��x����
y��w%33SW���d����r�?����?��L��M�����z�Z��r4�i�����;%�*��h>TUm��g�k��h<
q�x�i�Q��H94\3t��G����X����QzP���9y'���i�^��_����F�'�xBWZl��Q�/_����;wNV�^���k��_;c������'�Gv���|��Bf��k
b��C�����|��D�
�����s��)!��voo�/�;����8�����yR�������q��i���n�'�nmu���@Wm=h�|4o
S�:���G4]^����r4�H�S��#q�F�q$�J�=?��{�9�>z�D��U	�~�>���Wsy9O;2�&���e�����oG���k�Juu��X=z�,[�Lbc/�N�<]!���a���GdF�k���_���R�j��m��^�K��s����
���i���	%9�"1��T~�d�������4u��|dl0�B���clj��|d4I�f-��k>2�L]&1���>�]b�����G�g�����t����Sv����gS�L1�����W5����-��d��:�/���|(������~�L���d���?���G�E;�K�9��AiL=�_3�����r�����:*kv������z�08L&�������7�^����j��r��y���W^yE�~�m9q������c�}XQvH���S%(,F3����#��TUU��5k���c����W^)3f��L��
g�����o<'��'�{a?�E��z���k>/S>�B$���l<�.�4�(.6T����o�iF�y��������F���P���1�y�Xq���deS�:��|�����G=���S���s����]����e��y���Jl�)������=�S��
4������U��&�������e�$11����#�IG��o��Z��6�^�s2&��y����xsb��k� �]��T�=���M�6�E���`�
�q�����{��%d|x+33����)))|
 �/��G�g=~��7�������j{�����5�����}Y�����X�B������k_c��������v3gYYY���h����b�������pPv-���Q����O��L$��V>������><Ef������!�����{���[_�|�hq��Y�z������v.]F���E����+5�����O��6��W�����d���f�|�rIOO7c_F��a����y6r�H�7o����JyU�������K
���*7:�j�Y���y��$(������~�k$))���
�>km'XM��������1w�c��"���$@�����������\���S��z���28&D3]�w�_�4�&�s���[4�'�V��������O?;]&��6�o<'��O�;��}'U3xk���r��I�9��s��n�M"##5�<�W�L�2N�/++��9kdi�/�����\,�Om	��.����O��6�'4�?��{�9rD3�&M�$W\q������{���T�l�T$�	��L	���������������\X���F��u������=�o���z�u���x@?�������v-�Hd�{E�Z0��O�D��$<m�����]eL���x����Pz�}��E���4�W�����u��n<2��G�����?����~��l�{�L���k<r�K�����G�/����;;��BYKq�����4���#���������keV���p�`���D����u;w���g�j���S.[�(;��%��9=���������WCTT�\���t��=#v�l9pA����xK{A��*t��}�t��?K�����?F����*�C�i����}/Y���/��233��>��������_�}�&���_|~�f�����%�~��e=��>��m��I�x���b����eeeIcc���l6�p�����v)�l��c����+������+�
�t�����:��7���R��.����%))I�����+>,{�����b�NIj���]��?�4�@Bm�g�S-��
�IZZ��\����/_.���f����y$8������x�����g���D9Y(��m�u������S�/��~]��v�uR9�S�]~MMM	_��>k�k���-�kw�k������<�����O[�[Gr�-�i�q����d�Q����~���B�\-��:���G����3��;���#����]G��c 7n�n���^7
6L��������.�k<�i;,��������������~L����������[[��s���om����[��kh>@��2#_#Wa6Y�:L3��8R��g�+��Ye��Z8m�F�,o��jjj4Bg<��8�VhC��V�j���X#	n������S�X�	���������gc����q������u��������_����_�5F�K�����	�u�1�e4�����~������,Y�E������������}��U�4������F��}�\� ��-Q3=���c1�}������������M�6ICC�����n7cp���enT2$''��f3c�[���^�:S����,�A{��7�?-q@�C���T���	�/���f����d����u^�^aL=Z�v�f����,�U����%rb�\]�dJ�jU�O�a>,i_�,��z�?�B����������+�x������n���m�g���y����"��}���Y��*""B&O�#��V%��*��QA�����d�}��x��t�D��M	
����u�����F�����_���'OJ}}�������0�[Q�q�]=��G���3�-�7����G��j.5,��
-�+�,��:���G&u^�&
w��O7�����o���s�� ��e4�^���y36L�^�������x��%����K��4�1�~��n<
��a���9�Et�q���-w��q�����]���?��rw���~C���@������������@��h�F��nLZ8m�F����Y;���rvJ�����g����Ij3��W\�?^6l� ����d��5����WZ'��9sF3W�N�����H(;$�O�U��_Y���G�5�����[�j�Y\�Xy7+^~��SRY��P�[���a?���"]�5X���_���{4���8\,'�Ui���G`��b�������H��N�:��G-r������oHse��^+�o<c�;3����]+�O�6?F#��m�$33S�����g���;4�'�l��Ih�o�9*Y~�z�d8�v�y,��r�u�3G�U����c�b]��G�w��n=�(aH�,�����g
����%.�8��HZ05V#t����jkk5
LU�~�Q{Uo<��%{����f���v����
�������F�����r�:F�z��d�������+�l�9X2F���AW.�d;.���,������|B�?�s	��pJ�#4�_9[T#������Ja��n��bilr�T�����#��p�t�����nT�7�u�Rt�f�5�J���4��o����j�^SS�y���G�.���E��������G�z��'d��URRR�+�m��=C%#K����V��SI�>��Up�����$��Gu@Gh>@���a��ay�0�@e4Y��g"�:i�f���4���hX�svj�^�����I�4c+'O���G�j�*�����j�"d�Uz�����=a��f�)7��#t�{��'[�l1��;�[#�(�v]i��zy4���`�����#i�D?�qL^�+�A�������������@���h����OG2,������G�@m>j��a��G%�������Gd���$w�Ls���>i8�K���k�in��7;���}���4�C&_+1���zj���k��s�
���h:2����qz��{��p���y&�G����+��-|P�?�k�
�+�E���U;�������4���?S!�/�j����	j�MA��;�
�]���G���g����N��J��#����QRg����r(�:���I�	�H�:9����u�QP�����U�[uJ�A�6q�,��E����5%%%�j�*9q���X�k��;�����]�d�}�|7��2*���X
���{~ ���E��
OS�&�����4���#%�JN�����d�D��m�F��I��z*7:�?L>���&��~J���Z���%����^i�(���G���%��^�c�����>^WZX5y�z4�x�F��GAv���������G.\�k��a���x9U�+-��I��|"����}�l�y�/2u���
���/>Y.�e�i���G�����5r5�l:2�^��F�Sc5Bwtf��?65�H���J������L�����VH��u����rl�B��`����F�����a���]�H�J#��8 �6m���]�v�(J�<'e�v]i�l��g�$K�[u����K��+�!ct@W�|�~!=#_#WQ�6�a^�f���q�X�j5s��t���d���pZ�"��s���D�?6U�?w��]��*_��y_p�=�2���"8��_�.��Q{�q4�"==��vh���[�n����k���3�d�	��T������~(c�O��� G�D���D�}IWt�Gx�
������J�/���;!���6�����(�LO`��cBd��h3v6 �!��Z&$�{���4jal���o�G�5�R��E�����v��r���+]3a����������4��?^�cGjtMYY��\�Rrrrt�Z}c���5T�����K>��|2���y�=Sb�����QW������G��HZ�0�@u$�B�*���M��5j����4j��w�_G�9O>��|T[Y�������{Gwo������:#��@��z\�\��+��.��z���#t����e��URTT�+�
�B��#��[�+-�����?'�96��g������~/�a�����|���i�����2&�}�?��nY��d��(��Sd�N92�"}����df��f���GAA���k*+4�
Y[�����)G���,����y?�p����o��7����5k/d�G��S�N��
\����u!�l<*�q�J�������2.���xl��;�-�7=�z�Gh[^���j�\1��-���;�5su��x2 D�K��&O?4YV?w�<��T���z=�y��'5^lj�%���I�^���`)	���A3e��J�-T��]v@&��+��������U[h��M
2>�;���Nl�
�]=D��s4:���@6m�nR���eC�qr����gB�*6i�k��S%���H���
��@����<�\�.WM��������[��������V�M����c�G5�=,Meyb�>��N��#��U�(��~X�]'�!-���	i��)et�Y��[�	)�����Pn6%m7�V	�Gd���������d>�hPU�_�^�
_�����2��qc�����d]�P�{n���|^��I����B��%1�����O����#�������\��c����c%�������)��l����2���^^��|�d�I�.��G�61���j����o�� ���^v��M
��J�&l���?���MHN�Y���\}�yI��S��7����?�7Y~�q������9q��%d���x�x>nL<�����.T��G�%�B���Xh�!����	�l]�,��'/�����F�VzF�F����i������e���+��C'��{9*��w�����44Z7��v�p�p9YM=


��Uu�	�|���,���Q�8CW�3�"K�D�}�D	��LW[77id�1�J���i��f����� aW~H
t�������D3�r�#�����]i�@�����?I��?��-��p����9w�
����#���<�\-�;LF94��wo�oJ���s��wNi�j���2-)F3\NV��<�:���ojhh����N�T����_�QIs�8t��}���%l��2��H�����7JPH���UF�����5s/�0J�e���K
*���&79���g��n��O�E�#��
����#���RZa�I��yL=�?8t�\��.��Y��7XO=���G}�j��'���d}�������oJfv��3�GQ��bO�c���yn���&?��X������*��v��)'O����S�����`�Z,���g�W���L]�,���J��O�
�.?��  y�z4yL�LM��b�O5������r��X�p�u�����@#�R^^.[�n���J]�^D]��/� IE�E�m�Hl�|d_�i�����H��~"Q����@�;p��;vL3�
*Cdm��Z|(���X�$<�FW���1�ABS��}��#�D�9V'��1��~���A�3�5�����z���Vj*��A����H��Y#����y��������<AAAYWl6]q�uY��[Y����y�y�l<
�����r�D��Bl��%�f��D	��Q�y�e	��D����L���f����e]�Pilj��84�H��K�����w$d�M2����}�]�Wh>@�y{�y�\�D:$-��#�c�Qs��t#+q����XW&�6���(W��w��iY�r��?��nd�o��Yrs/}����<O��Z�V��Er�l6_�Q���JD}�^m�H���%!S����O�T���+��z�=�N��;vh�^}S���*uv3O����?���#f����,Qw}W��+��G(e�h��:L��4�h>����z���2���.������]UUU�D#wv���QK�RG�f��$~����U��4W#����M�4��.k�V�4��xC���Ti���������V������4 �x�zd��b���/Hn^�fN<�Ln�[i>�sV���<����"���a�z��xTSS�Y{����O?2N���X)r�$IE��1n����}�T���m�����i����2��qc�f7�"���%6�D���3d�^�,d�r���_�1v���4 ���0���)�2jX�f ���0�h��P�{I�f�}�����Wh}�j���M�u���2�h���r��Y��;y��9)?��V5��*��m��H����X�y��Y���4�Fmm�9���	������xQ����<�B���+�E\|���g$($BW�~s�����9�oq��Ei��4��La�l>pA3W7����oM��~h��%{�&�sGS�|D�����G��|t��i9p��{�X��GYYY��
i���-����}�,36�#�zZ��%�"��3%��'%d�2�:�W6o�,%%%����|�������"w:���A�z�Z�����%l�����|�����D�QC�e�����@��F`h=����C����!o=�@����r�B�|�U�QPP�i���*�5�X��+=��d����Y���Ivv�F��Udj$�7_��3n��%���Mb�����S�}�h<:��f�eFI���<����X���9&_+>�q$�����#c�����'/���#����Y7-O��Q������4r�h���U�-B����#�������SRee�F����kd4]��{v��)'O����S���xv�|=�W2"�s�R��������
���|������7����U;������C��#�6��G
����GH������������g5����I#��4����,�.�y��O>|�����c��WQ�$��}QnY�+������\�BW�������E��n�|t��8���k��[�O��<&Z�&�h_V_o�@�X�����|t��s��;V�������a���GL����{233�&|O�������'c�ru�3��%2����#�*]�/h>��3��W5h�*-5N#���-�#����J�G��_x�|Zo�w\g3&�l����{5ro��]b��d�+?���cS��{N�:%;v���������?$��LW<_����O$(j���'4���{�zd�f<yl�f ��k�Y�\EG�i>��������Y{��F	m���U�#R��K�����t��}����T3W�!2��=�:�X#C+�4q$_��


d��M�Y���%��z�l+��i��%��O�
D���{�er�����z@�������Y4�%����j%4�Q3Wu�p����!��;.\� ���IY/I�����l]inn�����L�D���}�,���WVV&7n����0���2�2G3kF�Q��^��i��_�|���a=�h@�C���2OS��Cmr��������k��a4�������}���7���W#�W���t4���yo
������k$��c}�����xTSc=i�0!�$�zn��'L��O�N��~FW�;����J*�e��|�\��x@�������$Hd�M3�:O�����b�wYg�����7Cv$33S��?�����<�{���$�Gc�f��4����,3�y��]�!3|�������D3��2$����_�1���_�>�
]h>���4�����d�ZF#WL=�?M>
	��Umk�Q7&UWWw8�hB�F�.1����5s5�"��76aF��3|��x����0������4se�O���>/��VW�����3��]55VF
��
���Hn�f��Z2��G~���Gaa�5���G�P'
��������$7n��5�d��<��%	��$((H3Wq��%x@�D����}o���r��I��V�-���k�*�����G^{�\]hh>�_��^��)���U��x�@ �����
	�����S����VB�=4������3�O?���07\���+RXX���B*%%��i�j����s���X#C+���������o8p��;vL3����3���Y{�����_H��/�
�@E��O577k��4�h��p�?y�f Pu4���%#$*������G��ZqD����~�US�Ml�f�p���#������r��)((0'y2�`�8�j[7&���������;�-���u�[�����~����/��g_[s��\:��2���#e��d4������m�.h����/�k��\�S��$h�����|#aaa���4,^�����]1JV�Z%������������.�7o6�ycXE��(=�Y{a>,1�A��%��s+eh�	s}P����V��,��+n5���v��)��c�f���������2]iQ� ����D���H0����g��;oo���Upp�,�7L3�6�+��'�5s��#�U__��+c�PP���G{n����R'),,�={�����*[�l�}���MGF|����6��/��Q{F�QD��>z��|�y�p�m�������\��_�����_��>�[���M�6ifm���%���f-���$q_xU�
����#���&�����JK&�l, �����&���BlL=�c�����F�Q���������k����v���0�`�D�jvIk�Q[��������~g��<�gq�_�W��UVV&7n���f]qo���ehe�f"�M�d�����O|����]���|�����RQ������8���:�S&?|)S�~*C������S�QYU��=^�����$0���yn>���&����J�~�MI*���K�5����V6�_'555�������PzP3�5
WJ�����?t����h>�_I�0�hZR�L��G�yU��_�'�v�KiE�l�W(?%Kv-�G�d��\t�L=�ouuu������n>�X$���%K��Z���%��4��lL����Wh�^bQ��-�a�%�1������1�����f���h>���� ��O��J�Hn�f��z�����S�������<r����YC�z��<M>r4���R���f���S���=2;��PvH�8	������	���*
e�K����]qoD��P���74��/W]N�.���?�\���|��i���h�,�;L3��6�W�Q{��u��H����3g� �����j5r�h����h	

���3��F�+��������w��{Q7Ul�#5|W���d�_"g���{�*�e��t)o��_�> ��x�w���G��(��G���d�N��#���������f�\��[hNF.��>�{����_��YO>
	ii:jm>jn������&�4�5�-S�}O�2W^����U�Bg�)!3o��M���R���d��7�d�]uo@�9�y�5��0[�R�
��0�\���)�22����#�������|���<]��{�����%����#"9����D�
��e��p�>,,��

2���.�+i�kd��2`@�i�1!"�����C?�1�,��2s�#��Iy�W5|S��UR��H��S�9�*]u/��T&�Y%/T�+?���7G���sG�,�:���@������4r�h�`I����/c��'��M�~_�f��N��U}���#GS���l�lm>�$��R�n���2>�D�{E���,��#I�zUlMu-�R��O��5|Ksm�T��;R����9$����+��/>��<"O�V�6��U�{�!�-����>o��"9WT����T�.M>j�{Wyj5r5w�@�����������2�����	��i��!���4�����&��9��>"��������s�Q���Hq�H��p�^�v�d����)l�t���YC��7��.��>�������:�S�������I���E���2nD�f�W�����GA�1f,����V�%#J����k[�]u^pd�D��	_�������V*_��T��ei*=/�!��'�i������d��qa�f-�%������G�|Z��J�q�X3WL= 0?S)
���A���S'�jV__��+c�Qpx��#��#4r5>CK����%G�|���i��;:�f]|G���R��{�v��Zr[��q���#����3���0^���a4���#��
���|EzF�F��� I�7L3��.M=j��5���d�����y�|do3������Q{�6HT]�f�k aW= ������$t��z�-u�����>&�E�tEd��[�<t�f��?#�Gk�"88�l<:0TW��G�Y��M��������qf��3c�Q{���8�d��A���y�|d6]�|'�^{�2D���$&$Hf�{K��2.^������R7q��.������^�Y�
s�����}oJ�+Oj�b_��R1Z3�2�d���MF��������G�Y+3����Q3Wi�L= Px3�������$�?O����qi���h@Z�l��y���6o�/=�W<s��"��R=�Z*�=$�	3�
��j��!��|S���-�s15s�Ti�l:9X�K>u�X�z��iI`��>���I^^sF3W3������@�pm>����$���&��|��6b�H��&� G�D�������1�J]|_��x���4k�5�J�����{�!�.{�f���h�|����g4�H��������r�B�f�n���w��TJC��v�w���;q�F�w'5�J�����`�8��k��>|��|�E	��LW�P��u�pj<:�\2�\��{��vY�3L��7�_5m�<r{�f�1���svg��6�����!r�l����r;���,����J)��i�q�Q��|��{a>�Q{��o��O�(��]�C�������495p�lJ|PN��+��7���!RQk���GE����x��#�c��'w/a�?���|d%1!R#��N����#c�Q���G������I��1!��qy�W%������(�?��G���4���9�>��r0~�T�v|P���!RT�Y�A�����C��"�S�����
G��b��%	��@`=�H�����7w�@��jk5reN>��<����P>�o��&��?$4���c�����@�Y ��="G�]-�����6�"���5���� #���@Gh>��06�yU�f�}��D�@���|4X&������������;�����\9�������$�?�{�ZY�������W:��L�/r� ���Rd�8^?����3^x��F��w�H�8:J3�	�
����f�Q������?���u���2�F4GH�F@`*--��o�C�:^%9�s�1�h���{�c��y��>z�Y6g�f�y4�@���IDAT�'�{�Y�{�T3W��#��7��
OS�RF���=el�|��$�;q��OK��G�H�%3��9�������I3W!��������"��e������S�����o�����3-����i~���H��kh>@�;����G�4V#H��GV���4�������,�~t����ir���z����c�
o(��0��X��
6���+��	��/u���n6���H9�����N$��@�f�u4�����������[�K�$�S����8�hD������4@@)))���Ok�jx�!	���N�:%�����Y����`+M���57V^�?B�;#
��/���G�W?<^3�����V���
�
5s5t`�<t3S�T���L>B`�4��0�x�E�������s����%//OW�9@����w�#���*2�&_��D��u���#������7Nj��C7���P�f �?Si9
92�.���k�@V]]-��������$��\��|?e4�����u�V)**�U�
��I��)��
esf�
E_��$%�N�����}��o���u�������t�P�@��<���r�EGS�F�5��i>�ill��������s������
���+�.������w�_?���]�2o� ��g�|�>��P���-O3W��`s�\��Xo�72J#������}��26[��h
),���a555��~y��We��=���������?���{2�v)��2Z:����.�V��U���Z��
n�[m��j��@�h)�({%d�� {�?��Hr���Y�����q���='��$�o��|���L�8/���LO{E�x�w?�,�N��>�`Y4s�v�y�[�\����G�H�p_�@O��Y����AL>��jR�����M��KWlE�����%��Gp]%%%��g�YCGG����j=����Cru��2>k���]%�9<W�k���c_\8T�tw�5��{aM���-�����ar��<���db�����r��v���|H����$f��|����l�%=h�v�����oi�v"���q��y���O�����'N���<�k%&��Mz^�f����\Y�1_~�y�^���A���o\!�]?XW��>�eu4�H������Gn��\���
�/����|�������}��Ls�uK�~D�%HYe�K+��W�JMm�^�\���D���=Q��w$��^J}"t�����j����#������m�|�����������-���;��S���6�u�u�������uk��%{|���!��k>�e�rM�V�Z0D��dc\������c��P��V�������D�q$_�����2�rN��@��SUl��$GN��V@U�D�����
��9#�6m�
6Hzz��:�a�kO��>����3"���A^��E;�����S_-_�%FW�k>�ecL<2&�32:H��!Z;��V�N���:�.��;����_T-�k�|��a�`L���o��$xDK�o]�5����#��h��������8���O$++KW�\�+c������%&�x�������Q^s"xt������&��q���@g#|��"�l������%��i\����Hbf�v�S����Nd�1�IH
,����z�������$���+����d����5��z�V�����,k���m��INN��:/�����Z#W'�(��t���U����kg���S�{o�,�o��{�*\�����g��b=���V�X!,��Z������5��k�[�\����;�F���������	�m{��~F�=�5�u@��/�@��a������?�M�s�L�T���+ejgn��sZ9
������z�1E.�#DWl�Y��Wp����x��������Y��0&�Z�Jv��%��m����E&e�+��^�����j�]���E���::�:�h��~���#8������}�[��X�-[&�����K����g[�ik��]_��d��l�7
��/Knf��=�����D[�"c���h�xmc�"B�������r8�HJ+j�xZ�<��IY���^��W6dH~Q���~x��c���S.��um
����~���edd��M���w��>!>-�avmN���T��GY{3���2�K%�{k%�����U�G����oXcO���XW�7x�`��!?���+I�Us�V���[�x�d�0yf�����t�Gh�����-�7���-���q�q��Aw}mz���*yaM�
&�,Y4T�<�����5�QtD��U�a\g\�Y����Uso~rZ+��=_�d�Q��������������������<��VpGYYY�u�V����r���o�.{�����������Js����
�>G���y111�}��>Y�����Se��O<54X���x���A����2�t�L����S����/����P�ZS��P���
@o�rU����hgk���2��H��RG���R��c_��>�����L�z& @�r$�H��2����bc��s^���U���&6�='�����k����?SF������������n��{�l�������Us			��h����[C����h���STd�=����xY�h���1C�I�����]5_���<2GF�� �+ |��L�}"�=-�KFXg����5�5k�u�e���\����M������hg+8��:����PgL j^2�G��FSK�.��7��}!��(�����=yE�Z9v<�X�~zV���V���y���se�|��hya�DY������'��s��,���C����P����s�O�v��zW��9///3f��v�m2e�			��]�I���+�{�z��i'x�'�,#K
�p
��`����3n0i�f��ZyZ������-�k��S�yd�0	������j9���Z>$�,x��Xo@�!+�3�Vje.����G�S�ZWQe?�4}L�V"�yH�B�){���'��������I����L�����#G�hg�bq���<
���p
�F���9???���+���o�	&H@@�$����J�3�����f���5�`j?y�{d�h��'����L�u;zc����)������'�xB��npq���7Y�:M��+��5cl�,��_;42�ZN+j��I�<jd��!+����A�#��q8���w�d����u���0Hr���H���"��WI}i�T�(e�~)�'6�w��A��.$�_������*W_}��������DDD��������c������g*v�"e�V;��������$��K��7B�{o���x�*��G���|��q�I�'�:k���Z5X�t�V�����
.��������Y��2t������`<���r_���ir��zM-_�\+�Z>�����������1����u�����gkg��������I��S��?j�\��_j�1���r������IWi�#�t�M7��>�9�}U#G����iL<*��w���V�M�����"������WW�5>��qI�iG��-k�����Z������0������#G��<L��hC�iG��D}}}������c�V
f����c�<��^r��/����Q���9#x��7Nign���2j��OM��r��*������+�����k�>%�����]����A$::Z;�}T�pn���Z���zK�<����/�WW�u>�����b��s���Sz[r�������tI�,����!r�������>�L{�zZN���c����L�[h���n��V�G^^r�������Jw��@��_;t����Z����?�����'����Iu������)����IS��G����lm�����1���q1!��w��;�F�
�>�G��1t�7�t$x��Sx����
N�.������1����Qg=�#���L�����*�����o%jg��>�����FF��c��Fj���&y���W�������s^]a�������+�|jJ���%&�<5a�	�p
���kg���7����~���WH�`��WA�VK�.�������C�	��|-w}mz���S�2�����a�P`��'Q�[�N�������=�X'����4%�.u*xt�5��M7�qJ?�E]D��	�
�CM�>��+���D*�����N�^)�Ac�372g�x�V��s�d��$���x��J���5x4f���\M���8<��Y��Z��Ht?��W���p/���)����|�P�����K7�����0��~����Z�k��yk�i��3�G_Y�|��
W�/O�1��H?���$t\]q���9a�E��~ u�g�k��5����8���+I�A�G����}�1�]�[��n��r����v*��[���A;�������,�9@����<"LW��>@�e�W�:���E�oH]���N
J����!��`��|�������r�����������:e�V�����V"����y�fy���e��
r��1�����K���|V�o�qn�V
|�X !��[<��
�>�G���7�]�y���)��3N�=�M�{����Y�fi�����pu+W�HU��'$�t��2u47��y��ipwy��BERo�M�^w6�R*�j��{��F7\�W+s3�Fj�����+��m���g�����P�M�>I�K���^��>*..����-[����RYY)999r��y��wd��kdO�����F�n���<k�;U�H����X<��k�;����s���E�N�|k=g|�<��	r��>���#t���rd����I�D��0��n�SX����b��VcH)�\�����`o��#���5$X�����eb|�v�*�����Pzz���J�o�
#'����C>'F<&[c�$��*�}g����R��O|�e��U���'�|�V��6H&}����6�����w�]zp/g��C���I;�^7�G������]��=V~��(�1��;2~�d���zO�C�c<�n�����l����)?+V��e��i���4�����/��K�j����
6o�|��{��������]����A��P^Y'?{=O���O=z���6�_;�_GmEDDX\~f7�v�~��}<p=,�u��Y�������k��#M�\9�>�Z�����Gc�����ZGEE��
��7����
��G
������:�}u�� ����jsS���K��:u�Z~��|�.Yrc�L���]�����	F�!*�C��J;���
�h��b��Z���i/�}�k5��1������������������������������ce��=t\rr������X�����})))R]]m�cbb�����@S			Z��c��~��p9��BWq���
��8G���������]%�G��|e�p_������}GSf�{��M
\����r}f__9888�������|q�

����|I�������7�?���������r�� =�G<,"�z����eb�����wt[�h�g�D��d�q�����y��.��{�����U�������|m�#=�#����J�z�\;[>^�mZ�v�;�^�Y�bw��H�>\$;�A�(�S����/��9T��h_��-a2g,�.�s��iuyEWe������_��W������r������'����3?e�y�O^FS�D���gk'�u�V��cG��>�Z�����e������k����7_��6>V�qh����.~�����_G������]�����-Fn�z�v r��)�D��U����3A��������j\��d��u�z����v�Zk�H{��Q��5���Gc�������3g�������RRR����Z�������������N��#�
�����v�����W����O���t���}g��F��Wu���f_.1%�d���%A���~�Z�M��F������B���V�}���������M���kU��L{��3���sct������#�����M���g�wUWW[kc�����Zh�����<��}��]N�,v���U�Z^k�@����vw3nr�����h������t�������#����Q������{������.������z#������rppp4M���
G~Q�����WX%%�uv�G�a�L
�8���Y"�2�OI�v�������Y ^!}M?&w9�2;����a�^��S�=�jc��;d�}����K�@��������l��Q�-_�]_���XZ���1S;sK
�
�e��yZ��������:��SP��������%������/^��T������S��	�8'�J����XW���I����������e���$>g�U��Om����3��q��{?@�S|��jnW����K�����;����������Y�l�Vmc�������+W�je���Cd��@��]�.]�U�+Vh�6�>�`_]�*�?�����Z9u�~�hH���Z�z4.{�5Ltm�srS�����eRLr��D���OM�5TQ�.#r���)/Y�J��k�%.o��J���K�2�����n�k�+��e�mwI�����d�
�w�����%4����	�#\6-��k��f��u�9�d]w}m������#)E���$���\I����<���Z5`�3�����S����E���O���O+����:IHH���"
�8��H�����_?5�F��3K���)�N��*��������+���_(a�~O�f=�+@�d��>��D<����g�
zz�G�2-�����3-�i�����pg��e�j�S�]<L+�����������Xi����Wh2��^��H?[����~Z��$&&JU��	WC
hu���x�J����u���%��?H�]��G� ]z����K���e����2�5��e���Z�s���]=�*E+s����	�C��+h�����g�
 ����f������L>r ;�B+[C��kWs��1�l����>%��t�c����S;��*��=)�q�t����1���%��o�����
z�G�R-opi�4��e��6���k�2���
�;�������y�l
��c���j9�������o<2�o��Y��Qn��4��m�����Tkg+�?��\Mmm�l��Q���O�r~�V
�c.M=j�;�V	~�o�����6�#�+/���/���7"|�.�u�V�a�f��b=�-[f�i�>���k�N
J���U���[r�P��4�y	��x�J�I���F����a�M��|8����N>����Y��_�#!^������d�������'�|"���z��233e�������+��
$���T$�a���Fo_��������'����7��1�1�u����������S���F;[7\�O�\�G;�"cRs�	H��1�z��B��GvX������k��:��l��IRSS�����9c�>����W8��v�g���e�������!�j��_�����y��pY���#�cv���i����w}m���Cy�~�9�lx��EC��+3&���[�2��b�{ L<��#�-�#;���>��}��i���^QQ��c�iG����b�W]�M�����h�1v6?M2nB��l��������s���I�[���_G���{�����z����$+��
0��{��4��v�XBB�V"#F��
�f�w�~��}<��3&E��4L�4���e��%c��1)�'������x��}���\�����#w���mU\\,�V�����gs��i��KOO�����q��ep�!�D|',��;~�Z��_+8�����N{!��]�}�������#���|�M������"����:&&F����5�����l?��G0�ru�������pa9�Z9��
����B{�!G��Kvv�v����e�����nD��f�#��	�!|N�;[��3���aZW�_T%�u��"���]X������Z�=��������Ka����������<�D�e���Kl���"��_�|Z;��!|��#G��<��pa9�UZ�����	~>��?�W;�U^^�hg_nn��8qB�Kv��!�����gL;�����T���Aw>-�a�h�Gh��
��Q����1!r����W�� |���|�(���Vh���t�Zwp��r������H��W���HV����f����3�6Y;��#|���N��Kk����C��*��R+[���Z�.�_�Vh���4�ZWk����Rqh�l?�(I��'&E5�vd���+�;��:��.z~u�V���7�:����"���"���jP�?�D���
mu��)++��u���r:l�|4��r6x����(���g>��9���a
t�G�zo��,��u���%7������E�i��r�?�a�Q���;�.���b���,��a-�U�Yk���k�d	����?�K��9@�T�:�h���ZW�Wh����#G�u�Z\R��W����T��U��?����k���v����9p��t�G���R���N;[7O ��Dh\]N�����~�Z]`�����L>j��m/I����?�y�����3k%�,C;�����@/����t W;[���L=�����
���z}kbP��l%���edd��]�d����z�j������}�9E����4U�^8b�>����!�
�Xk��h	���L;��#�^���V^X����G�H���v���VKeU�v��z{������z�hH�&��Lqq�l��]�n�*������/EEE�U I������^ik@�I�����P]h�����+kO��GJ���%���K�����_�+��#�^l��T�-��$���#���}�� ����}�|�o#C��o��G�����cR���e�?~*�V����4=�\}��qR�u2��!|d�P���";�� 3g��;����}����7�g�
�1X��<�R�O���;������a�z�KN���Q�O�����#��>jT��GN��+�X'	��t���b���������Otd�\7i����Z���@=z��|��2o�<6l�u�;>����G�<r�P���H���9�|�>������c����>�����PJ|"u�}��#����IF!_Z)�FM������^���[e����H���@/���$5�L;[�C��9Q�w�[P�UsQ!2�+Q�|�M��K��������D���w;���r��v����##��/	b��<�������i�����k!|��O+�W?�����E���&��v�QTH���?+5�RSS#�u�27&W�#K���"�}$$�K��+�T��;W�]�L�:M,>���>�e^X����n������&!�6P4�_�V�]1�X�����k�{%''����j����D�2k��9��9@"j������\PP�L�8Q����=��!|���w�i9�l~��!~p�<8�v��K-���R�.�T�Us�~U��U��%�����Qqq�l��Mv���+�W����weB�{2���9�_&��|]���.���?/��r��=Z���>z)�������)���R�3����Zws�|�<���R�}#c;���N;[�>���!�zW���8qB6l� �V����t=c_���5>)2��X��[<��$p�$����
��@�@���X�:�����n�%�F�i�I]]����9W`;����~����m>������Z9u�����g�}&999z����T�����?J���J���J�wV���{�
����@/�v�9�q$_;[�#|�z�{���r4�H��|=m�EM�������������{���o�-�w�v:p�(&�L�>[;����@WXRm�z����b�������^�l9���-_�Z�X,���������?���]�a����_o=������F���H�x�$|z�0z���z�����S���Z;[�M�+s������6g��[��37v��V�ZN>����Z��������R{����6���/�
@�@���~8O��9���@?/yt�0��;y��Ly����
 ������T}��"�����+w]e�{����k�v��e2���������k�=�;>��j���S�Y�h�D��h���=��_M�}��+���=�G��K�����Q�������W�m�z�����:9}��v�1GC
�Uo����Jt�!�xz3��J��z�V�IVn�v����E3h�Ajv�|�����}9�b��)1���������r��l�~���������E�hL��Sz��.��C<B�i�{>���*��6;~���E���`��<y����dF�������d��pk]UU%��p�>��ZE�����d�I�����_����w!|���&M+s_�y��pu�Y����'���VW�{��h�i��i�����������Z5g�����)oIT�1]�x�S��@oE��������4�l������<vX�|n�`��;x�)Z9���!���!�5p&|TVV�U�R��}�-6V��J���:@B����NX�+��#�$�L���6];sK
�
����+�`R�v��|<�'��/��+����GF��������GW����JDE�v�y
�$!K�i}��>�AV�r��{�$�bB��`��|���
�??6^�\�GW����##xT��������
+?m�?��c��1���|�G=��;�e���lE���G���m������O����30@WlUUUie_O
�d����Y��i9��;n���S	��)]���@�SP)+W�jg���1ZWq,�X~��	y�����;";�6�rt�|�J*����o�oO�?�1���L�i���L+����#��o���_6m�$R��=�8|}������G�C������,>��_�*U���Z8��L�p�N����rH�����
9p�P~�������viFj*4�[��v���>jd�X������


d��
���&%%%���%[�n����IQ%>�R�h���8X,>�#|�"�~�p_����Mr���-�.�p/�M���g�M����0���
A33�Ehe_UU�V�p�;��8�Us'�^c
9�z)^^^���Gn����:���%7� n���T^��~����5W[W//E����CI��jk��Z��t��#�3|d�����$11Qv��-k���w�}�:����Sz�c555�IGfj<|$���r> JWl���W+����[�:Ur/=�����#��)����P��P���������?p$4�[��������1BJ����u���2��c������n�:��g�5l���o
?���X�HF(�Lu��R��/��/�J�;��Us�a�%?��$�>}�h��>pS{N����������E�,��
�>�����o��<�l���cZ��+�eff��5k$55�:���'NhuIm~�����R��]��(���r[}�
� �l1��y����k��2���a��p%��O>28
���"R+���|d����1Ui���R]]�+�[�!5U��/R_W��H~`�X,��&44T����@k��}�&�Y����0<T���p%�Uur4�X;{�kB�e��p�3�?�j������g����i����l�D�kk���F�.�������vm��O���Gn�Dz���Q�v���G�5Jn}�����G3�Eh���L>jk@�'O����t��w��Y�Dj3�4�z�0D�m����o_���Gnf�����C��������EZ���+"�j][&���k�9��?/�����m��Gu8�1�GM�E��Z5����2C��m���6g��$�7(�
��4�/p-��Z�|d�Oh��L�]�*�(������{�2�S[.��&5e����&���m������$88X;8����8�S.+W�jg��E������N��kg�� ��qZ9����O>*..��?�Xrrrt��W]�LO}Y�����|1|dL>R���R���%���VSL=h;�Gn��������Ye���o��8�l�QH��\?��v�f]��s*+��j]YY�Vmg�N�8!6l�U�V���g���1g?���B�,M�[F��./M��suE$?�������Z�G�����u����#�������#����"�l��
���2LB��u���@o�::\��UWWK]�������Gg����>��8����N;j]pH�
�Z���t�[3R�t@����G}JR$.�v���#WTV#+W�hgn��a���?�pu���O>"aA��������1Z9���R�������RMM�v�;z��|��'r��9]i]PU��>��v��\�${�f6��t�������d��M���^PP���;�@�Ppq+W�Ja�����W���G;��*���hj�v����X�^U?yz���
?>(H�uW�\?�mS{���,�V�������/���S<�+�{�c���������/�MF-�-�4�����Tyhg+�,C,^{D�V]in���Z�-���G�d���������N=��P��	?!�^?8H;�i������ �~5C�����h�=����*��W^~)�c���P��_/))��4����&�����x�����j�XdO�G�V"y�C��V�%�u
a��E'dr�;R�-����iG3f��a��Ch�G.��^������kxt�0�����R������[��|������%9�T�6w8�H+[�cC��<Z9�Q�(==]��['yyy����2�pz��������?%^C&H_O��f�W�u��!? ���LD�S��d������&.���=��"|��V�J���7�L�����q,�X�=wT%IYE��:]*�Y�!_���a��g.�%��|4.&D���������e��mRSS�+������������ge@�I=��o���7�Z�O���3��Ln@���<}k&�,C�����
�E��OB~ks�v���G��<�^���|��G��������l����9�Z�gm�����Qee�V�3��]��O-�'��������I��T=s�%0B����v">#�Hd������C�S��<�t�9��:��G��_�����z~���9M}q�P6 @;p�m:�+'�K�s���N>������@/��]�i�����2�8<����	�W���WLGM���X�.�~��gJ��<�l�������2�G��z�.�����x��^��\�kg��t�OB=4X��~�v�;����V7>6T��UYZ���*�.��
W���i������b���9�3��.�?$�`2So�������(���9����@G>p!)gJ���ign���Z�����G�L�i4.&D��U^�������R�[g�GQE�eF���w�	��'b�u<�9����j�g��,K������D"J��R�_�:���ya���������!��k�������q]>�����y�Ur��i��G1�{e|�	��y	X���N�]B������^�h���o�g�X��3&"E��`��ou��Udk��W4�#��@��E���-�����Vt?yd�0�@w�;�H�E��
�����J�� �:WeU�V�<�k�j���^�����}F�h��M�7�~k���Gh	�w�u
���Qb�����p������*[/_���v������-����@G>p��U�rU�v���<��>&�M�N=�������x��������Q20�OO�w���Zu�����5&*UZZ������w��f����S������l��~�}������/b�	�.��uQ�Ge����k��_�X��
�����S���N;[�����"�����#u�5Q��%c��Wn���h�������/��n"3�FH�P���HT?���C��k�{w������=2�����,��{�<��J��?����=r�D��i�X�w����P;[^�Wh��"|����6~����� o��#p��<�/���	���Oe�wY�_�|R������N;j�o����"����i����)��73��N����Wu���J�ly�V�N>���?)�Q~�<2��L<#:����~�>�� 3�����^��i��"|��*���S�yt�0		��t���E���N���yRR^#U���Q6���
S�\EUU�V��k����y����G�e�25�5k�w��;a���l��Fke���3%x�$��y
b�@g!|���_�"�����WD��S�i.��V�HM��)@M��z��***���S[!~��5hm������G�}c%��X��0j�u��=���J�~��� �q�u�9����s@�v�(�G�d��y{�v�<=,��G���t WN�7�
�W�zdp4����B�}}�k}����mR�����"�]�{��,#�<|4����]y)pp�cZ5��7�@g |�M�_����%��JT?�����f�Sw����#CEq�V����$0$L;�S���2%6w�������k�U��+E/xD&e�O������R�j�e|�2~��zE���$������k��w�	����3��u��G�������U���+�B������C{�=�x��M=2T�je���C�C#�slt�&�[�I����%���y
+���)s|3�����
5{%��;�g�uz�%�I�}����&�?'�W��g�Y\f'3���
��3���u(�H~������O�{��-s�L��L=5$X��<L��+����H+[>�^����;>g���g����x���zy�M�W�����?�G���R|��C��k���`��X,�c}��mzU�i��:{�E��2{au�V��pC��&�y����>{X��8/�e5r0�P~������3���M=��u�d���_<����r�<��Z\YQ��-?_?�����*]�^�)qy��g�8	��W��
,-��e�����e�d��u�z��>{�l�5]B"@�.��.��7g��S���� /���5��7��k���%��#����z���
?����������o@�x��>%)�bk��O��$Aw�R<Bu���0�,2�d\c\{9� p�>�L��*d��T��=�8F+�J+��_IUu���z�����97J���sU�5Z����~]r\W���MB+�J�]�����
�^O?��V
���/�����[�Z��2H�m���Z5|��/w��9s��'mG��2Y�*Uj����u�����a�����'�r��V;s��I�����Fi�>�����/8��v���25�M	-?#>���_u^�g����]x��=b��:�7[�bE��GF�g����5�5k�u�e�x���t�R��c��\����m�����7�W�,���m)���U��g��f�q��������|B"�o���-��2#����W����*:.��<*�W�a����-[��y���������������]��@+.��N=r�������S;�Qo~rZ6)��>�3��w�Q]]���y`�r�w�>�Z{�!��U<�K@�x�������y_��z�����	�<��Z5���G��m�����3���>�b��N���j�l];��\3��v����,���wFO�zTYY��-��r�_��0H�������	��Z��P���q�V
�.]��}�f���AgM?��c�V
Z�:t%�G]h��|Y��Y�lOP~tq�v�3��Q�V��/����Q����N=2��ie���B<�����u���?�V�[�|�V��i�-���@Z���S���<T���h:*3�\'ig�r�?��/^"B?��S�UE��G>R����7o�Vm�m�6�pO���������S���)�����j:��C��7�����k'����+��y����(_+[��dpJGC3g����4����Z�b�,X�@,K��X'��L��	�OX~�����3�����@�V�FF�
W�������{�[��Y4�m�*K������Gv���U���Z�Vg0BF��-kJjd���=�L"������kR�2���C%6*P;�2J$)�T;[K�h�`�����/�����������<Z<s�^��*KK������waL2j�9�&!$H��"|��^�8S��kgk�� ���������P�V��"�d��P�.�>&\^X:Q�,�!�}w��s� =��*+*������w6�|Y�|����_<�n�j]k� ���:QZv����4��=�h�V�3m9����Y�#���*������$f����5K���q�F����k����Ku����kF�������h�G�����Z��sn����}�2h���y�����O��o���l���gD�'��<������g������r�l��h�]<���D��#G���>M-[�L�m��mc�p�7��cx�<�7o���=w�\�X�/g%&&^�:o�ut��l��[I����_��l�xx��@��������#�����C���j���ejjj��q3������s����?L�Ml'}��8Y4c����$Y�#[W����-+�NOOO]�y���%������,��}t���F��1Wi����$����:..�G��~�mu.w�a���2&��F�4��L4j�gg���6S�.������8i��%<Kh���N+��h���>�{���[���}_�fM�a�����
���yEU�N=zd�0�G���2��_�3
�{?Ur
+e��<]�5!�[��'##C6�_'�V������~a���8W�04s�L�l��Q+�d������h��<Go>�2;��������sp���C����:M�*��jf��~r���@k��8/��f���6]W�UT���_I���j]�59�O����td��h<}='�~��%��Z��ZNjZ�n�V����}�Y���z���7_���;w.c��/~�����u��o%������Lh���c��o�tz����Y��g��Pw������:�f���km�D��������HJ�,��Q��������|���C%..N<==u��U�H*��#����#b��NX$��o��;r��:t�Z�E����;��@���$��mx���}�p������i/�}�����={�vb
������}�/_.K�.���j�g����6��1&5��4���z�sd��a|�M������&��
�:t�x{{[k
N�:��<����0����s<2,Y4��m���)&�#�r8cb��V����f)yc�T'����\�?�-'��/�_�������
b�M�
@[���C�����������k��<<<888ZM������GSf�98z����y��~�}�_��>K,��^<�����E�������| WN��h�T���=1�W+�P_S)�k'gBF���{d���e��/���7H����k�4(?/����e�����5�N92�q�F�t������@�����ZS'��G�yom���9��!��+!�����U����k�`�"�"�����SS*���r����w�#C��G������Y�l�Vm��iC+V���"�g��m	 ����y���m����Z���OJYE���{t�0��_;����G���
������]G��&��i�����d�{o���>R�7@W��r��!�����r���d������,��r&�c\�n�:�:��9s�V
v���U�ZN`Z�t�V�
�#�6(,�����$��RW��5w��u� ��3M=����&�������A�j����-�Gg������}�v�/�����*:.��^��c�%����5Z�h��a���~Z+�Z^�����Y��j`L`jO�����#��2Fr�����<:s����S��Wn�pFkS�>7/Z������E��=��>������������K^^���]pe�L�|W���(�����_�����/_��X�<,���q�+?M?������s�5M=��Z�v������BI9[���9WF�w��p���G~>r��(�D&�[g
����������|��s�vm�_U #r���)/��`o	y�%�{���YZN.j 5
��X��&xdX�v�V���3�x��chd2�E��-CH���2xd�o�	J��#'���r<�J;sW���<8J;�����5w���G��|�P������@o�z\�����;g
���;�u�VIJJ�3"UUU�s������1��Om�����	Y�dN�27���.��PB}I<��0����o������e�l�G-�����E-H�����0����>���h
�#�V���S��P�v��
�'&x@{�e�Q#_OYr�P�����1Zu�����G}$)))RQQ!�������%77Wv��!���z�}���2�&E������o���%�O����W�x	\�C	���xz�{�
�� #L�2�c������'
�g���/_�+�3>�G��@��E����8�o�����g�37t@�5x����+����b���'���; �����x-Q~s�h���F�_�O���($>^?8H�q{�\?��^q����G����]s����~�z����2����;d^��2���������{����s�������;�n�:����1=f #td��tv����K�^���P��q�HW~���r��I#Pz�M�6i%r�5�h���u�������
���_+���u`OBB�V"#F��
���%�\w�������m��IMM��6n�i�)�k;�Z$?��q).k��u�1����N�>jMRR����Z���8�����TVV���;[
�&���LO�@�����?��z@W��_+�/��:�;���ot-�y����/~�����94��`�1�����Z�����7������i��~��L���t���@?O��#�G������)xdp4��������
:<�(���S�m
y�D�.��@����V�jg�u����A���|0WN�k�c���s��s]��QQQ����M�\��F�������#�&������$�����#eb|�v���6�}"�;L=:z��l��U���t�9���'�##�d�����5h����#��D�<���������r���G6�yS�����;�����b���^+����P"�2�.�w>-�s��g��G���c����K'������d�?�@k�����G_�5�e�����%55UW�3&MM]���&��������2�o�5t����������^/5�L�z��TV����[��5�����7���o����c�%2�GB�%~p�<~�p�iz��������$11Q6n�(|������Y��IF��^��
/;-�2���g7HH�9����\�\ ����?�,�#�,�G�W;w��<�/��s�^;XL
�8�����������Kc�_#�?9E���Ty�;W��i�<***���9r��l��Y�����k���u�d��=���m
#�&���u��_u��U���2�#A�&7?���.�?��?\������J�k���NHfN����yFY�h�v���5�sn��yj�}�)F��o��_]V�^-[�n�C�I��)��pE��&���n��g�j��#(R��~PB��_���>�R]}�5x��Q�+����G�}O ����A�V���Gw��]�INN��k�JZZ�����j�x�U����$.w������-��U��n�����~q�s�;!|z���xR$jgn��p���#���d�<���$+�BWl���##x�k�.�:&���LO{E���c/��P������D��:+^K��G��3wEl�<��(��a_B�����r��{e�o�[��~wk�N.��{��7�tH~�r�d���{�r��G�<�*:.3�^���	�w�?�G�Q�^����,����\lT�<��(����J4z����������r�|��e�Y��~�)���G�7������z�}�=����G�U�2���2>k�����G^��7�Y����Z�k���4yo���
���N<

�������������������z����Om�D��Jl�n���J�$� ���)C������#��z5��>��3���3�3�e
EE��
�[ai��h�1Y�3[W:������G��k5xT__/��%2����:��\���\����P�^��E���z�������$7}_�zF������"|z�U;��k��3��i�'%��
�[BF�|�/�e�����~�~^��=����t��1E�������}�����I�q��eX�>�S�*���$�����W%�������'wK�wV�����{=�#��}�/G��v�v����q���{8U(���������57�`�|��%#�\�j�?Oyp~�����d����z��������%??_W����+#�m����/H����x�g������v;/�~5A;�~��2}l�v�kw����RiE��W6U�uzE�4����k?�"�8D|=���s��9z�������}f�#C�]�������#\��]��+�Zg\�`�����m�^�>]����q8�H�z��v�=vg�\7��v@�d�a���p��s����qJ��1�:�1'J��1Z��	��	
����AMBG��:������w��c��uk��o}B�G������7�t%#�c��-[&�����K����g[�ikP�+_�u��J��OHM���4/�y�,�9@;��2�0����������~�|��yh�������ON��~>U��������!33S��Y#�N9�{���n<���Q��|�v@�F�n����`PK�5��������]�L^�5xTXZ�+�>�`�w� ��=:<�rx��������]q
����l�"%%%�j�g}�L8�J�rw�JK@������U]`�p�7�����,�fLj���?_���g��1s�L�5k�v�����x��k�j� z���mD������.�6]z��5�\��F�h�sG%�t��������;��s^bb�������b���8+!!A+�#Fht������9W�x��4>`������r���K�r������Z{G^^^�@�:<�qJ?������g����G�J~~���+�j������v�T��q�?���\]���.>���7�
��$))�b4�����g�Lo�����\����
����KNN��9�����})))R]��������������:��c�fG�Mt�������X�b�,[�L;���2Hd�d�����|��D�@OVS[/K�~D'����'��e�������c�����n�&t�����[�:|��;���}6����}R;�M.s�������J�0n&��k�v�Ut\��Y+�
!C�� �#�j�7"|��mu.w�a��Z������������<�3����@�i�i����82��k5}_3]�������'Z
������v���Uwh��)E��Z�>;J����f���#�G#r����5��G>#�<ZA�]�x:IS3g���c��DM9jz��'�j��5u�k���^I�]���37ax�<��(���c���Z5<d@�s�|�������=���k���#���������QDY��L����}�+��N�U+�>B���'�6�i��t�R��k�k��P���
�|z+I6~������A���G����H�������C]���^N�s�+u��<zt�0�\GG�G�y;dj�RqVW.��"�����=��������Mo�i��._�\+�����k��TY�3[;s�������`/]z���J
�}<��	9�Z��9W��?-'�����Q�������j�o�mZp���^G�����������G��'��3�m
���z����"[�ign���.<2G��#E/|I�����/��?�-��9[���BM�5�;�V�8B�]���� ��+d��b�X���3����zZ{�oW�6���o[����t���z{��������>;v4��1k�,�����ro���1��om����dign��py����u���,)��)��b)[�I+����Y����L�O����N��HUb�>������9�����G��2ndY�lY�PR#c}����]��Z�4���O�����v5���~��k��s��wS�������1!��?�u���1�aZ2�k�uc����^�(S�{�������0B��S��G�V~Q�S?���C�9<���+��n��*��? V�������t�VZC�]��M*f7��1nt1n���"@���H����D����#e�(�^4��1�\�c�x?#������������i�b.��S~t�H	����cL:�+���m	�<�I;�+H�#/I��(�}cu�>����=t�vZC���������[��4[�n���d�ht�T�<����������5�h�%c���i�?c��6MA%& �#5�L�������� �#?������T|����9a��<��3LnZ*Aw��B�-�Pkm�����w��Zp�#t��7j����e����t�R]i0k�,��q�K��\:����*]����%d��S/��z��yt�0�iz����l����b�����0��}���qv�5����p�|�����?���{������*)����<��:��Gqu�����$��_K������������
<��������P"|�
�#t�'�x�b��1x��q���>M7���(�=2s����NHIy������h���A�0�Z�=���#x���S�R���,X�z @��K(���xB�f�|�/������L���9g}`GYe��������p�k<�����RRY'�KZ�D]�o���2���K�
�����3"Z���1[�X��n|L���F���\8l����6W�:����O�5���tZR����8���b��op9_�l��I+�A�5�x�����W���|���k���{f�h�u�������F%h+��������������W�x��&]��������������{n-�l�,'_�K��|������:Y!/m,���[���I�_�r�,����/Kb��u���Jt����=>��I���Gy������K���z.��U�h��b��Z���i/�}�k5��1�������$''_�����sh�����������111���m�4HHH��}{��C��6I���M���gkg;5�#��������^�l�������{�/
�*����/k
%�l�7A���'��
��i=p���M#��WcL;Z�n�vm@KWkzS���In���HR����_]*W���9#x��M�����e��
�����r�����Iii��m~n��Y<�:�s|�����nJ�8w�a��Z���W����G�p��Q���C��<\N�Y����=Vn(j5xd�8E��|�������;���2�j��/n,���Z�.q6x4���|���n���]��}{�������.����7c����q,<���eR������:s:QW��+h:���X���
�y9A�������P���c��<���.~�����(���S��>|�V��:���=���>��thW��h�D��3g�4C���W���[�hg..*P~�c%��S��U��f����z��k'����;\�<;�{���O�r�kR�yD<#�������k�l���Z���]I)l�<m�Q�F��	�k?�����c0�����i��)�VpF��-���q����
�����0���d�>>?��|8�������+6;"l���5��:�|Y�v�v���q�e��+_�4
��;���t���yJ>���v�F
	��_m���rJLL��u4>>����F|�W�j7������>7�����g{$��J;��
�� o9�R�+��u� ���a�u����H�k������.�s�l�333e��mRZ]g����;��=ZW:�x�Hc��x��f�Z��mu.w�a��Z������������<�3����R��qt�����q�F��|��|m@��{?����������.{�pG]�7�sl9��T����]�������R��3�5W��_�d�?d����e��v������wZ�@� |�.a<�H�S���-7�����y�����H���Sx����d�+_`����[���3�'�G�|h��-����,X�@W��rO�������r�j��'�wf�(##C6m�$�����_�^N�z^�J��ls����G�Ur��)]i���\��#��_(AAA�
�U>B��9s�V
v���U�ZNZ�t�V�9n2�Y�n�v��&3]���oo�������1���x4����p���Q��[���^H�����W��#-�<}�>�<***�������[%++K���%77W�{�Iz�������������;TW��_U #r�����H�u��*WC�]���p����'�co�P�@��O?��}-��j�����������S������QC�����Gqv�Q�}����q�S����B�����#Gd�������+���w����k��d�9Z�.�������+O�/s�^���/\�wI��o�Gp_�
��!|�.�����g�v@2��4��Ohe����%G7���	55���\��P����S�����F��a�pF������a0��h�����sZ�d���!Z�.,�[�s��v���j����r��7d���7^}Y:�g��Y<�D�k��$�"����Q�!s,�d�OQ/���y�����t���o���J�����q�K� �.Z�b�M����e����[��@����_��M3��k�je�+_�`����K'���{�����#��[�n��A��������a�Xl�9=@�8U(ig���u�����"{���e����G����*7Mo=�����D�lZ%k_yN^{�u�h�)9p�V�jB���������� ����j����2'�G�i���U	���$���$���H��^�����\���Q�P^R_o���q���O?ms�Jk���3!��[��Zc�t��{��������/~~��;�zSt��i�����R^Y�+��vk��1'J�����x��h||<_G���1b�V@�0�{�;�C\��A�b��-[���\y��������������e�w��7e�n��G�����~0�Z�X(��J���J�����"d��m�l�222$3#]rr�t�c<�keP�a���+���e������%`�wt��KJJ��������8������_+8�����N{!��]�}�������#���|�M������"����:&&F����5�����l?��G�R���"Z�|���������A������d���-7�t�k@o�v�L�z�D����q�����	���q�������:_Jv�l������&��JdR|����	������ON�o�����8������������wZ��`L=r<*���/t{�@� |����&#�dv���f�k��-��FX���[j|m�Wzm��r
���OH�����57J�1Z;���a�c�f�3�9c���Hbf�������?�����l?���NV^��w�i���;,��f��9xB�u�.���������h����9�	K��S���v����	=�1������z���7_���;w��84�*e�����r"�XW��4��|����������_G����:
m��b�����s?��>������km�����5�Y�N��W@�K~t�H�vb��}<_^���I)����������}�9"���c�+s%�4UR#����U��uc���{t�{%%%Imm�����8������_+8�����N{!��]�}�������#���|�M������"����:&&F����5�����l?��G��x��������e�G����O2�O@���'�]�!��>#q����������tG^�(S~��q��G��&��Qff��Y��S�G�U2"g�\����:�I�����\y��.<�9�.���'�����M.?~`�vt�#	��xt���S"�^uP%sbr��Rb
�SW'��W�_���sB��������l�"�����<��K��l�(�iC�����r�_�����+DF��,>���96x�`4h�vz
�G�K���D�v8O;sccB���Gi@�I;[&���y�E��kg�����rIjv�<������]q���y����s��6O;��)�!eF�!����r�������,q�K�������V�_#���*�}c�7 PF�����q����>]���%��=��330P�|h��z��@�3BD��K�k.���:������n<��Cr2�DW��-��+Nn��_W�cL9Z�n������c��##p4%�m�w�o2y�H�y���e� ���������%�{ke���U�
�g����KDD�vz��]������g�3�?��:����
W�z��xy�kgk`p����-��
�-s��OHye��oMD��,�9P~��y�gSe��z�u%%%�iG|�������cQE�ev�2&{��)!_}U��|I�:o��I����]sL=z0�G�����iy��S�[
���������s��270�������%1�D���h�[3jH�5p�����cw�����z�1#pt��q��a������iG����Q��+sdR��2>k�T���J���;��5~~~����'����vz�G�S���-+W�jg�b��C�$~p�����K�2R)���~8O���q����3��|<�'�����t:pTWW'�N��8���������E����)/I��$ko����ZwDLL���=["##�}xx�L�0AF�i��L��@��d������1&M�����F����3�a���G�)�jX�#~p����+d����Nkrsse�����[o�����82x�U����dJ�u���G����e���r������e��1z@OE�t�����/_N���������9w��S��2	�������Z�7oR_��cWJ��@]���_��z$$$HMM�CK�gez�+2����t~��)OOO��t��@�I)��^:��}��=Vn���v����2	������������~a�xyZt����k�� �WT�q�����T]z���]�I>S&O�xB���t���[g
��cL>
�k�:T__o}k&2�J�k�k��������s,//Ov���]������s����5�Q���Cn��#���h������QA���B�;o��w�`�pM����GC��bq<��l�Q�0��Fj�Xee����Sjk�CL���!eJ��2���dh�>=#�;~��}�=�����@�>�R\V#O�tB�r�u����d�����u��/��u�m���FZH�0�GEE��z>�e�Gc�7HdI����/A��A�zF<��*t�G��j����3Kt��u���cw�i�����oK�v��|���?/�cC�sl��������}���rE�����M���-��]B�����������63�G�
�37cl���#�����)��j�Z�S+�U����C��)���#%%%�y�fIHH�sAU�2+�%����*:.�k��w�������_%��$������/7�t�G�M~�j��<�����q����Fi��K�.�`���3j@�<~�py��h]�e��?.6l���_N�>�g�E�e���%�2�a��O�].a��@o������x�!�a�@#|���w���}z������/O������3��[��s��W��i��������<ILL�O?�T��]k
���_rr?m��)�+��h'�9p��<�����AW��#|���5i���l��EE�Y�G!^��{H9S&�~u�9�\�y������n�*�����[�N���#III���xrpKF�� |�/��G_�����#���X�H�y�}�)�o���\X��5x4 �WWp5�u��W$m�1��9p��5p������sE��Qv�{o��������X<��,t�G������~���9/O����Q�+���#��ly�Vje����Z����2�����_(a�~O�f=�g��>v}�����w�����xtEl�v���3��g?|PS�U���)�+���H�	��x�3�>H��k |L�8�'�_K���~a�L��'5�\B|���R~V��3GC
�������7e��y��7�g�\�\�#�����
;�T(O�xB;��ug����W;�S��R	����VXy��W��Q�����Pv��%|����e�
\�#�Lbf�5xT�J^q��a�h��p_)g�$��R;[��>��_'�WJl�n����2'���E�(p���;n�^
����(+�B~��	).��s�]?X��v�v����*�/��_]0&bGi�XL��^���b%~��e���(a��1�G����Z�z��d�W����f
�/.����z�]#��4�Y_#�}��q���{�����+�=+�7<&>�n��!z��# U5u���'$9�TW��8��|��X�p��e�kp@�y��#~~~����F����{L<#�
����5xt$�H;sW_)��\�v����r���3��(|T ��b����fY��4z�h�|�-�@�B��^��/�����kgnb|�<��(�p�Y�!_��g��W!�~��j+��@<����9s�Hxx�xyyI``�L�>]&N�h==�#z�?�7I>�������������b����#��d�~���t]��[������k'2x�`Y�p��s�=r���Jll����������Re��l��E�����<u�TYU'z;I~����v�LW��Vke+8(H+�����!C����v�������FI�0]�=m?�'K~�_V�0�����n�D��
z'�G�2�}-Q^Z���9_o����t�SY]'|+I~��	�������kD<���=�k$�_�v�;>��(�����qJ��=�+�����B���TU���]g���?�5;��5
����V@�y����N������%��/kw����x`�L���Dz����$������{J����3���Uke+��@<�>��>�k�v�������o�z���2wB�pO���rH�������l����3���L+[������N�����2����[bd�������9'���9�Z�+��2.T����f�Q���V�{>��i��#�7F�]s���=���������|�P����JN�!),��U[�!aZ@�E�������.&����=��	�C�o�O����e���$55U,����w�V�{>�0�����M;�4"L������A��{hk�(:�^�|]�<4[$y�����5�_\�g�������1�@�E�7�8���O�6�h�W�JlT������+��/'8<V&wM8/_�*C��I����r��!�-��z����y;���0���"|��b��70Gom��������3{���9z�\�_�,{^���H�g�H}��q^|�v��!aQ���#���=����_G���"GR���}�cOK���5�QW-N������7�.]����n�iG�����2�G+���d�BDC�����Y21�@W�.���LO}E���I��?�i��3��>�M0��������_M�}��+�����5192/.G���u������l�S2p��%l���3�= |��c��7H�.���|H>���+�MX(w�=-1e��vA��2������!	{���{�Mz���.�iG�������������e[��-���m�$\K�%b�����E'�H L�i��nk��nY��=�3kB����a&R&@�4�v H���se�����$�A�-�q��9�s����������������S�v��U������������L<���=S�i����M�w��k^�d1��K1]����o����}������~�;7�)��pg����S��|=�iG���?~8��o>����_L���,����������
�|%]��x1v��?��t���B����r��/������o��'=��?�v����T4�|=�iG��_|����?����}�S��tt�y�n�.<�x��/�fz�;�����v]�����������p�5�I>����#V��?�����������l1���.94R��Z:�^��C�u�������ls����J����.��_��#��$@x�����S���o��u�'�cy���E{N��������x������>�����Oz�������A���~6]����.~�{����X1]H>�9��#V�����H���_.���������/��/;\�i�����o��o��^~2����M����k:��~:�~�w�����
�QH>�9��#������?����}���7�X_;�n~���?����_Y�li��c�e�?�^������T����������w��G0c�v�*x��O�����z��iG�9�h��}!�����\�/�sa��p�t���<�n|�_��^������i�7��x�I�|3�iG,������n�y�����z�{��.>�����������^����������IG�|�+���o�}�����L��#�O;`���~.������Fn���t����������^��o\ZK��'�����7��6�G0e�v�2;~�t��?�r�G��`����{����H?p��[��dz�����=��O����I:��G0%�v�2��g�N?���"}������������P������Hz�y?������^|e:��[���GO�=����;�#����MIGs"�����XV|����?�����I�y����c'�w�<m��������?J���O�����;��>q(���g��y��������U�x�y�|�iG,�H:����p��~�O�'}�{��]�����?=������|S:������.���-;>���-;���?�v<���;���#�O;`=�������>��t���{�{��t"�����z���_zu����I'�w�w��/�7<�@������~��i�R7���G0&O;`=��'��sN��/~:}�s��t������=�����t��/O��{N�N;��\z�_�'�������/�]�����D�����X6��p���|:���?�>]�������������]�p:��W��/��x�����Z��7�>�������c�#�G0O;`��{?�n����}�+���"���������,}���>s���w�{��{�-���^�?�v��;�����#����X6�����������>[�9���}(��W�'��k��G_�w�����trmg�n;{v�J�z����������.+��$@K�v�2�������W�L�~�P1&��k��[��p�'/��t��/M_y��������}��{�w|�w���J�]w]1�E"�Z��#���S�������{~��bLJ����t������K����t��+�w�[�(�^x"���oN���H�\s��,$�G0�����)G������{�x��N��\���z���_�����K'�wSv���w��|���������/���"�|
<��e����_l&�������'���>��x����y�Stw���������7�`�����,�GL���Z�r�������o��L�G}��j����\s�5��=������r���Smw��w�~&J��-��,���e���u�^F]���
O;�����$���nH?�#?RL������=����O����#H����3��U���^����b������^��W�7�[�UW]U�`�H>�
O;`�<��������s����p�����_�Xz���>s���)����t����L������t�u����$�O;`�<�G����_�v��'��^s�g�M/����y��/^��tjmG1U;�ON��w<�������G�k��w��?����,3�G����vt����N�8��y�����}-��_�e���?�y���������x��?�����(=��C��L�}�{�o��o�����L��k��~�W~e��c�=V���w�y�L��r$��_|8}��NW��zZ_;]���sN?�^����������� ���.�`UH>`e����c`u�:u*9r$=�����_�j�����>��������H������>����| ���a:x�`z������������J���������}�C�=�yO������{��{�?����~����������d��?�����~6}��_L�?�x�����:����������G}�X:�����{��h�������3e~���K��/�/=g}�c�Z:���w(��������'��7��x�U$��F�x�;6�BP�e�iG���'O������z*=���K_��fr������O}�S���h��?������@H��w_���?����������p���b�H2���g?���l�+�II�_��W6�3�;��&-�����[o=S�im0+'N�N���2��F?��?�D���O�t���w��|����~�?J�����XV��#]r�%����>S������i?���x�O<(���c�m>(�O���������m>M(�*O��_���o���M��������x
������T��zh�)E�D�H>�D�x���?��������)G���i$M�����bV��>�L��_����������,�:�W#']|�y���}���?�.���b,���QNo
�u���Q���>Xm��7��b���������M��x���x�����B<�H���|�3g�����Z�(0T��8t���t���3��:.�����hv��������W�^�q|����-�t~����H��cf�������;7��$���J�������G��|��v:}�5�.���v�`�z����2]w�u�X.�T�x
h��o���c���a�*m������C)��e/+��"�B�m`�9��7,�����%���@�Gyd���p�5��]�vm[�y������mAXN��XU�����G����_c������\�^����1 �����G����o>(J�pLSr��o9���M?�������F\����='kU��"���������=���9�M�x�3���x*�������+�}$]p��t��S��7��t���-���$�hC[�����Z�X��
L��#Lr4�@3�G0�8�pu���-""H�I������$�G��N�:��DTM&�a	E�p��/��O���L��]�������,�A��~��Z��R�x��?�y����_�7��.�}<���#��W>�v�8UL1�5_�������6�w]�����+�_������$mh+�6\�2Y�����|�I��f�h&���.c�EDI`9I>b�x�� �K��u�D�p$����+>��m�.;OM�N;��������3�v�7������7~��=���k��?�����E�.q�rt~����U�(�>������������f���/~6=���7�(U�v��-����ssV���
m���R&k�b!�60]��`0�����$�`�<���Cb���� 	,'�G�
O;bZ$A?4%UK�\�d��>UI:r6I(�F�P���t;���2�(��q�t��~6����*^A{}�h�E,���;�:����b�IX��R~���l������������c�+����4�������?Z��AB���h�E)��H�q�.�G0��
hf��f��`0q����!1f[DD����#�T/�~|��#�M�LO�8#y�.�����:�vn&U�������y�P�DT��~j+0�;^x]����'��������-��2_����_I�}������6��]��G�]p$�}�[��W��b,�	@�
�
�L�"�B�m`�$�`�+����I>���y������mAXN�{�������h2�^����iG���#�&������p�LEd�H�)��6����f��L��O���H>����0z����sOZ��'���{cx���p*��m��������{��w�K�{�������=gx�o6����x
c��E#}[��e�(��_�b��?��������>q(��S�M/��?�|�K����$mh+�6\�2Y�����|�I��f�h&���.c�EDI��8X5���'��6��><��Y�|);v�L�PY��N�:U|j�m&IC�$����#g_�m%m�W&����-#��H��D�3�@��[	=g��"�g��3�@1nG��x]L{f������^��.���N������-���\���g>�L���>��|��������c��O:��������7ca�I(��Vm�mM�"�B�m`�$�`�+����I>���y������mA&��������|D���z��&��e��x2QSQu��g��mt��i{N<��w��g���o��[����w��#m����H
��T	>�&
m����������g���{�>���'u��y���my�����������/������^�������e{NJ/���7�%�]����;��+�-�$mh+�6��&k�b!�60]��`0�����$�`�<���Cb���� 	�PW�J��|H>��E�w=��y�|�����g�}v�<��3�����2���v�xv��������e��H.��1n���~��9im������7__��:���������;}Rw���n���nQ/J9z�d�o����{"]��x���/�{��������,�U���g�s7��>�9n�_�����{�s�H(��Vm�mM�"�B�m`�$�`�+����I>���y������mA&��������|D}�w=��y�|D_E��L&�&����+�\=�O�L�>�����]�7����D������IE�M*:�`tEZ�N�D#:�q�O����<����@�-�E)���W��|�#�����c���_��bh�8_���s�����6�z��J{����1@���
m����d-R,D��K�&���?���#L�g��xH��$a�*\I��G�'�M��t^<��>�|����/����9RL�v�<����x
��'��zBQ$�yR�������Om���H"�J,��H":�hT$���W|�I�q�O�v�H�����z����v��sO1�$,�E)G��J���L'���t��)����W�;��%]q�����)��M?��/�~c��=��0���
m����d-R,D��K�&���?���#L�g��xH��$a�*\I��G,�Q�v���}uz��/,^�dH>bZ�^U��$���S.����m&�-���'�I&:�d	E[Um>�(K"�|}Q��y�S3/:��I�.���0���Y��Ro����o������L��]^�M�G�G�����G��Bz�W?�^��[��W��t%�hC[�!�5Y�����|�I��f�h&���.c�EDI�����#���7��G�LT�\���S�7��"a(�T���3�$�����Y�{������&�M0�b��Eim��}��N������-���>_��������O}8�sQ:���r�%�o���K��#W�6}�����;��$mh+�6��&k�b!�60]��`0�����$�`�<���Cb���� 	,�j���������r$���~6���O��'Nc���#�M��9r����2���{�T������Ce����d�H$:��7�VT����x�Q���7���/�J&�~����D�"�hm���,w��o���e9:?�{NV�.J��G�G�����_IG*O9�3(��=���W���O���'�����0	
�6�@b[��H�q�.�G0��
hf��f��`0q����!1f[DD����#�,���W���j���Oc���#fE��j;v��9�Dy�Q�ZD��O���<t<���'U�^�����S�6�F�%�I.�hk��y�S�Jt����]4�"��0����9Y}�(%����'�3��t��C���K�w�|t��=/��I�P�������E�����tI>��$W@3�4�|���W�1�""�$��$�7_�����G�)���iG���������r�������'����z:o��;��F��E9�����6��^�=�h��M0�b����^|���N�����,�A��~����E)�����������~8]�����J��_��bh���'�E{���W�4�s�=�X`R$mh+�6��&k�b!�60]��`0�����$�`�<���Cb���� 	,'�G��$���1����S������#����g�&=��h	G�\�����v���d��R$m&m��>�hm���0w��o���e9:?�{N��.J��?�r������+wN:��s�pz����k�M���7c�i�P�������E�����tI>��$W@3�4�|���W�1�""�$��|��bH����$7j��T�Q�����G�����>t���}�j��g�yg���m����[O*z^Z�����%�^|e��Q�.��e��0]:��I�.q�r�F�z��(��'�H�a:��'W�]�����t��#��o~��#�!	@�
�
���Z�X��
L��#Lr4�@3�G0�8�pu���-""H�I�m�,J]�_x�pz�c�K8
�v��I>�����I&�K4Z����'������_O��x*�-��	F�w��V�,����"�(��&������z�%��Y�4w��o������E)q���>�����?�x1����<�.���t��H1�%	@�
�
���Z�X��
L��#Lr4�@3�G0�8�pu���-""H�$��M����b�Y�|Du�����4��e�N?��x�s'�h<�N�J������%^G ����G���d�<ul3�(����x��y'���v�<ZLY����<O'*���z<����o�]�{a11���;}���F\��L���x�Q��e��L�]�����M����������X`$mh+�6��&k�b!�60]��`0�����$�`�<���Cb���� 	�PW�J��|H>�����4�������^y����V9�(�5u	B���i���|���O��L"�L,:���wsx+�(���<\Ly�����y:���xZ��S�6�6��2�q�O�v�H���n�<��c���q�rIG��y<�����v�'I�@B���hClk�)"n�%��\���L�&�3\]<$�l���0	u������#�h��N/�hW���/�pDo-b�Q,����������O*�zb�V�Q����Sv�I;���o>��,�������[L�A��>��E#}[��I^�IG��'�S_�Z1��H:z�E��+��J:���P�������E�����tI>��$W@3�4�|���W�1�""�$LB]�+�c�!��6��������?���#V�������.	Bu�� ��=�������Z�=��j���n>�h=�Rt�F�|Z��i�F�����/+�J:��I�.���@�M���}����>��t��S���v�8�����������bh�;���&EB���hClk�)"n�%��\���L�&�3\]<$�l���0	u������#��������bh<�XT]���$�jRP�������v����'��L&:��F��	Fe������L��^P$�M&��b��]�]���'}�h�o�}7�E)�����?��t��C����8�X��q��������/,��#f�%�hC[�!�5Y�����|�I��f�h&���.c�EDI�R���#��������<���kG����:�,����k1���#]\|��O~�L����^��9��=�>�Yb8��7fo��#�O(�	F�X�����?�����S�6NR���K���.z���������Zt��N�����,��7�?�3�E)O=�T�������=Q��f��C��O}<�=�x����7}������n��`�$mh+�6��&k�b!�60]��`0�����$�`�<���Cb���� 	��B���|D�/<~8��c_�(O�O<�t1v|���8E�P�H*���eB��C��1	E������$��IE[�[O/���������/-�*	F]��w��E����q�O�v���X��X��t�(����������x�M<���O~,]�����>����u?�~�-W�v���lI(��Vm�mM�"�B�m`�$�`�+����I>���y������mA&��o,��{���!fI�U�Xf��;�<T�\t�����$����Si��ci���[�d�-^���G�^o���v�������z�L*�L"�8�������xz�����7}��N�����,��������6��?����n��c_L���_�m'�?����|����CW�������+����MW�����S�F=���P�������E�����tI>��$W@3�4�|���W�1�""�$��$!��E��2�h�iDer��C���Oo>����c������s��[?u�l��f2P%A�_�w6qh+a��c�tZ�{AZ�sAJesx��������7��f|��k���i7�����N�����,��������A�|��_N�|�#��/=�6�@���D���_��t�W?�>q����k�+=��W�k�� ����$��'0[
�6�@b[��H�q�.�G0��
hf��f��`0q����!1f[DD������t&���O�O<&��~����Lt�����H&z���D��'�����	�'#Y�8�+�"h��H
�J:�4���S��������+�Bg����d��H*����x]M(�D"@��>��E#.bYu�c��9Yu�<��c���Hz���}�2����<�����|�������N�z�%�hC[�!�5Y�����|�I��f�h&���.c�EDI`9I>Z���8��&~�k��3_?�t��G��c'N��'N�����OQg���������'�;���$�3���D�O��4��1|�����	�}���p9~O�[w�0>w��o���e9���%��d��|�_H���7_w������O/~����?�����M-��t���C����o/��I�P�������E�����tI>��$W@3�4�|���W�1�""�$��$-7	GL���G��C���'O���Z:��S�O'�$���N�#'N�#'����#6�Z$m�����l���F�����������3�B�#���S�6��z�P��w��o���e9���%��d����_�w���n���O}2���N���7�$��	@�
�
���Z�X��
L��#Lr4�@3�G0�8�pu���-""H�I����F��_���J���H/z�����������������b�eq��3�p$}�����������#G���'�������;���=���i�j�}��������Q�D)^��{N>�v�<��v��J*�6�ec�L(��	E����v�.�`9���'}�h�E,�M~Q�����l�H���z���������o}]��
�W�����
m����dI>J��`0�����$�`�<�I>�'�h9L�	G�������CEG���c���_M���DO?��Xt������������N�:�����>�����k:��(IC��?��XTM&����zo:���������N��0��{Q������/M�Q��4��w���k�.����/�U��N�H>���_��������s���&�7�������_���?�)����V���
m����dI>J��`0�����$�`�<�I>b�����bh�������$�hqM;��u��<}��.�Ww�(9>L��S������,��NG��Z:��S��f���r4��������f�z:vzG:����b����&�����p$J{�O�}{v�={����_��0t�%[�D%��yE"Q$�9s����g���^{�����t����]4�"�&�(������������:��g�<}�k��{�O�u�_O?����@B���hClk�)"n�%��\���L�&�3\]<$�l���0	u��������2���*������%�6����"�(��O#�L$:��ml��%
�~�T�L��@���3e+�h����{�z��wo���������}Q�L�7���]���G��q�O�v�H���.�(��������~Oz��oOO�yN������?����3�3�������!������,'	@�
�
���Z�X��
L��#Lr4�@3�G0�8�pu���-""H�$�U��:6���o^	GU��-��BT�I*�����L":v��F9���8���<�q`m��l	E{OF2Q���SG�����ki���i��}��.H�����'�p$moT�bn�%�`<:��I�.���@��]���������Z���_�������K�v���?���l��t�j�P�������E�����tI>��$W@3�4�|���W�1�""�$LB]�+�c�!�����pt��������U�Q����q}&a(�>TI �&?z$=rxk���t���b�b��#i��Ci��g���G���S[�D�w��{�l>�h����F2Q�HO%����b.�#�`<:��I�.���@�-�E)7�xc1��<P�"�hC[�!�5Y�����|�I��f�h&���.c�EDI���
WZ�:��w�;8p �����-w�}w���f�cH>���%U�q���,�h�����n,s1������'�Nt*��Nl��i�����m&�A:���7�v^x�����sO1��'�`<:��I�.���0�G}������.��+.�x����m��V��r��7��n�)����,���"_��L13X
�6�@b[��H�q�J�V��
hf��f��`0q����!1f[DD��IX�)"�q�]wmK:�E����o�}�����s�Q����[m�����_�C������#G6K�p$���$��[?}*�>u8�M����x:QJ{w�J��n=�h����.�$�������+R:��������.�`<:��I�.q�r�{,$n��_�R'n����S$mI(��Vm�mM�"�B�m�#qX�+����I>���y������mA&a�/����
7�P�j�����N@�|4{��p4)���2q��LT-[�o��nu����S���SG��S���t2�^?�v�XO�v��=����{�n�����.H{/�$���������=]Q��6$�G��>��E#.bY}���r�-Co�R7Z�����W�#�hKB���hClk�)"nC����j�\���L�&�3\]<$�l���0	w�yg1��w�Q�W���1��{*R���G��l	G�Ptn�P�P�1���g7������YF��~�>u4�8y8�>�DG��v��{���k���g���g���{��i����^�vo�]^����4��{��1
�����N�����,�A����=�n���@��n�}�3��#�-	@�
�
���Z�X��
}"��Gr4�@3�G0�8�pu���-""H���1���')�9`#�hz���#���>�0	G����CeB���G��=�?�����c�����O�����<�Q��'�l&�<},����v���y2�g���g��i����.�L$Z;����D�w��]��>�|0w��o���e9:?�S�jzrs��+}������$mh+�6��&k�b!�6��8������$�`�<���Cb���� 	�����y�t����}H��x���O�_9�����?sN�y���tj��9�$U������O?��<�����MG��HG=�h���x�S�6�wU�wG"�F�u��=iw�H���������R%�h��������$�G��>��E#}[F3��p^�g~���RJ��)}����#�-	@�
�
���Z�X��
}!��Ir4�@3�G0�8�pu���-""H�*��5m�/���|�������W~�l������)>hP�Q$�IEG�}*~�k��3J�c�������9�����iU5>�������^�i�����D{����DT}�����y����oaUH>��;}���F\����������r,�MV���G}������.��I�P�������E������<��$W@3�4�|���W�1�z��$��Q/�yfM��vuO3:y*����?���k��i{�w�J��l=��f-������{������zj��c��������t���t�������T������E���{;c���w���v���v�wQ%y(O$�������������#�����'}�h�o��hn���bh�x���Qo�2��Yf�E)@[
�6�@b[��H�q�@�V��
hf��f��`0q����!1f[DD��UV�Qn���t�}���6}��V-���?������>������ tz����v�o���~���;���w=�.��l�x��t��c��]'��]�6�7�;w��������������]w�Umw���Cp�T�s�P�s���������nJ����:��>�L�}�X�$t��C���N��<�G��q�O�v���X��<Vq����8���I��LB���hClk�)"nC�����\���L�&�3\]<$�l�����F
�����eK>��cO���G�_|��t����������'O���Om�p�O�->�^{��im���NO��%������7�����������t:��������X[����-����_��b�����M**�����E2����i��=��C������;���#�����'u��y�C��<,�qn�R��]n�2+.J��P�������E������<��$W@3�4�|���W�1�z��$L����Zmw�=�C��QS��J>M$�{�g�?�=��d��"�����E/��p���/����L�k���-���7���������4M�*%����������v�x&���3���+���/��%�w�9���=�a�H>��;}���F\��4�r�-����/^u�S���(Xd
�6�@b[��H�q�@�V��
hf��f��`0q����!1f[oT��I��p����q�^4N����3��O>�d���O���3�4:��;�^}�W�e{����Si��S
����6V=i��]'��<�����w��b������vnQ�V��#�����'}�h�E,LC�^u��d�Zf�E)@[
�6�@b[��H�q�@�V��
hf��f��`0q����!1f[�R��I��p���1�G�"q��K����t|���.��,�sR�8k;O�L"�[$�:�l��~2������;��={��}���\������D��������m�����|0w���8>����-��Z�\��*	@�
�
���Z�X��
} ��Kr4�@3�G0�8�pu����G)H�$�U�R_��*$}�/>���ts�_;}2�����Ey�P��A���I�k\;N?�L��w�Z�L�s=���;���/�;���������$���>�h���b�]��G��q�O����<��������E)���
�%�hC[�!�5Y����y`uI��f�h&���.���t�Q�$�y;��������%�N��Z��x4��V4r���$�Ug���t�����G�2=����N|>����_��W��/�zz��'���x^z��_�~�
������;���?���O����3����L���w�����K��=�HW|�?J��������)����i�s�1�_p���h��zG�)���{����,��n���Al�Y�`"i������$�e���Z��y�Q��s_�|��/~�x�^�^�~�qMz��^�&��x2��SG��S����Si�������w�H�;w��kOJ��K�w��N�� �Z������/C�]y����p��Wof���0���<�'�������t�w������X�1�/:��U]��w��c$��?�T���hn���bh�x����/�,��;���f�����5������O�t���K��PO�`��y���x$��Q/j��c���I����+
z����_O/����N=�^����+_xU��E/K^����%��v^�mi����S/��t����cW^��]��t����{.�e�Q���,����C�P������]��]�~���O>Y��v�UW�0��}����/��x��8��������y�����D��q`�y��|����Z��2�;���8wT�E�$�����F��#�$��9��>����A�9W�$=s����]�?���w`XT�}�{�O��O����m���t���b��"���`��;�<�0q���7��".#	�i��N�����[6A����?j�����wj#��n����UJw�}wz�;�Y���_��_.��c3�����������~,}�u3����tj�-:o��b�nv������_�8W�
�����S�^�������7�)����f���
���<�Wuyn���t�}������[����_�/�����y�[lf�i-�5�\�}����Y�]w����+^��b,�N��Y�����m=J�LVU�@���G���~>�;z������>��t���R��f�����"Y(Hf�o��cy%�$!uS�;��Q����(�?���&�<�H1����dZ���|T��D�8�T�1�z�:���q��yj���\��<m��J�����K��_�\XR]����0}��2�e�|T��D�`���!��_`�8�B(��H<`Y����M?�?������_��_HO>�d�U�z���<�t�M�P7qs��Q������fl!�2w�yg��'>Q��*�`�"i��0�z7���	Fm��qq
}R��Zr|`Y�����X�.O>�y������Ox�����LZ�b3�LkY�>�(�IH�K��Y���x����NLS}��;���<	i1�Omnr�]wC[\���IHqf-�������-���KO�Gy���j�^��U�z�C,��L���
����kG5����G9OB���	���n��
��]t����f��������'!�qf�.c�ED\\�$�U�R��X����'�IV�>����W�:�q���o}k1�>��yM����a�0��Cu���7�y�;��~����a��8�Pw=�z������E�d��R$(��GA��*���}��}�OT�����b,�w���O"�Q�xG��/H��� e�|��3ew�����On��"��7���,q�%��<���X�'�����.1,O>&������*�x����w0�����y�����|��,z�Q�����kh�&5��~��#�B�	�w�rG,`\qA�o��o��{�c�����Zmw�=�C�w������W��N��S���G��W\�~�w~'}��}[1�e ��4I>b��!��IH?����M$�t�M5,�x<�`W^y�f_�'�'�>�(,J,$.N9p�������(���ws�)�|�,���3����~�g~&���/-��l�y��G��2%���|�b���Jb!�!��i�t��������0qqQMS�O$�����#�<����w�T��<E��������O~2�s�=�����#`����e���y,��>�������x5�W�����~��6���^[�]=�|D�	@�b3�LkY���������F���<�L\��o|c1`tu���-"���e��\��<�H>�tt����$�F��[o-���;S����f�����|$����xH��q1���v��<�g��#IG��~��x����� �Z�Q��$�V�1�"".�X]>�`1���5��o��cyFI>�t4���#��{���#`V$0T���������.�X�.�G���I�[lf�i-k��#IG����Cb��������AA��������`Qr|`Y���y,O��#IG�4�-63���uX���#`^��!1f[D���LB]�+�c�_�]VA�.p���J>�tLS�b3�LkY���$�V�1�""..g�*\I�����
�v��<��.�H�0}��2�e���$}Q�1�""..g�*\I�����
�v��<���|$�f��;�,�����;�!������ �Z�2�H��7u���-"��r&�����1�/�.��o��cy|���#?�#���nK?��?����[�L�~7@�b3�LkY�0�_}����������0u���-"���R�b���[�������b[�.p���<����	G��&'�X���G)�Xubf���2�e����������@��Cb�����f\H@_���E���fb!���,V,D�X5u���-""H�$�x����v<�@1����F\���~�����z�X�q`���Cb���� 	���v���X�C��X�{RRO+"n���x�z������v,��,�����t�C��X�{R���{�������C�m�b!�6�������mA`�����,��������)"n���x�z�XR�~���-����TK��������;����@��m[�m`[�lG����E�R�5��3��6Ns�!.<j*�/��h��v<��,�|o[��	�a���Y���������-0���}�K���-m������.o�yXeu�D���<W���r�aY-B�faP;?n[��,�~8�������o���m�w^b��Ju��,�'w�}wm=mS���L��F�Rm{Fi_�m���Y\uu`���<�7m������a����tU��Q��i�����q�
�!�Q��}�H�
��m;��M�eR��-�l�<���q^<
��Y���0mc+e��~���D�:{�Ss���6�+�-o���t���c��41�(���j���6����F���������7����n��x5XL�����m�n�m+�I[�:q�)����x�2�s�f�Kl��%��2X<s3��������G�5Q��+?������}��W��jx����m
���`�eQT��h�n�����6����������v�lG���<��~��������Em��4�;����������v:�G��Vw������;��������qq�(�~`zV�o�7���y��,R,D�&C����G��.�#,���+?g���e��>/L[]����o���8p`[|e���r��xH���9&��n����h�����j��v�^u��,T�D^�O�m�����i���h`���,��b���m���Y|���,�����������W���[��l�y\W�a1��nYV�o�7��"T��A�sW��sU��,}�H�
}��G�<pV^�����2�z��G�zN����e��>/�B~�V�������~��1J�#��9����4e�ulz��}�Q����]�o�}�EQ�����e�O��F���#U��w�Y��Q�� ��Q��o����>��5��Uww�>�a��o,��{���!�zl\�~?0�����T[���Fu{��������W���I���VM��&Jm��K�7,�E�����mv��C��4�{�����!��_`	D�W��q�����|}'nU�H�I�f;��f)��A��.�m����<���������0�N��2������>�X�-���&O�hc�mEp0���I9p�@1�u!-��8�mJ<����e�(}^���~����M��	J���*��v$���6�y���{����@_�'��N���f;��f%O�����w������]`�����B7�?~�����/�sq,�E����o�1��"8�<q�z>�&I�A��
�yn��W/�n���e�H}^��I�_���$��O2�j�H�����7���?��ph2�vT
�Btb��M��S����Y��Q�����h{��6�����~`r�m�6��V�!�'����z
�"�Q�'�>}z���oX&����Y���vu�1�=�GL���������s��S�����j��Y78[gQ�F�.���xV�G�*����m�6��VL��Xu�<�&�T�s�aY,Z��%�e�}b�C�I>bj��������hv�`�T��P�#]��!7�d������f;��f�<M"8��m�v`����c!����L��-��$����������;����q����G���xX�t4��
�����y���z\wS���$����ou�y�]txn���m��
1>���A�i���h`V�q��<-j�8�y0�s�q�u}`����L��-�����&�C�'���>�q����b|yQ8,�I%�oX&������z\U�qiO��P�Z]��N������0O�<�Hc����q�C��#�+�^�����
}^VQ^o#�h����H�����������+JY���1.�g������ol,P����PO���!�<��F�sT�Ab\~'�� ���>/�,��z{���C�J� ��s�x������������cf�q�U�������I����j��E��m�v�<�X���n�������~�����<��yXvuO��s�8���b\�����}r����U��>/LK]�Q�CM#��j��G���l,��}^vl�N� q��g�G���`����yq�Z�O�yGyz�(6./�/�r�-����eU�%E�����#���[om,0���D�'�:�T���w�y����!�/���yq�C�w�� qn�F~�r���CPO��U��x���3��#���{�m,��Ld`������?Jv�lG����Z��Q���&y�,�E�����	��<`z�y�Y��#�
0:�h6�>/LR��#�h��	�G�J����?�<���yq��M7�Tm�$
�A��E0���U$��D�]@7�lG����Z��Q�����v.�����L�sDX]����i+�U��q���`5�r�-������N�G�!�I>�%��n����*v�8�vT
,�Em�9o��{����0��wP�N/P�k��~�����k[��<��8L��8����>B�D�Q��<x0����,^u�2��#X��
�8p�@1�e�y��y���}���2I�lG����Z��q����[om,ts�M7C����Q�������~����m���<`��y`�i��*r���sX&M�G��NC��|K$W������nd%s��W7��w���immm�1�Q�t���Uu'z�lG����Z��Q�����Q���m��',�E�����	�<`z�y�Y��#.��"?�����s��f�����M2�(8��'���y�����!��6�N�>�O�~��bh����m�9�vT
,�Em�9o�/?�or�]wC[��;,�e���6��_���y�����Y����q��\���lq���f���i�t�Q���N�X\�}y��Z�m�T?�w�6+���c�N�����A�c��J]���t�h`����M�Y�~n����t��7���[��7���0����,tw��w��
���c]|�����`�>���5���������%�������&��W�������VT?��s�w
j�NWZ����n}_�EZV��e�����}��9B������������Ep�a����1��������������Q&�1d�������`�l4^���1=�7���81(������a����b���o���n���6XT��6jw[~��A��u��}}���W_�X�s���"���~���q0=���q����s�Pw��I-����M���u���n+^m9x�`14>��v�6Jd!�c+Q	XTkk�k���8���w�������vD�}Xy0-D)N��A����M�����W����j��y�;������6jwi���0���o��h�~���s4�N�A�8�/��q|{���N,�E�����}[��q�
�!�S����z.��B���@���%�9�M7�tN���.t��"��o�9��r�a�����������(��{��s��Cb���� 	,���� :��"�v��������m'h���6�������,j����I�.q�r���;�������b�����c���/����LW���J��f��">�<d6$�j��zu���O��'�(8�������i%�2��>�c�Yu����/�D�D"N(�A&���*M�"��8a������k{�7�vT
,�Em�9o�\$5F���?������|lQ��@�Ms����r���y��tic�yX%q����!���f�'@=��7�>/,����J�m�[���#�=�������%9>�Ge;w���m��h����j��Y��2����6jw��;����n���*�Zw��������vXB�9�"�����}v����p�j+�}�y�t,R,D��C����F����}�Q��
�fQ��0I�>%�I�>��C��!1f��$a����bh�Gy����`Qr|`Y�����d�b!�6�������mA&�����1�/�.��o���X%��VM]<$�l���0	u���A�wX}�h�E,�:|��bh�7�����r[�X��
�j��!1f[DD��I��p%u����*��E#.b����
�X�q`���Cb���� 	��B
XL�\sM1��#�<R�b��E#.bY��zk1��=��S����+"n���xH��$a\H@_����,��PO+"n���x�z��������OS��$��6����x2
����"$r�y�C��<���w,�=)�z����v��sO1��)"n���xH��$�Y�.q�r��K~O8k�b!�6�������mA�����C����o,�`�����,�)hG�@I>`���E���e���F\���E������.c�EDI��G}������.����;X�X}�h�E,�*Y�X��
�j��!1f[DD��I��p%u����*��E#.bV�"�B�m�USY/��#������Z���l��:�e�`���������;�,�����;�!���H�q`���C<������{7+g]t1����Z��(>�`1�v�\sM�g�4y��Gk��r��7Smg���n�-���M���~W����y����w5�#�G���o{c�Y$����~�����M�����{��I8ey��'���M�3q��W�w��]g
�������,�G�D$�q�g
������-�`1H>jI>j�m��[�g�>�m��X[��������L�"�B�m�US��#���#���#���#���#���#���#���#���#XP��rKZ[[�,�~������{�tQ�C=�m}���������$Ms���U���X����%�E���������Uu�Q`Y�b���3����4�y@��`A����PJ���/��;p�@1���7�\-���p�
���n+������n*^1	��V@*`����Z��J�������T����������f\e���E����\?3y�<p��#X@y�F��C5���c���_���6/�&u0��U��D<������*a���E���I��%�����,z'5.�iZ�X���{
��wA�4A�h��*����*��U����3��J���`,{���3�%��I>�T}� �^���wC�������������;S���Xm�W��P��J�E�����#�P�N&m=�/}���C[���n��� V�L�4�
��Z��J���+��?�B�j�c�>U����g\?����Ls����#Xp�.Jh{��U$���y�K�,��Q���r��e%���yfK�,���X�<
z�����jWX�<�%�z(�����V[�wc���i�Ru�
74�7)����r�f�~W��?��C�����������[�u��(m�9Hu^��U�:U���v]��7�?J����?_���r��r�����/������~�.������v���U�%J��M�c��u��V�|�yw]�U������q���e�.}�|�s����|��_]���~���2L���6�wD�������%���Q��:.W}������uU��\��Q������k���e&�GU�]������7�*��:�r������~]Y\?���A������\���\��q��U���A������v������yT?��;�����n������9�`�����m��I�I������k�������}fP9x�`���T��b�m�)�������m�'�L�m��Zr�����uu"���I��R�.�o�v��~�m����u����y����"k�W�LR���,��>f�����c�>sL��(��(m��n]�T��Sr����X}oX�
{�N�k�L�J�xG�����l�����C�q�FS�&��"-+,��}�Q�$��v�[���}fPi�OoR�W��{L���G�}�~O��0h=��c����?����_g�u���f�o���l����u�Z��A�����|�%� wS��Qf���+}��D,W����:�41m�u)�WW]���7h=n���bh���.v�-��~]�^U|&���R���
�>f���]��1]�u���|O������&7�|s1��f]���m�.u����X%����v�[��������������g\?�p����B:�V�0/����r8�Su��wC�U;uq!D�I~�;�Y���c�_��UL�_}�jK�1��g��e������Et���;}gS��n]��l��4�}��W��._�������}����[^��z�_�<�Z����u���y��UK���J����{F���u��A��ks�����"[��J��u�>fh����X_?��>y�9�)_�I����W|.L������.����u��������L+f��V]��;�����uF���m5j`�)"n��(q���k���v�L�U���3[��A�k:��Q����F'���:���7:T�����(��Qb�}W�g��&^OZu��2h}��K�'�v�o����e$�6����*��0m��w�N�I�o1���4}�A�S��5�{���!��N_��[����eT���� �cr�>uW��(KS�1�rt�g���'mP��i�FY�����6u�6�;����3u��n���-[u�(�t�>�6��e����-��}1���3M��-Oe`r�68J_-��������-C�>,�0��KV�#J,�����L�A��L�[��Z���R����g��yY���2�w��?;L���L�N?���4}�A�S��5���;��C�W��,���m]�����;9TU���q'�����;R���3�F��xuV��}0l}��%��S'�{l9�A�������n�������<�b�c���r��T�w������n�^�:�Y�����?������*����9�N��2h}������+�C��>y���;�sl���X����0�����4��i���5�nU]� ������>M�����s���\?0y��`�4=�6W����I��`���7�E��f}��%����lt���7����/�y��a����@gU���wL�qYb}���G_�*!�v����{�vaJ����%~��u�/8����14��q�E5���a�m�vY~��bhK�xL�+�*]bv���gf�m�=_�����g\?0��`A��aw�,���u�_��w@��[�>�m����]���K�I��;�o8���O�Y����^��v9"XT������S\�����?������uY���T��K\�R�VB�'�����.v���:�F����q���3q�������5��e}\?3_���mC��,>�G� �w���m��2�|��t��Nn~w�y��>m;�1�ytp���o���.�r�K�i���#�w��S``Q�)���;�^������������������H��q���`�uq��U]��n9n����Ob`q�)���V�,�J��q��|��.�~`�H>���n��?�O���w�&uF����i�b;G��s��jw�C���g]��q�]��Gemm�L %_o��K\%��2����o�I]T�U"�z�����Y����K[��J��$����������q�����.�~`�H>�Q���m�mR���I�a��w��6��I����Q�s�q'�n��b��@\UH��)���J�Tr���o\��gw���q�jl%���l�i\<�'�V���Mb]����H,���y\?3��gf��3����G�C�)��Q�TE'*�,��v\�����T�j�$��.�`1���P��`J�#����U�[���O��q���v���VGE=)��H�%���\?C�����#���mtt��6�z'c�#(�O�nD	e E�eI"���r����j�;����&!�2)�0}���
�����#`.&���Ew���� $�s��@":�Q�>&����.�A��e�F`u���_���A�q�jl���D"
����X&!E(����b=$ 0k�����3�����|=���3����T�`�%���i�2�X��e��e��;�3[w����A�� ":�Q�	[��X��P��6��aA:�e���J��g�.0����q��������U���[��X*�D�d��/�EP���g���3���3����G�C��R����W���j�=)u��e�F�S����-�|w�|]runD'8~A��Z���S�.�q�`�-Z\eX\�N]�oQcm���qAAy
��*u�����E*�Q���hq�U�fa�a}j����*��j������|&�[E�,�a���?�]Q�|����|]��T~��������U���]u�N�Q��a7.���y�UB�����K������1_��N�y��`�-O�i�Lu1~�2����V�.�KMw���\?3}��Y��������|=�w��:��j'p�w���A�k�>:��.�L[�����Rw�q�rfu1�"X����p�qf��	�U���J]���?�G� �7�)y"�c���[�A��.�����e�Lu1~�2���G��w�XU}���~�,�����g��~`�$���1���Uu������`@5�&p��6�S�.��~{1����Eb�]9�hY�b]��.pV��t��/q��/K�#z���]w�U�5���������e�y_~��t�~-K]�/$�r���O_�<�r�� ��Y�R]?�o�������w�;��+���1#:��i������}|�m�>M�������8�}5�a�?��I����%�*r�{g��2��|=b��m��z��`$���1���������][[�v!@�'������i��7�K�E)MN���v�i��O���*aZ�^������M|(���'��(
���~f>��iR�.����y|c��	���2�E�����F9�5x����Fs��_��U�#m�U&tD�i������8_�v#�4���z��m�QU�q�6�2h���P*;��;��I��]��t=���M����{�~_2��������tY�i��q�a����K�6
���������sy�v����lC��<�,���A}�y�U����m��K�zMq�R�u�/�;W
u���~U�w���W��}����Y��q�a��M��)>Tn�6u��o�����}��,���2������5a��Eu�=Um�e�6o�#����bU��k�XD�f,f��.M�.�S]?�X���u=�������:3]�J�{����j1�f��s>�'�:�(�\��A���u;�����I�����;/�o��?H>m��m�|�a�Y���]~��u//�������y�����h{l��)����2N?��r�����>s�>����8�)��m���tM���%_�.��u����L�.���"�O��S��W-m�)&��-��EZVXfm�����,�z��I��FB�8������5^�f{����su����tM���v�g�uq�z������K�W���o����[D[�;8T��0mq���[����Nn���e�b���S�K�]T�b����X��\,O���*�F2��~��b��A���n*],C],�^���o���W�~\������A_u���������.��r�:���_���i�U�9��,u1����]|���>��\?3]b2�.m��1����X����"�y��.�u������X6��`At�L����Rt��������e���tm�
�V�S,s��G]���i���ol�����b���i��lZ��q��(1�:���q-C]�u��*�����}=���o�d�q�0N�����m���e]�����q�m�M$Q'���w'��9��2������������5q����u*��U���8��e�~&Ls�a�b�C,W�u�j�`��m�x�9�XkkqJt�s!X~��?���o��V�y8�yJ����0�"�B�m�U���T��#���#���#���#���#���#���#���#���F9�5x����F,�����k����-�4-R,D�X5u�O>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>jI>b%����Nkkk�J����tW�?�-��r�����Cs[]y����w���i+�o�U'~��:��k��i���g$oO����Qb�{�[�mRm_�L���s{�u;���F�m��q5�s��09�m�09���w��'��}}���[�R��y�/}�����l_�:0m}m���y�e��1`\e��q���Nb��h����%�����LO�}���:��V��,]���m5�+�)QW��<S��>�g]����1�B�+�����h���NS�hX4�������
7�������t�G��Q�������wV�����2TU�~������Ow�}��p|���i��Z��m;����h�Dn������/F9����6��~#P,�q����wEl'}D��>�0�>@������Q�s������`��Y���9�r��2�Y�u�|��gu�� �����=��������u�u5,���e�,���D\WCI�+��D���~�Z^q"P�X����a�'���qr'n��]v������O��.�=��tm����/�R�����!��2�7/���O�>����Q�)q�	��y���b��������GdU�0i��D;����sm��>��L��I��t^q�aF�y���H�f0�(�*�&m����Km�W1]��6u_��Y���6�8
b���Z�\WC)��xzk����������Zd�����,^u�:���O��v���^�!W�'�$���n*^
W�F�<�l�Y��c������|^P
 Ls[W�g�mi]�$?F�It9��������,�>&+oO�;��A��^�y������zlX��-���������3��}�|�K]�C����V����6����4������=?NW-�>T�n}:nA}��������W��?�?�����u����{��8�N~.!n>�|?]��m��������l�j]Y�c�$T��R_��EZVU]=/�Z���sK����������h�&o^������<>�;��aZ��$�I[b����y[���0u1��e;T����`�mU[���f�@����e�Q,��f����_���e�o�Xg��j}e���T�e������Q����eV�������s��g~����m������	r��K��8�,��R�����v�\D�o>�m=��E�����:�,��6.�<�my`��yYFm����(���N�v�N`r���0�Y|G��85���m�
}����Q�H^���r?*U����eV�������v����,c����_Eu;���iYaTu��,��e�cJ^�Eu��tL�&�����Z�@�`\���������-��r�Q�����=�ZG�ze��Qi��������?Q��Ok[��;F����,���;��������?X9�E14�#��}�4�?��]mB]�4tQ���,��?�y��O�e�.� '���c#�w�������[�1FU�?����n;Mb�F��TU��3��LK���\R[�E�i�W ��X=N��o�w�{�8KhS���6qs�a^q�a&U����E'n4�?`\W�Sg�S&�k}1��H>b%�'��h�'��?���#
�(���<7m��a����Y��_=�c���I����?��hU�<~�����|E�9��E����rK1�?����?�U����>��C�I�W ��d�,�g	m��>�&n�$�3n>H���!n,q��������:�6��� �vV�+o��S��#VV�`��@����q�Ww2�����t�M��l�	ry�<������K���-h��m��P��V����/��Xy{������Y�o��������8�/c���n}�M��I�g��I�E���$��.n,:q�������1u&�u5#���U=a��pTO�'�]Ep&���\[[;���(yC>��y�{��YN_=������i�mU?[�����]�	�P��A��)([��U�>U��������m����|��5�4�u�yv�UU=a��������:�p)�/�����{������|~��y����-(��(���r�q��B�r�Y�a�yOj����4�u�yvU�-�����E�g���z�X��?�P3j������,�1@��f�p��s�i?������oX{�4�xo�<��r���}�a��v����
��|�]�	�����������&���g�;�eg�]����8>���E�_���������M���T��OvY��g�]UM���<�2vQ]�&����C�7��M�<��r�*qnP��q��i��W�LvY��g�]U����Eq]
�,������_����i����w�:�<��gL�I���p]M?��x�S`�l4�����n����i��~.��:n������r���sP�h4�Y7}^��M�,G����������6�cyU���::L^����Au���-[��f���?�eP;�/K����v���u�k�6�Z���]�g�.�.�+K��
�]�������~v��`y��c�6"���gX���oS���n��[���g�g���N��i�3��u����vY�Z�\�e�����&����m���?;H�2uY�&]�cUU�O�S����Z��c���U�s�����0m�O�R~� �fw�o�1&�.M���e9��W���cU����+��������
�W��dS�[����2lY�������:m�3���T���.�`�e�2hYsm�5������.���E�n+��8�yu��\�K�^ti#r�~=�~�i��n��z�T-K��vY��u��$������m�A�,k9��2t��a�����,]T?7�w
M��eY����2l9VQu����iYaT��]�{u��6��+�UmW��+�~Xi�Vw9�Fij����T��&}���w��:�ji3?����C^���{)�~X�[�q���e�kjB�,u����mU���u�k�6�Z���]�g�.�.�+K��
�]�������~v��`y��c�6"���gX���oS���n��[���g�g���N��i�3��u����vY�Z�\�e�����&����m���?;H�2uY�&]�cUU�O�l	�&oZ$A�,
IDAT �F��X�=��
V��A�h>m�2�qls��+u���./M�k��v���L]i���&U���2���^�|y;�T���Q���m�/K�u�V�Q�u�|C�e�i����$U�kX;�o�6����#�v��97�eay�Z�s�|��Q�����W7M^��~��4m��4]���}eY��]�I��:����N�f�������������:�6����n���S����Z���u�6�z�*���x6�(��(��	���-u���[M�n{�����}�����7d9�ue���������R���/_�4y����4m��4]�;hK�,��������T����{���m�a���6��I������q���R�]�v�:�(����D��/���JS���M�����e������Q�u�|C�e�i����$U�kX;�o�6���
2h�Il�.��������h��FU��e�������6�l�����O��j�G9�F��g��7���5�r�2��m�'J����U����a���2����q��0��W'_���8l_uY���]�5��N��T���x�$��m�5��G���-sn�����n�������1e���n��j;���Mi�F�i���M�6���i'����3u�yS���6,U?7l5M�o�Q�Cum~�US�>��}$,����kC��)��kj����i����������|�]�b���u�Y��;J�����2h���Q������VO^GF���A���|�����nv���i��G�����s���MW��u��i��v�����������-G���2l�]��p�|������.�,|o����+�O�&U/��4�|_��P7}������?�z���R�,u����)K�<��-�k�vY��;h[�{���]m�[�A���:�A���o�6�u�����pU��R�G���W�����i���j�T����������t���{���r���o>m�y��t1�Ab>��������-o��e�R�o�n�������T�1h>uu>��n����������-K�tu�i�R7�|y����]�����^>}|W���e�����w��(����|��.L��7e[T?��0�z��������x]�F���T�i�1��g����R7]���-k��y��!J�.���7^R�m�7���w�o�a���/w���Z�|�����|��9��UW�6e��EZVU�~W�����mq�3�j�Z�����u�mj��Fijk���O�u��:]�s��Ou��3���tu���n��T�[�T���:�$�s��SW���b���:M��������-o��Fi�g��4����|�� u��vy���6&�.�r7���(���/�������I���<�'����2�M������������-K�tu�i�R7�|y����]�����^>}|W���e�����w��(����|�-u�1\�e>�T��������l�����T��i,���������
m���M����0m�m�\�
�;�L�I>��&����z��?���a��4��:M��V��2h������(��#��N���tM���e��tQm�|�Q��c����ouY���N��eU�sy�2���~�������R/���&��m�/�L����O?�h3}���Z�x�:m�2�M�e�Q�t�u��2hY����f�����a��Gi2lyFY�:���y�������2O}[��j���Ky�;Lu��|��^�|�6�M������f�R�y��t��7������1�:]�Q�O,����RO�z�I>m���?S��z���w�f���e��^u��e����|�4����e��t����|�����T���V�]�2�7$>[���~uU��6���m�/�L������ou�(���a������#��N���t���.�W��2h;���2l�����|��e��w�m�-�(������z�j���,}�H�
����j�����T������6�'o7����g���6����3T����6�m������8���ZO��c�|���T7g�O��ou�(���a���w��#����!��i��L_�.�����7��u��:����.K�a��i����~./]�S���a������Q�E���$��������u=����m���_�A��U�mZ�����7J�.��n�Q-K����7_�x=Lu�(M�-�(�['�y9�A�UU�q�l	�&o���L��#o�J�����4u-{��~���������y�6�]�N7l�R�M��u�(m��j���.'Q��mA�4��o���6u~�z��i������GU>�(��geYC�����6�����]�6F�o�}��C��m��U��<�l�&�y��VO�z]�������n�u�ml��]��:m����}��^�6��������������k3M.�L�6��F���T���u����7���[�6����7��n�>l��-LC�~W����^�Wmo����i3M��g��	������47Bu�u�]��a��U1]�su��y�z����<&��?[���#o;����Y���Q����!��r����Y�a�����Y�|[��m������fy������������!�����|r�zC���6�f)�SS]m�O�V�3M��f�R�/�Y�a�t>�(��i'FY��o��v��4��3m���Q��o�a��/��tM��f�6�yD�����m��W���0�j���{���K[\m�����i3M��g����x�F9�(����|����/��i�b��������f5�ZW�����,M����~�i�LSe������e�a��,k��u][�f�\������(�����u���������g�m��:��:���Z�K1]���4���n���m���zT�m3}�OW���f�������Y�|[��sm������fy���������7�F�tu��f�6���\�mS��b�[�2��}�+�W|>}���c��k_x~����*��UWc�����N��v[�*��F&]�����{��u��'�����~����
7��9��U�r�-�����s�a�6����<�F��������q
�nUm��Nu�C��_[����y�Q�>��t��=���>U�aZv<����nM���M�����2�A�i�����k��v�����a�n3�:�1�p�M7�����~�}z�r���m��.�������8Luy�n��m�(���������[��F]�I��g?�>��O��~������K/M�]w]z��^T�i��'�V���	��L�\��9Q��/����u�vy����|Gu�
m��a�=���amz�mV����������1�����:W��T���f��0���*��*������Ydb3��%6�/V��r.\�O�����N�/7������V���U��� ?�t9o�������������Q�C�����(u~�����N����g�;��3_��mb��t�u�����g��%63;]b3!oO�5�^E;#n~�a�=���i��l�R>�.��0��i������s>��:]�m�������mJm��Y[�e�;q���9�m�?�A��j���Q�S�I�fv\W3�k��/�#���t���.O���f=�FY��O����jh������.�r���XG1���m��a��i{�r�j���y6����u;��;��|�
��_&��}%����� ��|��nn�N��� ��������h�D�:��)��aV~��bhK�m'Uq@$$��g~�6����;���
;�����y�	��UO����rGvB<J{Wg�e��C[�v#oW�L���5���J�z�������\���,Q����������R�]����E�$�U�d6����mn��������*����������(���m��A�?���i���mu���k{�V�<m�u�m_!��s|��>������7�e1����Tl&�r.<�11���|a���(����V����������y�g�����/}�&qqJ��e��`�;�������x����ffh^�� n~�����u���m��.mY����L:n�_44�u.U�Y�����mfK�����t�����j��?�?]cP����u5t�����������s|��>�����Y�����	���/����+�����4o��y�����h5�$?�D�����1�|�'m��`��pO���V������v�� :��>�.��<�{\��=�����t�>��V��o�qN����$��<��M�w���m3,�U���������a����y������!fiV�=���M�3�V�6�9��b������k���i��U��A��\����Q�'T�o�W�:��i����i&m���WOlf&��\x�����B��\]���)c3M�4	��'���3�c���|�j��l��]����>�C��|>�]��j�?��OC���b3�O��o�v[j��]����m(n^7�&u6M3i����"��q������������j�������M_�M��3^��]WCW���YS����6��gY�y����^���c������	���0{��+N�@����6��:�.�e�)Q�`ePv~UL��X��G4������|�����L��d���e>F���O�����IJt�|���������/6�/� ?y��Qm��U�v��l��J��1��D�u�*�'?��b��vwa�F��ump]�v�l��]��7�s�e�J�p���Wuu������#����e"63����r.<�����rM�26%��5�s�U�s����(�=?�o*���������������T������1JlF��_V�nT�����Q��L���.f7�>��q�-�6�#n3��Y|��L�0*����(1��6�����F��V��a%���p]�j�|4A�^u~1�,]����{�)�i�z��m_'�_\b���<E�.�A�;��I�(nw����T1K�n�����qNRb?*O������t�	��m����I�$U���O��uxRb�����@M�\v�e��t���C�E;���u%��8��4����~O]r�*�d0w�NV
�+�����BU>O`r�f�c����\x���4�rq���3l����<%�����p�3�1�v�XM���/��U]���|��	y�T�i����g������G��f�
�#n3�6[�mfG�f>\WSO�������.�O���&���W�1�-b���Y$��&��o��b�~�;'����<s1:AU��c>eGj�h\#�Q��6��62��3��q����f�}7�Z1K�nx{1�/���~4��;��r�v��0y�+?U�Q��O�l{�+^Q1K�]w]1��>�buu�+�����B��A%�/���,K���Yb3���	m�.���������Q\4��<#�[������G��>�2n`\���L��_��b�Y�cl&��3
��������A�Z�c���_�����������-q����v�?`���k��is]�|���URmoG����
�8L]����x]}�9��H>��7����3��M��!w����}�������9���]����(�<T}�('����T��N�c]'y����rF)}�V���oJ�����S���v��m?���������mE���>]����}z��3[�n�Q� �~�����E��nw=�;]L��7y��^�^����|�W��;������7�:�������y7N;�m�
aVm�(fq��y���26��WO&6��\x�����r���l�'��t�Pw3�i���@|f���g�T��}�����>����Y�^�����P�c3�����9��y���}%n3[����K[<��0��M��w?�8���l�9n�0��f]�I�������jt��g���v�kt�,��}�+�E�i�S`�l������sU�������>���T?[��a�@��Um��E|>��:�x]'_�����7i�y���,�j]i���R��(m�f��������t�P�������/���5����m-U?;�:3����~������X��7���o�e���W����V�]Q�|_^�c>�.�1���:��e���[����g�������g���mT��uyJ�yT���U�i&Q�n����;���m�2O}[��j�nj���n�:���m�m����m����h�������W�iZ�i��M�������e�w�������/]�c��CUu�u�X'_�|�����:����I��m~�6����<��<��A��\�Wn�����Vm�K^���[�N��?�U��(m�`^��T����b>���U]�|��������}�~vRuf��)5�M���i�-]�K�D���v���Y�e�QU�wS;��-n�l����~��o�lk�Uw�h:vU�iZ�i�C��u�u���NJ���Lr���?h=������U�-_�i���6�iu���T�gyJM�A��\|g�O��tY�����]��+Lbh+����m�/��1�\����~P�g�2���-_�i����viGB��m~�6����<��<��A����4�(m�K^���UQ�6e��#��h0�����y�����c
o���V��3>���q�����Y��`qw����e�WMr'<�!/����g��wW��nN���,O)�Gy,�.o��u��o]�!�V6?��/�Z��3e�e��65�����{���y4����s��CP������}������z��S_.�7���I������}���3�ww]�|z}��y�����{O�8�Sj�W���U��<�2�T�|�������6m��
��E��^����uY�|Z���(�����Fu9�Mk�G]���<��8_���.U�.mJ������O��#����N,����bh|�j��a�NL���V~�����K���Q[�VS_:�ya���.��i
ry��eYC�MA�q�=������T�������R7JM'�����������<��|Vm�4��F����V�T]��7�������.%�|�����M�����;n����L�/����������8}�|^��s���<i���C�[��P�k�(�c�`�&y�5q�-�7��v-y�[}oT������@q�v�y�������f[1����Pg��Qr]
�*����������e����.�u5�#�*� H4���Y7*q�isb��
u��[�8�m��by�e�r�fyB�'���'E�ib�����/k��Q]�X���N>�6m�4~�0N{Z��h+��vP��n�l��nD����T?���!u���Vu�Em�u�]���L�.�w���4~��$��X�j8�+L�F��6��7�65�����<�1�M�!�u�qq������i��`Y��t1n _���k^7����>�H�|z�p������7��j�7�A����o��������wY���W���3?/+��/_����n7� 4���9UH4�lb@�D�3u.�n3v�9���G�w5p����O#�:�k����+���k��y��<����%�J]d�������/_�_�EN9������jn�����'f�I!r�����D7�A=6f.�b�w��|L��Y���������O������-T��*t���{sP������<'���N��9i4ogy�%��I���w�����qg;�Z+�&
��hf��s�k^�y
�c�����3��ybo|����z�p�O?�Y���:����%�g���k����uF���{�3������qp�K�"�:�����{����p�[:�Z+�^�:D.{s�u���n�����{T���m��@����������������5�95���"����c����M��������V<��	��1�t����_��[���D���]�Z/�Dy:��Rw+��#F��x����6��\/bF�Q�.��>�zQFr�n�a��J��3�mm���F����\:�X����S������S�7+�=��.�Q��~�����C���F����a��Y5��x�>����������B���0�;^�wYl4��R��h��D��wk�6�6�(�.n�{�c��=���a�w�^�[��!b��\'�Q�+r��cU�s�x������c����$��V�����3���7w����R�;���(����{��(��^���#v��V������w�n��>��QF���0R�J7N��n+J����x�J7��M����������d�\����]����(�.�Q��zQn�����s�\F9��q#�U9��x]v}2��������_/9f�mm���8^���t"V�1������s������ZoV�{��]�����%r���x���2�yE����j����}��5W~����}��r�Uy|��{�n.��sM'�=J���{������E��;/���Z7�wqC�.��2��7J��8�pD+���=�mm�����1}����\?���\�~Z=�r����r�]��(���L?��.n�Q�+j���U��7���>~�����+�{3��%~���Gc~+�M�6�N��z���nck��r����w9vj��|�F}W�E�e�"f��ur��"��<V�8����}��9����Hr����/����wo��ug��Il4u'���s���;�J_l�sF����[�X+�R���9�v���u�8:V����������_�5��{z4�l�G���3��9�g�����g�%�[�OV��Y+�=r���M-#��tr����:�����\����Gb��3�g�����n��g8����#�������t�|�y|����}�^�:Gw��8S"����;�J_l��.8#������+�T���9�v�����2��O'����N=�o��J�Qf��#�k����r��yU�?G����l9�u;��K��<.^r,�X�����=zMV���^�9�V���e�=={6��f�����3��=����|[��rU�)WX�����������91Jg�|qg�1+�����V�k�3�X�N�c�ceo|������c/���Y=�"�^�p�\�9{6��f�����3��lY��������'������9��������|:���\P�9�V���e�==����?+���Wu�s�<���n�1]������?@�4�=���=��/������zO��v#��	������Q��#���'\E��#�G��_�_ay�8
g�soGs
����^����D�=��*���S����aul���=�_aO������5��y+�����5�w6v�Y�f����s�Q��V��-��s�n��v���19��>�b=���?z�xt�[�#�K�^k��w����q��������;;��9�\�`��g��H_g���m��:r
b��W�����Z��<�{�������9�����`���<������Gs�~����:6�p1vg����@w�u��[��|��H��H�������3����^�W��I�h�.�~�O��_z�~��%��:	��tg�������3������*�w�f��k7����vo����X�q�bwc=^oc�����D�Zwf�����3�Pm;+��L��V����D���7�����yV�[����?�V���{����c#��k��z7��o5��g��D�3��~m9��]>��fF���f�F���oV��5v����������~��_g$�;oc�r�3r|��������!�������Q��s�-1���X���(����E�e?�(��h���e��kw$�F�XY��Y���u��6�k5�x��v5f����r�m�|:�������l'�r���h1j�3�mu[#y�����9�$����O��K�:�x/�����F���cy�u��x�r�(#������-����1S���}0�g���l�#�6����1��gs��������r?���k������z?��\aU�3�^�eO�W���y-F����������{��k�%���1z/g��ch�������mg�>��3�k��D���7�����yV�[����?�V���{����c#��k��z7��o5��g��D�3��~m9��]>��fF���f�F���oV��5v����������~��_g$�;oc�r�3r|��������|��K|��Z�=]�>���_�~��O;x�9�^����������G������O_�|y~���������[��\-�G��z���yOk!�m����Z���u�!�y�?w��k�������O��U$�������?}��b1�����s�>*��c���?�k�����I>��x|��&>���>���)4���~{�	>&��b���?�k����#��Wrn���"_��t�=��mQ7������>��\�us`���Z��s���N���x,(<����q����?�|�����_�>����u��0���\�us`���z��3��#�����G�*��B<�\��I^�~����������sp=������k������N��'�<��x�D���^�����8����8�?���������XS���#�W�`�}��/_����*.�c�'���q���>�$����&�Q^��@�����9�qp=���>?�o�����_~�0>�%����z���8�{Z�n|4�z�o>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z>Z�����?|\��������D�q����-�m|�0��#���#���#�����O���}zIEND�B`�
query-b-create-tables.sqlapplication/octet-stream; name=query-b-create-tables.sqlDownload
query-b-query.sqlapplication/octet-stream; name=query-b-query.sqlDownload
#15David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#14)
Re: [PoC] Reducing planning time when tables have many partitions

On Fri, 26 Aug 2022 at 12:40, Yuya Watari <watari.yuya@gmail.com> wrote:

This performance degradation is due to the heavy processing of the
get_ec***_indexes***() functions. These functions are the core part of
the optimization we are working on in this thread, but they are
relatively heavy when the number of partitions is small.

I noticed that these functions were called repeatedly with the same
arguments. During planning Query B with one partition, the
get_ec_source_indexes_strict() function was called 2087 times with
exactly the same parameters. Such repeated calls occurred many times
in a single query.

How about instead of doing this caching like this, why don't we code
up some iterators that we can loop over to fetch the required EMs.

I'll attempt to type out my thoughts here without actually trying to
see if this works:

typedef struct EquivalenceMemberIterator
{
EquivalenceClass *ec;
Relids relids;
Bitmapset *em_matches;
int position; /* last found index of em_matches or -1 */
bool use_index;
bool with_children;
bool with_norel_members;
} EquivalenceMemberIterator;

We'd then have functions like:

static void
get_ecmember_indexes_iterator(EquivalenceMemberIterator *it,
PlannerInfo *root, EquivalenceClass *ec, Relids relids, bool
with_children, bool with_norel_members)
{
it->ec = ec;
it->relids = relids;
it->position = -1;

it->use_index = (root->simple_rel_array_size > 32); /* or whatever
threshold is best */
it->with_children = with_children;
it->with_norel_members = with_norel_members;

if (it->use_index)
it->em_matches = get_ecmember_indexes(root, ec, relids,
with_children, with_norel_members);
else
it->em_matches = NULL;
}

static EquivalenceMember *
get_next_matching_member(PlannerInfo *root, EquivalenceMemberIterator *it)
{
if (it->use_index)
{
it->position = bms_next_member(it->ec_matches, it->position);
if (it->position >= 0)
return list_nth(root->eq_members, it->position);
return NULL;
}
else
{
int i = it->position;
while ((i = bms_next_member(it->ec->ec_member_indexes, i) >= 0)
{
/* filter out the EMs we don't want here "break" when
we find a match */
}
it->position = i;
if (i >= 0)
return list_nth(root->eq_members, i);
return NULL;
}
}

Then the consuming code will do something like:

EquivalenceMemberIterator iterator;
get_ecmember_indexes_iterator(&iterator, root, ec, relids, true, false);

while ((cur_em = get_next_matching_member(root, &it)) != NULL)
{
// do stuff
}

David

#16Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#15)
Re: [PoC] Reducing planning time when tables have many partitions

Dear David,

On Fri, Aug 26, 2022 at 12:18 PM David Rowley <dgrowleyml@gmail.com> wrote:

How about instead of doing this caching like this, why don't we code
up some iterators that we can loop over to fetch the required EMs.

Thank you very much for your quick reply and for sharing your idea
with code. I also think introducing EquivalenceMemberIterator is one
good alternative solution. I will try to implement and test it.

Thank you again for helping me.

--
Best regards,
Yuya Watari

#17Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#16)
12 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Fri, Aug 26, 2022 at 5:53 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Thank you very much for your quick reply and for sharing your idea
with code. I also think introducing EquivalenceMemberIterator is one
good alternative solution. I will try to implement and test it.

I apologize for my late response. I have implemented several
approaches and tested them.

1. Changes

I will describe how I modified our codes. I tested five versions:

* v1: The first draft patch by David with bug fixes by me. This patch
does not perform any optimizations based on Bitmapset operations.
* v3: The past patch
* v5 (v3 with revert): The v3 with revert of one of our optimizations
* v6 (Iterator): An approach using iterators to enumerate over
EquivalenceMembers. This approach is David's suggestion in the
previous email.
* v7 (Cache): My approach to caching the result of get_ec***indexes***()

Please be noted that there is no direct parent-child relationship
between v6 and v7; they are v5's children, i.e., siblings. I'm sorry
for the confusing versioning.

1.1. Revert one of our optimizations (v5)

As I mentioned in the comment in
v[5|6|7]-0002-Revert-one-of-the-optimizations.patch, I reverted one of
our optimizations. This code tries to find EquivalenceMembers that do
not satisfy the bms_overlap condition. We encounter such members early
in the loop, so the linear search is enough, and our optimization is
too excessive here. As a result of experiments, I found this
optimization was a bottleneck, so I reverted it.

v6 (Iterator) and v7 (Cache) include this revert.

1.2. Iterator (v6)

I have implemented the iterator approach. The code is based on what
David advised, but I customized it a bit. I added the "bool
caller_needs_recheck" argument to get_ecmember_indexes_iterator() and
other similar functions. If this argument is true, the iterator
enumerates all EquivalenceMembers without checking conditions such as
bms_is_subset or bms_overlap.

This change is because callers of these iterators sometimes recheck
desired conditions after calling it. For example, if some caller wants
EquivalenceMembers whose Relids is equal to some value, it calls
get_ecmember_indexes(). However, since the result may have false
positives, the caller has to recheck the result by the bms_equal()
condition. In this case, if the threshold is below and we don't
perform our optimization, checking bms_overlap() in the iterator does
not make sense. We can solve this problem by passing true to the
"caller_needs_recheck" argument to skip redundant checking.

1.3. Cache (v7)

I have improved my caching approach. First, I introduced the on-demand
allocation approach I mentioned in the previous email. ECIndexCache is
allocated not together with RelOptInfo but when using it.

In addition to this, a new version of the patch can handle multiple
EquivalenceClasses. In the previous version, caching was only possible
for one EquivalenceClass. This limitation is to prevent overhead but
reduces caching opportunities. So, I have improved it so that it can
handle all EquivalenceClasses. I made this change on the advice of
Fujita-san. Thank you, Fujita-san.

2. Experimental Results

I conducted experiments to test these methods.

2.1. Query A

Figure 1 illustrates the planning times of Query A. Please see the
previous email for what Query A refers to. The performance of all
methods except master and v1 are almost the same. I cannot observe any
degradation from this figure.

2.2. Query B

Query B joins eight tables. In the previous email, I mentioned that
the v3 patch has significant degradation for this query.

Figure 2 and Table 1 show the results. The three approaches of v5, v6
(Iterator), and v7 (Cache) showed good overall performance. In
particular, v7 (Cache) performed best for the smaller number of
partitions.

Table 1: Planning Time of Query B (ms)
-------------------------------------
n | Master | v1 | v3
-------------------------------------
1 | 55.459 | 57.376 | 58.849
2 | 54.162 | 56.454 | 57.615
4 | 56.491 | 59.742 | 57.108
8 | 62.694 | 67.920 | 59.591
16 | 79.547 | 90.589 | 64.954
32 | 134.623 | 160.452 | 76.626
64 | 368.716 | 439.894 | 107.278
128 | 1374.000 | 1598.748 | 170.909
256 | 5955.762 | 6921.668 | 324.113
-------------------------------------
--------------------------------------------------------
n | v5 (v3 with revert) | v6 (Iterator) | v7 (Cache)
--------------------------------------------------------
1 | 56.268 | 57.520 | 56.703
2 | 55.511 | 55.212 | 54.395
4 | 55.643 | 55.025 | 54.996
8 | 57.770 | 57.519 | 57.114
16 | 63.075 | 63.117 | 63.161
32 | 74.788 | 74.369 | 75.801
64 | 104.027 | 104.787 | 105.450
128 | 169.473 | 169.019 | 174.919
256 | 321.450 | 322.739 | 342.601
--------------------------------------------------------

2.3. Join Order Benchmark

It is essential to test real workloads, so I used the Join Order
Benchmark [1]https://github.com/winkyao/join-order-benchmark. This benchmark contains many complicated queries
joining a lot of tables. I partitioned fact tables by 'id' columns and
measured query planning times.

Figure 3 and Table 2 describe the results. The results showed that all
methods produced some degradations when there were not so many
partitions. However, the degradation of v7 (cache) was relatively
small. It was 0.8% with two partitions, while the other methods'
degradation was at least 1.6%.

Table 2: Speedup of Join Order Benchmark (higher is better)
-----------------------------------------------------------------
n | v3 | v5 (v3 with revert) | v6 (Iterator) | v7 (Cache)
-----------------------------------------------------------------
2 | 95.8% | 97.3% | 97.3% | 97.7%
4 | 96.9% | 98.4% | 98.0% | 99.2%
8 | 102.2% | 102.9% | 98.1% | 103.0%
16 | 107.6% | 109.5% | 110.1% | 109.4%
32 | 123.5% | 125.4% | 125.5% | 125.0%
64 | 165.2% | 165.9% | 164.6% | 165.9%
128 | 308.2% | 309.2% | 312.1% | 311.4%
256 | 770.1% | 772.3% | 776.6% | 773.2%
-----------------------------------------------------------------

2.4. pgbench

Our optimizations must not cause negative impacts on OLTP workloads. I
conducted pgbench, and Figure 4 and Table 3 show its result.

Table 3: The result of pgbench (tps)
------------------------------------------------------------------------
n | Master | v3 | v5 (v3 with revert) | v6 (Iterator) | v7 (Cache)
------------------------------------------------------------------------
1 | 7617 | 7510 | 7484 | 7599 | 7561
2 | 7613 | 7487 | 7503 | 7609 | 7560
4 | 7559 | 7497 | 7453 | 7560 | 7553
8 | 7506 | 7429 | 7405 | 7523 | 7503
16 | 7584 | 7481 | 7466 | 7558 | 7508
32 | 7556 | 7456 | 7448 | 7558 | 7521
64 | 7555 | 7452 | 7435 | 7541 | 7504
128 | 7542 | 7430 | 7442 | 7558 | 7517
------------------------------------------------------------------------
Avg | 7566 | 7468 | 7455 | 7563 | 7528
------------------------------------------------------------------------

This result indicates that v3 and v5 (v3 with revert) had a
significant negative impact on the pgbench workload. Their tps
decreased by 1.3% or more. On the other hand, degradations of v6
(Iterator) and v7 (Cache) are non-existent or negligible.

3. Causes of Degression

We could not avoid degradation with the Join Order Benchmark. The
leading cause of this problem is that Bitmapset operation, especially
bms_next_member(), is relatively slower than simple enumeration over
List.

It is easy to imagine that bms_next_member(), which has complex bit
operations, is a little heavier than List enumerations simply
advancing a pointer. The fact that even the v1, where we don't perform
any optimizations, slowed down supports this notion.

I think preventing this regression is very hard. To do so, we must
have both List and Bitmapset representations of EquivalenceMembers.
However, I don't prefer this solution because it is redundant and
leads to less code maintainability.

Reducing Bitmapset->nwords is another possible solution. I will try
it, but it will likely not solve the significant degradation in
pgbench for v3 and v5. This is because such degradation did not occur
with v6 and v7, with also use Bitmapset.

4. Which Method is The Best?

First of all, it is hard to adopt v3 and v5 (v3 with revert) because
they degrade performance on OLTP workloads. Therefore, v6 (Iterator)
and v7 (Cache) are possible candidates. Of these methods, I prefer v7
(Cache).

Actually, I don't think an approach to introducing thresholds is a
good idea because the best threshold is unclear. If we become
conservative to avoid degradation, we must increase the threshold, but
that takes away the opportunity for optimization. The opposite is
true.

In contrast, v7 (Cache) is an essential solution in terms of reducing
the cost of repeated function calls and does not require the
introduction of a threshold. Besides, it performs better on almost all
workloads, including the Join Order Benchmark. It also has no negative
impacts on OLTP.

In conclusion, I think v7 (Cache) is the most desirable. Of course,
the method may have some problems, but it is worth considering.

[1]: https://github.com/winkyao/join-order-benchmark

--
Best regards,
Yuya Watari

Attachments:

figure-1.pngimage/png; name=figure-1.pngDownload
�PNG


IHDRY�G���sRGB���gAMA���a	pHYs���+��IDATx^��	�]WA/�uso��J[��� �	C�$($O�����S!�>}F0�����| �A4!�4@�<���fjK[�RJ�4s���:Y'=9���{�=�����O���w�}��gZ����yX��I�_j��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� �����:������;v��[��-[����X�ti��%K��]�jU��NR��Q��oko�����E���$d��������W�V����l���K���m�������<������S�n4�����n��3x��0vc}-��.�b���w��$~���	t��\^�xqjU�=�;X.�K�+c�������%N�N?�N?���1���x����x��g�f���g���?`�g�����4t!�P]�l:A�+�k)�������f+������9�9	���,zH����@;O�"*.��NZ53����F�_[�nM5:��@2�����e������-[6���x���h��k��Z}q��'��&d���U��@�id���,Hii7q 5���X������x��\�y��s��P�G����AWQ�"���2��m[	�7o>��m�,Z�(�t���g�ht�u��u��W�6�������.��X>�g�����x�W���@�;��U�����������U�VU|�k�������c�/
b��B�Y�G��G�}Ync��b���������f����Z�I�
t.!�.Q��/J�����:�hW����t
�2dS���5k��Z{kd)������b������C`|j��d��B�f����@���R��>v�g	Z4��9@��7�0bp!�R1�V��M�PW����8-`�j�.\Xx>V����LB].k��l@;�:CW+��,�8{E���f��0���06����U��a#���>�,z@�d��lc�/N��b�b�f���|�����Z�P�+j-�%�@���Y:�MW06��
(���EQ�1#���
���
U�+��Zc&��#d�#����W��j�� b�u�EY��W��B6��+.L�Sj=��n��jt
!�R�s=���)�g�V^��Y�j��]�6��Z�=��U��1�@g��!�:��Y�R,����������b)�,��YY�
��Q����������\�����N��,����z�b)��n|����9������i�����'����6��J�E,��*������,q��V����������V|��z�����x�����y|j=�+=��-�c6�3@.��[7?nU+��mK?9q����K�OVW������)����f-�v�}5K��K��oK<�J�S�4��k���������Y�B�c�r�����X��q�'���c��J�Q���Y�i+U�]�%O���F��Z��f,�H��F����a<��X�qv�s�(��Z�R����>v�����
���x��J�XZylk��<nw���x��J�=��J3�'�jO���^<���P�������\��m��P�����E����a&�R�6p;�W����/^\�����U�q_�����j�������Vw����������o{�:M'<n�c�g,�����������#���z�{.�c���"�x��Q�s�_��J_��m�x_;W��:?j��L�DZWg6W��z����P���I�u9��q�[�b<�E���v�����B��-���sY��,���R�X�@X=��x"����y[����F:����=��4 �	�[�_��1�#�nZ#�'�:G�������O3�N��=�Y�W�����z�����v����6��*�z�}�t��x�g�i�>F��>��0���p�<�Z�\���H�>��Y��}���!]A���;��5�_Oq�,/��5�����s +�;���x_L���xu��������1�`A�9������a<�<�S:-X�����^
l���pm�Z�Q'��������l����m��X��1To�������z��D�w�����#�#����DB����x�8�'��b}^��|��1��>i�Nx�&�1re�[�vm�5W<���y>_�q����F�#�6Q�G���y�+�
h��Rm�>����A��J��<�m(��b��V�!��V'-R�>�����Y�3Y��v����Yb���m�
edddT����d��-��f��[�s;_��h���fwp����V����W�u���u���X��+n�R�x'j �|d}.��X��v���Z��J��w���?���F��,}��/�o������G����yRO�_�RK��/-����
)�d�#	X�s,>'*=W�x�4b<��J�@k��I���}�q���v[*�����Z�I/=�:��_���u�F���je��m�''N��(-YT��bi�6�������t��1�W�w��V�gi�?��J��W���z�M���kD������M��:�F�:�F�J��WZ��E��w#�Wi�z_��J�����Y�r����d97���~&�m��7������7��?������,�K#�����e�j=�Y�����J�YZ�Y��--Y���~�j�c}������n#��,�Gc����R��W,������J����}4S��t,�Q����Y��O��c��L=����7-���K�����th����q��}�X����|��z��mK������(C����z_G�:�E�vz���N�X�^����Egk��(��Tz�l��"�\���X�'���f?W����<�J�zW�G��#��\���G;���,;v�H���e��U�=��n_7�r�_�y�d�\��y?O�1_��%��X��xT:Ok����Z�����
)��C��_��c�O��dl�H��)O��,�Ay
�72�X*v���������U��nEY��NXg���z�/�y�dy��\c=7������7�z������[���Xey��;��To��f���{|�S4O�
`O�z�E����e�\���$����r�/Wo��f}v����:U��Z����u���z��N	&�*!��e��z�/7[��#�y<}��8�4�N�(�n��<�0��[T���
f�Z�=n���f<^���Lc=79���|������$D�z��7PPT����`i#�
�5���w%v�N��<tBpp<�����|<��EQ��zA�<�'�=�z����j�c���85z��t����	Yt�8��e�8H2�N�F���m����W�����n�;_������v�����z��m�j�`+�e���,�_���|i�k|��T3�v��/���B���f���1x��Wc7ze;��g�f~�������j�[�8���<W��7�3Y���Z�X���[����%d��b�"^����n�gz��)������l��v�V2������NTj�����q3�f6h�}.gy7�
�Z��FBd�����\�j�V��C+�d��v<�f�����l'�>��!���Q-����%�U��z�o��Vk"?�fQ����}��!�K���A��	K����������T=�����v��O,�8��y�DN9�������
����z7�|����3Q��f���q���c���������u.�~�:�:=�@w�5�Ka�h��'�:�z���!�X��=�z��X�{/�}@~�,Z$�2��(q_�.�F:icgo+�
����s1�x���E�kf�|�N�fv�7�3���o�d-����8n�w���>��\��\h�PV�_���v�J�l��\�z����;S�d����W�r"f�k�{y����3sO��+��9c���R����'@{��a�LH/����v��n�v{�Z1p�n3y06��8��kfx�\�}�����v�W���n^2��S/��W��~�1�����z�<��l���n!����z�U^?���@��W�����O+��t��x�/4���d���z�nE��K��j��=�\� T��q����`z� �XCe�nk�����!h�bH��y��3�t:!�;����0��b�q���x�:���^lk��.p����Zyt� a�����{y�
�7*��y�2K����y�l��T�]Y���Qg���ys��]J�#���/,^��P&2`��������q�����@�s��M���.��V^�]�y8�)�����1�F�N��h�Z��v
#5�����
��#ji�g�z��Y�!�.;*�4�q����spv"�)J���
�t�V�$����*����}y\���Z�z�E�_+�������*y�(4s��Z��5k��Z��{�j�R!��=~��)L<!�.;��%v��+�i��F��)���A
a
�F�*�~�:o(jV�*�\����{L;������������70��@���;b�%�)�%�*:5XC�a
(��A;�aD�~��y���w���/�Z2d����6Z��z��;�p9@{��-�N���:A����]�A�N�}4W3��u��SH�]�.q�D���-;���V\>%���u���V���v��<� r;-�@��A�;��lu������]g<1�@����b��Y�1HQU�Q��	�<��5���Vh�^0���4�
��k
@{��e��(
S��@�P�=�����C~��z����[���x���nV��<���?��ZB�^0�S�
i�C����!Z"v`f
X���0�@����y����NRk�^<���Qop�]���7 ^���U�{��=����]�����B�=7�a�<K-�9�<h=!&\��2
o1Xav
���������>�iK�d9�^��U�<��,'���ZA�vZ*�^x!��b�;�R�5e����@�Y0��t`��E3;�]�K��w��9d`�N�nk��3��|M��!1$�\kx#������������e��Z�B��z��&j��z�)^�ZO��	W�s��zO�M����I���v[���
��f�f�����o___C������T�D<��r�����q�u�u�R!��$K����)4N��	���<�LW|�Ij]E\o`�t��i�
�������<�����{a��z���z��t_�qv�Z���;�x;�=�Z���y1�F��\Y�fM��^���f���U��� ���������%����`i���z:M����IQ|���6����=�����s�]��o���7�Zu�BY���c@���k��M���i��\��S�B?y����^;:I��t�ko��J�z�=?&�u,��!�v�t!&T+b��)���C���F�NzW�������*>��&�8�����%�A�ZZu�~���NR��������r_��)t��[����F�'r��YA�zK1�H*����������<���[|��u���7Y>Mt $���^S��y���,�z�$�u�B+����l3�u����
��un�{����~�
Z��7C���v�Z=OYf���I�������s4>��wb��Y(�#0���Q�
���V/�4����z��<fs�S���x[kVZ�:YI�>6Yf��5�,h+��u�UW�x���W�6C����]���c��^P����y�7h���Q��h��m�������v����������+��E<���a��N�f�h���z��y�U/�2���4C����mm��d%���d	����B��fN���C�U��7�6���=G���'q��Y����z��,���������,�Ac��d��f@���c}
����E3��>�z������*K i�����=��c����Z�7�^/�m��zZ���{����
�	Y0��tj7C�0�r�&��<�<G�Y��mF� �s�������Z�E�6�EQ�d9��E�A�J�c�V�`M-�8��w[�s���^U�~���X�R���{�t�,E�������u{���2.Y�1����`�����!�� e�\���kh (>�<G�&y<OJe}_Y�fM��V��������z�G|���Y,J5���e|<�:P���Y�^�����y%� �X���?�����}V|l�*�n���t�se,���y��v]*�(K �lO��	�gg|��<;�a���,�����q���@r���g�
�@�i�y��@v�@A�
�2P�e�8�_3�*��fY��]'��5h�����H|L�<.���%��X^�����7��n"?�e9O��^�����m���4�qh�Fgz�TH����0��,�pY;����������v26�q����8HW\������#���]e}���B����R.�_|_)>w��7�{�Dj�����x������Y�g��"�8i�F^��x����x����R|<b�r�V����?Y�������>��%�y��%h�y�e��x�q�{����T�oO+4:�C;�V����V/R�%��=&��-��S�� C��^Gv��7�v������kD�o�?t�F�'����R|�)��YH�v~�d�?�m-���E��� ���h�}���Oyi���6cp6��)E��K��R�<�k���A����=WN��!�c?��A�U
��k�@H���%C&��-�H�lQ��B,Y��,�_:&����$��]�a,�x�T�	����?,���C�&��hD���x��DL����n$h���\������e��u��bQ�e�E�YL!Z&�����]�!_�s2khZ�8P�YCH�i�y��Y',��~�����A�<��g"��r���{����8�xi�
*L�{U���|�D��\�r{��_Uo��F�A�[����8������l������w����t��"����s't�����`h�x~�1`���)��x�y������������gU�<�^��[�Y-�,�PI���Y�Y<�&��*�M�������z�Y��&R����z�+�q��#d@�5k���y]�3�V�����4��N�����;n������a���i�92��P'
J�k��?���<`<J?����(U�������������+l��xc�]�>��������������n�-R�%�"401���0��������C���b�w�h,v�����iqP�_��\�l��c���V7�6����6_��gT�����5���E�������x�u�9Vz�Z�#���K���:��]Y d�c�����kk�-��5dQ+�g����<�-BY��8C����S�L^�����/�Fs�m&��a��'��|���q���>�bY���g���@�W������E,q��X�R��|����t���7�qi�80n����^c��64��n��{�R���"�ubx���,:L�g��5�w$`TR�3�Y,�VBf�����W�N����TSk�!M���@�KtTC���b��e�V��P�J�,[�lI�3	i���,����S���k������E���(��
(�f�Z�fM�@��yX��F���R��m���E��V6q`$N�]+`�e�@���b���s@��1��y�����#d���� �/N����"��/*#��H�%\ ������J�4�vBm���y���5�����!�67�A�#@�%���@�����M���H���08���5kR
���@X�jUa��x�h��}�I��h��F���h�����&d�!��E�i"d4cf�b����X?KX�K��<��t�����;v�[�n
[�l)���H]�d�+����/*}���)|���	Yd`���,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��,2��@� !��.#�����t~wB__���LYd d���@}��S�G���������}p�n�;���4G��3Yd d���@BYd d���@BYd d���@BYd d���@BYd d���@BYd d���@BYd d���@BYd dA����C__��JV��o��w�-[V�>��o��J�'b�/�����>bdd�&�\�r>VY���	�z����n��ua��U��M���ST���
}�z��N�"����VK�.
�7oN-�P��@���<C���-[��V}�tt��:���^y?�~��	YPQ��qV�F����|���m���E�R��lYf���}t!h�J�Y�O�����a��(?����R���y����4�F���Ew���	Y��R����/=l���vJ�q��R��q{��R��_�S�
��JZ'�.F]�����R:�d3��(O�d9������;��o�N��3>t���{���Dy��PY��3YpF8`��%�6>q*�R���K����~�Y!:u�Y�>��>�
�� d���C.L��)_�$�~W�Z�j�u���*]&�|�RZG��	�h��Tk���[S��N�7@%q����,�X�'h>!�W>sC��g���]��eJK���u����k�V����'��X&�}	YpZ	� E��r���i�#���=.��-J;�>(�OQ����x�Ki��eB���E�+���Y���J��`|��]�j�.��L@��`�u����9]����b #��!T/f)^��`,���,zX���\W�Z�j��q[�r��*-+2^K�,I����}�+����.�f��T��Y����WFg���z�P��E���
������F-���.#�����C@-}}��yD�9S���Nq� )�!�[������B�Nt�
��;`�
}�z��,�,��K���������J}f�`L��R����[��Z�u�����[o
��sOx����������tBh��"�e����yE^�9"�!�d��%�v��;R�vu�w����������h�x�J���q��n�/�^V��g/�y9z���E�3qT�I
�z�����V�J��M_Y��Q�����n��y388X���]
u�n�o�����s��	�&���g��=���y�R
�e�h�����j��C�E���������B}�������P��p^���~�5k��A��yE�5�f�r�p��T�4x�E���/KR�S�
@���q���X��|9������~:u�t6!��6Y�xq���.Q>�e���T��!Q�s,*�����(�}��mk����h��_�-���,zX���,�Q>k��u�R���[��Zmk��M�S���t�����-�����C����/i�u6��@A�YJ�1�QoY����a�Z��N�7�K����/������K�
�jWK��?.KR-����/Uk��N�7�K������YZ�_�>m9%�	����XDk��I���gu����}���vyP!�^��.;u�t�������#FFFm��������S+���((����8;����S��N�w+�`���`�����B�����/�s��	�&���g��=���y�R
�C������[o�������!"}�a���ahh�P�={v���/�a<�W������g���}��p^�}M�U�;0�@ay���_���?;��������QM��F::u�t!
b`�*�!�J���1�1�0Ar�`Fq�����?��N�7��r!�#�m��JS����x���B�^`
GZ�r!�XX.�<X��<8���e����<�k��r!c$d���@BYd d���@BYd d=���/��J� !��,�G����c!d���@BY@I5���@� !��,�G����c!dP����{��	7nL[�,Fy������/_FFF.�!�q��(%pDB%����T��4p!l�E������S��+V���,J�/R��M�R
�Bcp�����+�,�`��
��
!�{��M5�3	Y���{w��I����+R��={���+�,��\�2���;wn����S�Be6l��)hq�5���7����,*��X�|y)�-�l����2r���8`t��������B}��Ea``�P�T}}��kb�����<HV{��M�Sv���j��b��T;��t�}������B}��9a�$yR����&E���K5�W�������[o��n���=���?

��g�����:����<8p�����Y����+�����J}B�#�,�+}3)j$d��f�n��t3!���K+Yc!dA���y0N�W�A_Sm���<����� d���@BYd d���@B�=\FNU122j���o��E�����B�S�����|���'����y�Rm������������9s��I��I��S�Y��
 �J����se��.h��A{��*�g�����u�y8p�����Y�fy�)�W�A_Sm���,�GYP�5�n&d�_|i!`,�,���p���"�����<�k��R����pb�u��{_~�g�	��m�1B].*�������	a�d�{o�-h���rG?��T;��/�s�YY�Y\o�X���Y�_����m�d����Y�lYa�x��o���[����%��4x�#kn�	Y�I�����S-1��x���g��-��11�@D����pr��Tm�1�����u��b����G�G��F�"y�&��p8~���m�S^�j@B�q&��*_Vd��u
�Z*�?�m�FFFN��}���2�D����\}���0����F�N���^)(��{����>" Cgg�4�E,Z�(��+id��,�2����RP��9���[%���`�o���@�����V������P�?~�;wn�^��1o��1�X�"���5�n�o��0<<\���3'L�$O
t�={��Z���K��������3��E�g?'����ZP[�������[o��n���=���?

��g�����:����<8p�����Y����+��H_S/��w`���������cG�����E����q{��R�f��s�t������{�5�����J��E-���~J���k��{�,�)�b����x�����S��2c�u��Ta����6~���o��/����_*�}��b��4X1^�����F�wo�����Sm���)�
�1����k��������S�1�3a,Y�$��'@Je
����J3N��o:[C��&b�"o1��Vi�Q�o���|����������@1ghX�jU������X���� ���,�#�}��b�!�+��H=W]uU��)���7-���cg�����,06B��|��5k��Z����G5�K�T������E�Zcq��,��?.L���4B��3,^�8�N-�1��AT>+D�Wq��2����(��-����xqf�Z��w;��9��:`Q��7�!��9��z�b�Y,`��,8-���LH%qq�1�Q:SFQ\�$n�������L��J�S����<��?~�U���gOj�v�;�
#��O���\�9�`h7B�&J��X&�4L��5x�4���n����������.}F����%dA!`Q�L��m���LH-q	�������%n+�5"2�.B6��&��L�XC
{��M�l�;f��>��p���Sk���
�q� �]�6�N-����En�����I������7��*-+2^K�,I���s�L����*����iSX�rea��X�����'�FBf��9q��T�l�!!�g�(]�#��!)b`�8CE��1b��|F���
��/O��b8"�+V�X6l���6.�2b8#k�b����0��]��
��`i��vvjc��p���ezC#��L��"q��T��X��1��H0$�JJg���}�Z|����
�K.�d���i�1"��T�5�\�Z��@D��_���w���@��Vg��������������s�T{���-���|sj����N<�Y���.�����G=*��H��W��;�Xt���v�����400P��x����t����C����"8�>8k�,��4���<���5���*�������&�Ry�&U>�E��G�j�����|y���b�a���dY*��k�M���o\�f��?~��]��SQ��������(�PJ�n�r�����g<*�3���N-�JB=�|��TG�X�dI���c��T���1��$��<H=�w�N5��L�������.L5`<���.ruP�k���\>�|��8�Fi$D�l9��t�Kz��nT~����V+].$>V�>���I�d��=n+W���������z�0�����p�A���{��8�g^��T:�Uq���7o�>��
�J�����K�?#V�5�{�U����;�Xt���va��`Y���"�u �+�`���*�y��-\X����PC�?_�,I�<��j����K��9��D�t*J��R��_Q�J�����vx�a���r�>:�@�	Y��x�B,�V��HV��Ri?y��0r�P8qc���.8s�@*��������8se���J;k`�Y���tD#�|f���/_*$~�_�xq����F�.���t��h��f��s����W_�j����7����gYh��(�c��IDq���=�L�a�J��#d��bp���f�����(��r���F��(�b��u�V���[S���k���)���\���3m��1��q�UW�@6'wV��=e�����.H-��~���*!����"dA.�����QV�6#Di�"�8�������=j�7��7�o����V����S-��/O����[��=N����m��������;GFFN�������3Z��=���H`��EG�+�@B����JK��+_4vTl�\}���(
+�����W;�J�&��?�}�J<����B=s�����f��gO�;wnjU�r���a�����kh�c��d�Yl��)�X�"���k<�l��}axx�P�3gN�4I��>��J��o�l8��L�3�M?7������O�������[o���C��>_��3�����t�x��x{
u��������={v��q��W������g���}��p^�����y���E���<���!P*v\����=��xr�vlD��()U~�F��(�w�ol�w�����Q���io�w�N������p��W�Vs���XD�_}��pr����KM5�@U�{}�������+�+(���;v��)�������;0���1TQ,�A�(��V�*t��*�w�ol��?��<�M{�:KDZ��'�+�+bB0�/�Z3h�e�]7���nO��,P]y����Q�w��[��������?��"dA�bDLd� �?���z�F\N�<�QM��Z����s����{��Zmq����a�Xb`"��������')�K�M����R�OxZjPn���������V�Q:���%KR
�v$dA�bGE1�;/*.���0�X�
1����#����,y���u����Z}1 ����D�)��K���,R�R!�iC'��[���M^�-���b_E�.�!����hV".]R*@h_}�Q�$���h_1244T���N����N�m�S��"�!��1o�����t�}������B}��9a�$yR����)S�>����O[F;��3L:���������>Wv��"���?c���=�3`��E�R���o;��mpp�P��i``�P������>���gw|��yE8p�}p��Y�i
�y�}ME���&���<T�no"���t�i�})�F�2o��@���@i�"jVb��3����/�:bg^iY�lYa;�#dP���+S���ql��!��^7��{����wj�6�
K�4C:��
�3MDq��<��J��<�m����=.�-ZC���lhu�"."`��v��W<���2#L�rYj0�$b�!�l(�!�f/�Q������/2�XB���1�]+��6m��0��[��,����$�G����[Sm�%K�45`Q��u�
a����X���(f��XBu��E��)�*1Qb�c���p�������Z�M]��f�<5{���I��X�j��0G��y��B��T�eE���@FqV�8�D�a����ag����;7��h���4\���`���YSw�f-��q���gY�$�-����!@3�y0�����2j����.��fhh�P�6�z��A�V��qc�?~�;wn�2>1\q���OH��k<�l��}axx�P�3gN�4I��.��a��C�u��/��B�<T���
�+��v��8�����S��,�������x���lpp�P����}>�)J��V��=�W���E�9�����jd8\��������Sm
*=���G��I���B�Q#����&*XQ�k<�L��f'vn�>�������&]pijAsU���
�+��v��8c��-[R+f��Kx�B���2�QMi_�E]��9��snxW�����G��o�-D��|��+VJ\N$�����+�(��W���Y��� ������������D[�dI���c��Th�Y_zo!`A6f��a&������dt���?����z�3W�q����I-h�J���dA�*�A�U���������S+�u���U�V�V{(��x�����}���^w��Y����P��p^��[n�������_�}��p^���������
f�(S����@�;���,}�a������`������b���4������2�R���+�XJ�J��(c)�*���4Z�m��g,�F��M]}����q��B�u�7���THX�M��Zdg��3R�N��o+��F�5��q�kG?����1�4jNCScB��\Hv�^;�o{���Y.�F�w�����5���~w�<�y����9�>Wv�����,�Q~����)����K����Cu��yy��������O/�0{�l�:��+���)��eh���+�����G?���:��B�T����@��iS�Uf6�='v^�j�M:��&�3[dQ>�DF�G����>�)?�u���@u���}��,�F���\���V�UW]�j@�8���U�S�l���j��%�v���[S���k���)k��I��)?��K�����S
����������w?��0VBmd��
�V����S
�'n����Z�M�b|WP��U�V��E�^��|����q	����Q�U�t�c_����~'?������� s��M5������c�.��`��g�(J���r���-
6D�f���SZ���m[��R�8����O�����pd�_�
�M�o��Y���7���F�<N|���5�����j�E���R�a������/.�r���5{D���u�R����(K�f�]���������3m����~!L�R�Z�,�DX,_�<��^w����XD�/��>�x��K��.�`C\6����*:�����0x����������/\�.����z�,���g��B�S#��J���"`���T��K�����Z�G*l���0�D/�b;�_A���+�P�8��������2���U��9?X����,m!�� ���8�t��v���`�;n
�N�J������\sMju������t�}������B}��9a�$yR�3
��'<����Z�|��Oxv�7o^�����>Wv���v�m}���������B}�������P��p^���~�5k��A��yE-C��o��0��o�-�M[��a�KV.X.��4Z��#@W��aC��iSju��{�O�t�Z�X�)c��;����}u��E��ia�+���`l�,���b��T��o��8v��T����w�C{K9q$m�n�	W�s~����+_��0B@OX��;�x�w�N5�[���-?xwj�v�����j���C��{���J[j���W�s~����g�-������!���F<~�r@�;�sK��6�����|�e'�l/,rr����f.��0��oM-�K���^{m�u�N?~����������
��`Y���������������Tg�8�uS��si� d��8���M�R���Y,����D��bx8�F��`i��K��9��uu8����-�M����9���0��i�"d��N\n#C����Z@7;~c�Y,&?��a���I-z������W��]����6�%+��W�
}���-4���sV�\�j�)�ZKW���ud0h��o
��~=�F�z��Bz��������f��miKu���(�����i�~5m!}��S�G����t��������B}��Ea``�P�T1|0W_}u��v�5��Ze�i�r���t�}����4���9s��I��@�8�������/���7��p�[?_������od�+&J�������[o��n���=���?

��g�����:����<8p�����Y������sx���������m���f���0����l�5�V��@�zDi�����;���xC��{
��oO��t3!��=p����}/��4��!�|��u_|i!`,�,���p���"�����w������;iKm�_�[a���I���k��R����<����h�K�����T8��Wg
X�M;;��K�s����h��;7��h���.}zj���|����O�I:��T7��g�s_��0�)W�-L!����n���5@3�/�'n����`i����~tG8��~;���������������0�QOH[�HBe�zS�
@3�����c�5��B�����	���0x�kiK
�&��?��a�K��6�
B-tb��Tm���a���K-�������G� �y0m����'�s^��0���ZE�����k�<���={�J\�%�X����w��{w��hf��.#���?��p�}�	'n��������?��K����J}��S�G��<��l��-

��/����z����/_�������A��s���h���^��f���������9s��I��@{;z������'��L���B_�����������(�>�v���n�]�.�o��E�����B�c������f����}\��y8p�����Y�fy�)�W����
G?��0r�x�R_\d�O�*��O_Sm���<����w�Zey��L��;�/g�(X�y������~6��wgX�_��p�o�C��F����X�"�*�?~��n��o��{���hS��T@'��[���N8�ok��oM[�����Z��g�-�!�8�ss��6��'��Y?�Zt�����������pr�W����?yu8����ig���!�6r��W�Zes��M5�����z�".@�G?��p������ll	�����p����-����v%d�F���7���N��.�;�Z�M�T@G�����������p�Z�������a���(L:����v��p9U}����M@���m[*�/^����N��_�������`�|��T�l��M��\��nQ��^��x���}������s��	�&�����G� ����S�L�/v8����Z����'�B�7o^�A�*}�����z��]l��=��-
�:�����O�q��=����h�+�p�������f��>HS8�����_�>���m�J[���pN�����)W�$mi
}M�U�;�[�,��r�"kx�SY@uB@'~����;���h3��Q����K��|���,��� �����<'���2��[����C8q�g��l����_��0�y�N[ZK_Sm���<]m��a�����z'vnI�
&M
SX*�m
G�����
?�p�b�s_���O�M��������^�j�O`:����Sm���W3�~�}�_��zY8�����l��Q8�w>f���B��s�V:����6l��5����w����������m�Y,���������p�S��������p���:����
?9m��	Y=�[������S
�D'n�>���s/
��-J-Zm�������)<���a������M���0��W�s_��0��/H[�B@���iS�u�x�qV�s���%�F�r�Y,�������g�|�/�75v��E����Ma�s~1m��N�������<B��+V���eC����������~�Z�M�T@������_�<��G��l�,X����_�2�M���v��
�|������O[�E������N���������Y�~���e��e������f�]{����YV�\�j@�:���R!O�2�_���t����\������
#�J[�x�����yO8����sy��9�w�`����%!2�����a������W�-���eKa{.�%�����5�r�4�E\"$�c�	����<���Rk�)f�h��{���>�?��=���&���0s��s^���9?��v�8s�5�z�0���=\FNU122j=,�����m�-Z�Z���F$d�t���ys���Ju��[!>fCC�^ �����_�w�<�={���hs��M������{w��v������+X�5�n�o��0<<\���3'L�$O
���_��p�S�Z������o�y�U[���y��~������>Wv���v��O]
��
u��������={v��q��W������g���}��p^�cd�xx��_�?�=m�o�����&u�{��o������e��W�K5�J}B�T)`e
Yd	h�Y �g�X�n]X�jUjU���n!�������'��x�������o���};��4���g�����O��V��B��<'�+�`0�<8��qx�_��_����m�3��i/xm�����������������,�T����5�]�6���^P!�����T��9�u���-�w�����\�4��Hqy�,��Y����?��?�������U
X���U��Y.�\��R��
Eq{y`���Ku���='wU�L=�����,I-&������g��Z��_��0�g�<��k�
�=3m�\�~�@����/�BeBT�s,��!.����Y����/]��U|�f���}�{���9�F��`Y�0��|��a��{Sk���/	����0��?��t��}�����w��!dAE��������S+��(�5��d��W�U��S�M�X�re�����_�|?�F���%��D9��O���Zf�b�Y.:��cC����N���~��T�����Y0J�	�34�Z��Po��;R���!�,�S�M���aC��U�Y,�/~rx���`"�A�eB��de�������������������������p���N-j������Y�&������l��5����}��FN
'w�
/W2�JK�L�#�yg9z0�F�<wa���WS�s���Px���
{�8��T���g��={fjQ��gX�xq��Z*�!�8{F-��oz�������`j�6e���D:������lj���?f�������~�����+�s�����_}���E��YYp��e�t��u����r#�x���)��}������Bz�����
�����I�\�Z�m��������L��7��G_�Z�i�7�
oy�M�������iS��_��)���MK[�J����LHi0�Y��@/���7��={������k�)�j���e���������h�
�Xq��0t"�F�2�Ea�s~!�:�l�+���{R����������~�B#�,(,J�	��m���	�^QVC��//��s����.�^�Oh�����g���v��B&��/}8���=�F��:3L��eB>��{�=�~ ���}�����O
O�����F	Y��]�j���hu�b��%��|��o�GW�+��t����6�t�5�
��`������{��������l�O�)��wqju�k��@���nO���1������"<�����B����_���eB6o���,�!�L���h����4�t����~x[j�f��s���L���^�Sa�3�am���C7����J��^�����_��1��0V}��S�G�+��~�,���"}}��:%���Hx#?V�^�Z���N�w���lhh�P���K����h��y���vhF���}g��5#���+��
R�9,KB7+}����9�s�������������{Bx���������u\pAx���ZL�J�Z�}��n�]�.b���`���t�2~���?��5{�����_��x8����N���5�� M��j��/�?��=�5���.�����I3�O[:��cC��|���7?��T�����nvj��t�h"��:E��3Y������u�Z�LH���[S��:u��;
;�L�f,�*��f�hQ��T�n)�*���(J�%���|1��v�����R���w{�b���5�����#w��Xx�������?�j�����Q���9E��U�R
(ju���
oxC��n�-_}��Rk�c�-L5�t�3�H���>�g��KS�s���P!`���CiKu�����k/�4�h�8���K�\���=�p��!1��e��B��e7J7*?;u��V�\�������Bj���3b�~+=��Ls��M�������)����S
�O���x���='�����]N��<���9�
g����Y��EY>Gt�f�!�n]V�[o�����:��y��yp^ew���	�nxj�6��������!L���t���|�?�9=~�}�����^��S�:���V�����+
dQ��1�PK������A��������f�T�?�[J�J��(��gy����h��e��R���ws������XD3_���Xl����-���n�b�����}J��c#dA.�,Y�j��W8����N�7�#.2�PC���Y4�w�-�������
L	S�<s�>����w�Ze����0��/H�����
o�p�q���������~�B�,zT\:��R>3C���.�Z�*�NY�~}�5f���kVw���W]uU���i���r���WW6:�R#��^�:����,�.XB��� G6�U����K�������aju�n�^x��H��f_23��S��/=;m!/B=*�")k��I������g����V���kS��n�7�k����V]G�p��+��
����PFgd
[l��1��v2x���{���O1�@�N�����pjUVX�u����~ ��u��Vu��{nx��\���Z�S	Y��u���Z[�l��F������J�\�����a�M\*��������+���5���>N������'�����Z4���p��u�	Y�k�G���6n�+��{��.�_E8{�@�B��,�M���/�X��������F�����e��k��6��+K�b�������;��,&_q��{4����#�����{j�������#���������o�0m���/
k^���b�Y���Yb a����u*���A��{�����}����6m���,����;��Nqb�ua����5���
�����W8��K��
��t��t,���v�o��@�R�/��������ZL$!rg�X���+�V�^]X�"�R��R���g���S�M{�;wn�Uv����Z>V�X�j��;>`������q�&_��������F�>�Y&d�����'>=�����{�8��T�;+.���KS��&dA�6o�<jf�j������S�M��s���/�����m��f��KX�<toj�6p����/|]j����|!`q����-��~������$�h!&D��add�F(�!"���m�6�� :u�t�7�
���)W��	����'�������l�K��j�����������J[*�6�?��k�^���Z���2r���8�t��������B=.w���_�w��lK���e������w����p�5��Ve���^��f���������9s��I��@���5a��]�u�)W�4����H����gO��0o��T�|U��
�+��vA�(��X�hQ(�a<�����N
@��=����h�+�p�������f��>HS8�1r�����B9z0mm����=�5��~b���7�>��{���.<j��W?)<��������T[��#
��qc������J5��
}w��E4u��B�p�3����<wa[,��x_��w|+S�b�%3���?5��c#dPb����V����S-��/O���0q����T��s&?iqj�,����p�;�M������e��LH���o>�?��|7�s�����g�=7��w��{����v d=����1w����y�2K����S
h�7VYL�ri��,C�}/��;S�����0�?���j��+��Kw�-���i����"�=�rC�F��D��5�\���E�_�Y,����>��V:��/����M��,�|G>���N��hS��(L{�/�V{ht����/
k^�����Y�X�bE���Yf��'�+���S�_6lH5��j-2����b_�������{���h}Sg�?���j��^���.W�����	Y���wo��g�����%&��pQ���;1\� �b��M���������-�5��+,�L�w�	G6�+�*��I�^�Z�5��+�>�������/z\�B��(s����Z}1 ����D�)FFF
��sY�)e�h�f���\i��f:���J���^�Sa�3w����c����sE����i�L��L\�#�l%��B�=��5��S�
�f>*��c_x8y��Rk�Ig]���S�u
�W|l���g��n'dP��y�R�=42�����'o�Vj�6�,M3x��p����Ve�_��0i�����g����+z��@+W�L����ah�o��TH��_�'?���`��|��V��g�L��`ijM�1�^1���N���lhu��2!�^N��.�F���,4������e�B__�%n��}{�)�nG?��0x���m���3^����xc����f��tB5�2h�i���[�z��}_
C?�#�F�b����X�zu��eK����}���������<��p����Ve1`�7ezjM�W dPGZ�N�8��D���+V��N�X*���'���/H-h\NT
VT.�--�VG7�3�*���W�)O~AjM���^������]F� �8�D�]"��E�tX"����c����!��f�`*,��[FFFN�m����K�\k:-��<�����>���0��&v����^��K���-�,g��#l�g�
h_'vn	a�dj�6e��cg�(
X� EU�Z�*m9e��Ea��������O5�l#���������!m�l�Kc���Tc���rBcP[�Y'b@���E�wb�"�#�����N���b���I�>6��1k��M�S��Y�j���E���W�t�x������p���H[*���������V�����}
�^q��+z���8��Di��Xb�����_�w+��
���pr�WRk��W�yE14�|�8cE=q)�Rq6�D#���?�'������ m�l�qO
���R+_q�����f��/����wj������Y�$(J��N��B���:3L��R!�]LCK�,)�����?��W|�SiKm3^�������^��9�����Y�p��R!��E��U���U�
edd���Xd������{����9���#GH[k����a��OO�|l����[��V��W��w����+z�!��e�7����|=�{Kj�f�Za����vj�������`��b�uiK}�_��0���K�|���������t,m���]4{�<���n������}/m�!�*N�����1����?+�`b,[v�2U���w{8��?�7�
#�����M���p����0�������������[S��8{�;�`�����;l��%���/<���i+YY4���e��=5K��������@�'vnI���,8s����]�~}���+t��Y,���L�c_������'v.m��oR�������w���yic�=xx0��n
����iKm��+��;f�����uk�����!d0�@E\37�������sk���]s�5�W��S��b����m��B�+�*bY�x�K�D����7W�q	Zm���C|C8���7N���M��8��{��J����o?~��7��}�������W\���WDqi��|�+��XY����+\v�e�;s���JDC14���EmLD�����.%�w���vp����X��*'=�	��g��&�`�p�����s��^�����iKm}����/{s8�W�	���,m��
���������{4m���f��n������}-�+!`�+��"|�S�
�^zi�B?�y���b����-�w���V~��pr����R!�R\2$�n�l���a�h���n8�O�G��M�R���,	����a���B�����sw�?������p�R��.}b��^���;|��H-�#^>r���8�t�m������B=v�������^�����n�-m�M1���Y+�r���a����n4��}������s��	�&��csl�����I�3�
L	���!LM[&Vi s����%�Rq��r��w�������-J�S�;v�Z6$���K�]�c,�/��b��i����J��0�+��������������o�Kj�72ef8��_	��,I[��/�
7|�����.9'<g����|w�uW���;R��W��U�FT�����=���g,!�����
X�B�B��f�����}e����:����<����ZO��V����
}�z��~q�8�EQ\>d�����>JC]t���]j��=a�7>&�0�L�'._��/�����-�9tl8|`��p��'���u���kW�f_49m�|w�yg!dQ���3�-�M��pI3T�;������iu�"�����J����h���,��WZ*��Z�fM���
�f�������I����������!|�LD��{��wm| S�b�%���8��q��,�8�����,22�����dQ�k3Z�`C8��n��B��<��h�#�~G8��������\8��?�Z�a&Z�L�~��_��!����V�J��P:������000P��x8p�t��Y����J�W���[n9�>x���{�)���:y������&~w�R��[����C��s��|���?�>�7���=�%��0����Vw���������Vu�\rI�D��F3�0f�6�E;,�7��
��,hg'vV�n}�Y,h.L��;&��T��W���R��_Q�RJU�EK)U��'���
�������I�^���ua����0i��������?���=������M�4��~:�|����x���v:`AvB@f����qc����lI�,"`@;;���������hS�X�j�^�n��j����������H[����W�s������d���
��?���nM��V�rn���<!���W���3fB���xFx�S��Z4B�hH/-�/_�j���wo��i����(-�������f�����X\�������6��o�~���^��/����1\��{���*q9�e��J��,����
���~Gx����~�/m�������^��a�����LO[�u��`���)|r��iKu�75��w��|��iKw����\XV��g>��a����E��,��us���Y,������Z�b���a��3Jq{����1x�h���g������Q8yS����v�R!1P���|�p8vb(�t�C�/?������l�R(��;
�fQ��K�,I5h��7�^��p��M[����
����a��_���o����������V��������k��2,�uN�����/��Vu�~��������b,��=����6
��_�>�������nq��E����m��-

��/�����x���+��e/��v[�����Iqi�,bH"�'�+'��R1��i,�(1�;<<\���3'L�$O
dw�K	G>������~!�M;;�Z�t����gxx$��_|#�������.����OO-�.���uC�A���V�31�����'r�����Ke=���pF����}o���`��m``�P������>���g7���W�!t�g���}�������M[��ycj�g�����9�M[&�
7��}xO81x�o���<����_�6�I����o�=��{�s�[8wJ5���k*�Y4AU�N��/��X�n��r�G�"���E��E�EY�q��N{��`"	Y�q�}�	�w�J�3MY�4���oO��j�����������K�3M��>������3	YPM1T�z����1K�.-�/V�Z������0KX�<������/y�*B��`8yp^�!�0��������|mC'����-��0�%+Sk�|������-�U�k�>!��%OL����/|!�y���U]�����R�B�U�;0�0F1XQ\/3v$�5`���_����~�P���tH+Q�_�o=Yg�ht����[5`M]��K���������Z���dF�d�-b�E����c
XD�?%�~�W�g)��W�PE�x���*XDk��I5���?�?��L��K����hI����8�9`���s�.`�����2,b��R����hP1\1�`E5��a:E�-j�3I4;`Q���t��;���t��a��!�L������Se��3/L5������+�)\����g�(���qb=�/����fG��}��a�G�/�(�����s^��0p�3��������G��;l��]iKu�?5��w��|Vw���<y2|���w�U�>x�^��������,�Q�b�uI�����FK�#��h�55�<y-R�����C+V�H����lH�plw���B��z��/
��I�3M[��0c���V�e�����������5���������ZP[�����qp��%A�K�TR�������2y��F/�i��'�;���,�@�W��r!�a"��C��p����VeO|z�������-g���_~dO��=G�����}nx������75m���7�pC���{���b�xX\|��iKe���R���E�����k�~�-�8����+�uV���~�`�&"duz�����D���C��V�����7�C�_�5�9��p�d~j�^�/�o����s��aj��'������Rj��]���Z����+���K�E1�Q����w����i����:�>� �����<Y�������]�n�@jU6�/3��Rkbm�����#{���v��Y�7����`��������}����T���x�c��T'dQ��E��}�-+��"[�\����W:h��
YD���o$s�V�mu�".W��op���0�?�G�x�+4E8�uL��������>V��wRk�g=�����=%��>!��S����71�PE=�f��P�O���������y0N�W�A��<�y^��G��O�#�*�<��������5�>��;��>ykj����O�~�R�{>|����������L�RX<�1�I[j���R����*���A��e6v��i��2�p�}����W��c����B���q�=�w���w�Z{j�����;���>����R��|���������O5��b�C��b?E����y��c�K�;�O�,������A�nV�*���y�I��N���n�b��/
3�m�5�������7����X<��C����|����i���^����F����F)�R��ev"�T��-��
��:5hq����ZeW_}u��c����V��+R��X��
��?�W3y�#�{�����7|k���5���ya�r�9�p�`(�s(�+�
VTS��-�(����2x��p��oM�����g���a��G�-��#���������w�-�]x���W�� ����-���,,�����1�������M����>v$Lt��\i���S�]'-6l��j�]u�U���������
iGt��;GOG_4e�����:�K�G?[g���j��pC+��J�@��A8������iKeg�����%ON���������������
��Y�����]yQI���s��\a&�Z�>�������~�B��,*(
W��#�T<�xL���Stb�b����6����S���,s��M������Tkt��{���~#�F�re�-���w�wI��~��O|������h�e)����r
4����5�gjU6�eo	�������n�/���7������Qg�x��.(�d�m�����u��p�H����s�-�`q�9c�<�v���
o��7�Vj�{���7~a�K�

�q�����B}����+��e/��v[������S5�g��7o^j5O+��xX���������9s��I��I���^�����1���7��p�[>�Z�%~f(*�|084~y�����L[�4mJ���g�sgNN[ ���f�n�;����b���app�P�
�0���?��5{��	���;9����N���5�� M�������(��3�U���f�~����������S��W��	�5K��Z���{�-,r�d�~��G=�Q���~a��F}������g����i�)���]���T�;0�L�N�����1Q:�1^q�x�wV��8�g���g�����2!��-S7`1��/����?_w{����^9�k��sOa�z�G?����/|a���x��~/����rT��l�,�	�IA�
6�\6�������qc���k�,�v[&D��npr������H��:m��>x"|���Sk��>jjx����Z@�:����c_�`jU60������������n�^jU��������W��<�����|���/,�3,Us����MK[�+
W�~������&T�-b@"N'�Y�gc@#�������������.,�'j�b1p��0����Vg���w���UW=>��^u��������Z��?��p���-�&��?�'�����Vuf���
����M[�����^X"��2�]tQ!`1e����6����h��&e,��&*h���[b�"��(.b�!��R�����f�(*<bx�XZI��n12x<��z�b�������y8l��]�5���pV���^�Z��O&�������5�*��2#�����I3������������~������|���]�{E����iKw������m�R��K.���H[�����E���$���l�����/^\(��~����;:iF���!��21�Q^ZE��nrr�u!�H���.���B>r}�eO�W��,�������}"��D��5|�����Fj��Dq�����Z����c���+|������^y����W�m��|��__��S���?������I����+�'d����;v��g�?;�l���<b�����m�N�x�N��o�X� ����d��q���V����=h�-,�6�k,2e��0���S��}c�����aj�����������G�;s���&~����b?I����@>�����Z��x�[��'=?��u������nW�{��������<��Ou�8�]w���������<��O�~��g<��8t���w�_:kW�B-B�;J�[�n-�[�VGCQ���K;D�uz� �*������q�Q���A��&`@������/��h�����B>����X��*�X�)�%�~g�������~�j���n
�w�co
��~3�*�����~��S+__������=�O[���W���/I�����0�p�
�s��\���{���.����h����T���1�+�
��?�����6��Y7��L���!�q��IP�����1Z��;@	x���v�����0�4z�y��3	Z�'����X�uG�^�9�����X�����h/y�������Z�W�/�|������wk�y�\�75�B��Oy�~���V�>���?������CiKe�����x�S�O>����;<x0|�K_
�]w]�������5+,\�0��tf���l�b�b��_	����g�?���~L8���8T��b���W��/�������m���Sb�B��>�R)`�/W�cY�n]��S�^aR~[�~)��}����<����*�����E{��[��9:]4%,&��V������VY\� /��.�;�*-�Z����������pl�S�����f����V�>q�������e�����w����%P����G�7������-����V��w2L�t8<e����O�0�#7���7B8����l�������W�-\�7��{0���7����^�z������������v�mq0��
��IP>UK��+���q�~��U����yQ&�t���3�w���N��7*NIZ����r�y��U��C����z\�i����X���+��e/��v[�2v��ZV~n�A��N�o����us��i��:�5�����kSk�s~�}a��g�V���t~n������-��>.������_�����y�[o�D(��bXb���W���\�>���	�A���&����z�W(�a<�����N]i9{�����9!Y����<8p���`��� ����:q������<��e���|������>s[�H�%O���?+�y���%LK[:[|�n���B)�[W������_�f|������/�?vY�}���K��
�M�5�`���g�x��d�*�y��Y,��*�tQ�{�f��C��[�����m��W>{C����X�reQ�wk��q{��R��;�}S�-ZK��nv��������#������[GRk����b�*�'������4�K�,I�S��d7x��p�coM�*��3�m�����g
X�������?�k�w�7n,\\\+`1g�����?sg~�j�"�+���O�>te���x��}�O��|x���5�G��w����E-BMP+�P�(�8(*
/�/S�����5kR����M�BJ��R�d��g�v���/����y�s�����
�h
�Y��s����St����}�hx�h�/����'��g�B
�8��*�%*}�.�k���P����C{KC�gC��z�����ON�|���?��w��v���T��g<&�������w~F�my��M�[��V8~�x�Z����t�����Z��'\1��pEt����������G������,��4Q-8�&&j�,��#1�Q��E�����4��kTS��+�8���i����������%������+;#d������8�Z���=~�E�K-�|d������~���������3�*�����)O~~j������M�3�p�}iKu+�wqx���K�����}/|�3�	_��W��C��������c�~8��4�p��������n=>?�����[��PO\@d�%�����t���IP)<��g5�v���l;?Yn{�?�v���J=��U#��w�y���n��������S���(��e/��v[����W_�j�c��
��<t��.����3'L�$O
�����W8y�
�u���������V{�������T�
�7�������4O��G����[oW���a�����+��wj�&�
���+��������N
��=�c��ho�+�p�������f��>HS�:��!'j,�M�o��W�nj���
����-wU�H��5K�^��'�Vg�����M7��������g���0���L�G�p���]�p�"���L�5�p����E���D�p�����G���^��T#��w���&�C��,_��-[v�Lq��jk�vJ����D�,��J�J�G��n�N
YD�	ZP���F��d��=��w�4�F������g�Lj���w
����;�F���a��W�4W��n�;�����,�����Z�Y�Y�<B��`8yp^�!�P��:r�5�����Ly���Y?�g���]�,,~t�D�R�������y�V���XW�q�iK6�>����3�O�Sv��0\wdv������p������s
��xxQ�wlt_����*�y�@�����uk�������%���4}h�G���'ci����=���W��Q��s��V��SX:$��;�\���a���X*�#�����U/��+?��W�!�cs�K���<�9�,v��Qx��'S�������
X<x0|�K_
�]w]��]��Q��8������h|Y����������,,��Sa��0�K�$�Yo#��:�q��W��>�����#_	o����5�v�7��1����
���VX��������Jg��j]�Q>�E����D�+P������J�W��q{�[4��*|�<���Z4�����T�S,}S��V���w��wV_���O}Tx�S�O-�|-\�0�N��=����8;E5�?[���7]�|���UY��/3_�����g�rO����;��e���g�u�jx�3��t��G��o|����[n�%m���s��c���?r�������z�'��]��d3}����)����?^����{{m������1�{�o�7���k��$���g�wsv��w/_���p������`��,��|V���.�;j
���l�/&J<�x���X��Y��(
S���<��PN��9,�%�w�
���nj�6yA��k�������e�X��?�����X�
N��l�� ��;�}���UY�����_x{�43�3��sw�w�������GO�_���cs�K[:C\���o7n7�|s������������O=��0����!|���p���	w40{�������!<���	{�|A���^������v �{����7|�������SL!�1*�X�v���E(�wTNT��vXw��������#^�q��b���C(1�Q>�H�b|,�5�o�>����.	S�>/���g��������m��?6<��g���(�_��8f�J��~����)��������N�-����������Z�����P�������sE��������{w!\�k��0<\{��R��+��������!�j����=����G��Y������e��{���}��a���hBc;�;��q����DT����#��gk�x�.�1�[��|f�(�+��P���m��yTGJ��q���%�f ��c#`@/:���ljS�����>����X��,@�Y�&�N)�������}�O���Z*�;{����w�Ve3^��0�I�S����/{�X������w����1�MM[���}���M������p����5�����+V�+f
������W��|����>/|�d��RFF���G��{�����}�������<�xQ���8�w(TS�*���P��e�g��q��m"��j�zC(�����H[���j�A��X��N��5�/�F����?k~�w��p4�F��%�_�9�@�(�����Ov��Q�����z}��b�b��/�M�k��g�"�����p����;l�����^�����>%L�������o�=|�3�	_��W��C���l����/_���g��'v�p�KB8rS82<9|�����zj89�mh����p��L`���3CX>oR���]��PK��e�L"qP�l�����jbA���\��,�Z���m�q�+_J�����_���&^)S��3��n��8


��\r����S���'��o�v����i�^���=.����
���K[�{���v��0vg���a�m_N�3�|����e�'�����#��?��p�x������������)�����������b"Uz_����n�]�R�F�J}���Ng��u\�:��A�6�k������f����;c�����"8�>8k�,��4�]��ga����U��g,3�OS���;x"��|7�t�CiKu?��K���_�Z�-�|���{��M[�{���������O3@<�����!�v��0|���p���S�W������#?N�w��=�?<����q�O
��7�P?~����SN�����*���b����S:�C�7���P���ti���q����1og���kS�9����7���[S����w����[���s�a�x���'�O��?E��R���+��;��������e���{�T6�P��E����N�����\���=b�G�/$���~������Q��V�	p��/}�n�b�����8p�������)`�?ui�,�������p����//~���oN,��d;_RX���'��|F����c/����,b����W�??��'���'�w�j^��_yRX�����?������+w����	Y4I��2v���o,�Bq��b�C�u$�o�+V��4��iD�� x���'hQ&��~������~1�*;qy{���C���oW_&��s��UO��Z��?�}!�~��%B��>����q����|���UY�c.g���S������B�����E���s�+�z|j��8{��������F�y���������E��/L[�����ky�����W�D���l����|����#+���4�8�u�%�m1�R��j)����L������s��o��c	��;�<��j�M�8�]����)�v��^�����n�-m�]�^zi��'?�����zC��sxx�P��{N�$O
�����0x���u�)W�d����Z��]�6����5/:;<g���SZ
�u,���B��e����<X.�f��m��'�"��������3�9������'�-�s������M��&M���'�E.H[�W���[��VC��s�=7<�)O	�_^a����!�=��=:+����}5��?3>�3ax���4���x������K�Y.d4����f.�Q.��QMi�!���/NcZM��n��b��e���S��T!`������C�?*�O����T��W�7���{�,�)�U��v)7�~�f�b�E��ET����@�8��O����u��W�-���u_�A����gO	���+�>`1��+�O����g���������]�����{��
,&����Bx��/WX<���f�hsB=,�g_�����f���<����dU~,������q��^Zf�����U=,;��G�)�_�Z������j�-����t�#���p���ciKu3��Q������<�������7����������]�7u�o���M](P�P��������)s��	L��	���?�o��7`�aHq�"-Jq������o!M��5��������<iC%k{���}f��	]bCe�vJKK����q�����B��Mq����������yod& ���l�VP��2G~~��(,���i���o�0E-'.���[c��]����GZ���W�Y�������������X�Q#�7�_�%#RjJ_g-�&����oy'F�L�0�^�a��8x���q!���BTOe�y)J2O��2���8�1Y�>������������`����qG�.��i�'�x��US���U����r7�u -�yEZ��rE������*�����mC�D���e�>��>�~K��:�x��������G�|��e�S�����g��h����X���~;����d��\���3��Ue]�����\�_K��Mi�@t�V����#��e��/aKu|,C�
�B�8,[�c22;�p�c���kR}Y�)h��Q9�,��p�*d�������-|�w�U���{;��p���}�h7��.+��R�a��������DL�6Mv����	��� -��p��W��,�Y��? w�LY��?�>�KV����c�d�aY����)`���>��-��"`��h�[�n������������G�QR>����������"�q#e�w��
}�dU���Mi���<T�xq],$�3`Q],GZ���#f��!��M�>]]d�������NHH�G���
�k/N�m���������M����a�����"����O���:`!���
X\�?
m[�������a�wg��������)5�"���,�C� k�t�
�";�#F�:�k/����,Dpn���X�r���{�x=�^���W���S,�Cq>�1������
k^�r
CU`�{A]#�
;5T���0���,�_iO�+N�������E�0D���9�����cS����DDD�E��]���uu,�[Sf/I�G�&�j)���j���09�(e�>�����m�qC�q�k(h����Yx_�eg���6�����U�#v��?�S�A���F��9����xs�%��\���V\���)���\v*k�������0���D����������!JWj��������(">�EE��"`��x"FP�!ylAma��cJ��-E��*mYj�6j�|��R������!XY�������W��Yyg4s�O.�wH#Y�.?.?����_)r�������(�p���q!dI�k'�c]D��ik=���~�q!�n�@Z�����q!dO������Y�4~�5����J�+�.���O�6���
�L�B�^��gO��
a���1�Y��o����I()
���Fv����MeU���d���/C.R�u��.�������(�8�������}r4���c����<)h���2�,����/�F��M�����0�L~KV�K��7����_%aA>���>����L��/��,���:Bm���jC��NZ�������)�:��y�� �����YM|���4y^�� ?�8&+u7���-ck�.�b�
�pt4� v��7�t�1���-$��-;��";o������W��Em��&�������,_|��WY��[����&��
A�g[,h9�������v�� �|l��2:�"""k%g�,}-"�QXbLHE����6;;���X�W�g�i<�����"���M-|�[�C�)�������q#V�\�p�B�^!.���X{�o�Zr������8���j��E���,��,�5\�s��PW��_*��e(B��>�o���k����v�X������T���,�h��T3�z��"""e��Gz����Y�.�NL�B�D7��C�����v��m���"�O�$�������G��/!p�S��~�����W����'����-dU{��+�����x�{��Q�l�1���M����wPTR(���.Ef��(.i,;�%�����c��q��P]�q!.2�D������a��>.�\]���}BTe�s���Ue���"H�E��x��h�)YY{��������qG�	B�,G��kFj8.�����>�HB�O2�)���9�����,�Z_,�������
�_�I�k�����
�L�B�^��gO��
agz"~��2�e������������v}�������j'�5��q!DDR]���"""u���U��K��'5���F��P�����"���,OA��O�����
X<zu\�Xh�{������b�K,�Cq>�1��E� _L]���,\$v� ����-� ""��`�BydM�V�eU��^|T)�qT��uJD$p7O"""""��������B�����D�
o"`�C�����N��-�CO\��4�U��Wl��+W�DNN����q���S�N��n��/����ee[qi82�� '�r���3:/_��-�e�<
C.JHH�G��E&�-�=h�����(��@�����G������~�yYY�5��������;���W1>���������2�^���'�����0��7����6��b/V�8++ubt����eU�\��b���vw��_;
K��(+�������

����+z4�ccZ",�#�<C.s$�D������`�����>��(-���5�Z:*d��4y�l2w� �Zn����������W���������_7Y�����$��-p�����B�)QT\�g>�c�����niWkF�j�{EI>r������M����-������7��4P�*�����-pu/�~����.^1�p�B-�����`�����1;�w��m�]DsY��?���lYY�0�	������v���R<x0�DDDDTo�s���t��������l���>����eG��<��nlLR�M���S�cH7���8z��6�Wd������-�����`�����8��8������M���1�*;����n�����Z�H�X@�1c�)dQA,2��q�X� r�U�V�R�X���t�c�l��]�0aRSSe��1`A�������too�I�<Y��c�x{���_���O�U�q���q������^�nzo4
��k����G@��m�������Ae��v���Wu�B����]?G�Q�u��x�nVTTd:�Wn�LU���|a�+..�k\�|^�RRR.������AV��ys_C����c���x]�� ;�s�y��[���L��CY��L�����wD��5?�4##;w�4�,%v�����z��$8�5p����I�
�MvWJ}���5�F��Y)��n�DVu��lSZ;`��
,���r7�,W�A,���!�����s�| +���x�x���0W��-K�������itK�:.ZV�x�K5�!R�t�IU��W�N�*+��� -��p��W��,��?��a����/h�S���VV�s�yu.������������;�{|������c����Gv#v�P
"v��
�(1�Z����������{����&N'��C��|8����d���_y�"1"��"�Y55:�"""��P��2��,�r�0gI���5���Q-eEDT���E���""""���$�$r�����O�����*,u���|��n�"$������"8���:��W��h�b����[�n��?��Xl�u(`QZ�����m,ZE���+b="`A�a��
���\H �L��`�����9EG���T����u'�j�9K�"�X~u��F����u2���6�X�7y��Y���}+e�6�~�"����k�Qv�s�L�)`���+;�����cBe���>}��-��u����'����+F�Yy<���b�}�����{McA�������������~���j��	3,ZG���Gb��B\`k1A������#r����:F�0`A�>BT��{�~�Ue����wY�GOp��-���4�>�]V�qG�	B�l]xR�u�����FKP��q!��u -�yEZ���S�}�����m����������_���Su��W�����/�p�l��(k���wt@l� ��^F��4������{E�=.�+��b�	b��PX���3#��~ ��0Y����D}g��4��Nf���&����p��bg�Rm�����Z0`A�^Y��_�RC��*v7F�+��a���X�����=sS[��HV����jCdN���`8�,1dAZ����>�HYx��
?��F�;DT��������;���e�=�����/��Lf��V���?^��#��d�z�����(,,���o��]����	D�Z!�"`!G��+.�a{�`l����8U���=�����ss���9���;7C�����Y��d���_yp�6D�I��!X���s�j�B�m�Bv�d�Xto�p����6�#'DDDD�i�O�G�7 o�k,�����>q{�B���l��{��M����j$`q��1����7ov*`!�$'ND�����O��	e�e�W
X�+j��Y����wqG�&�w�	�B�CC�Pd��Rv�����.������!��b!��@����`�����u�;���|c�@����j�9K��#e�G6�GDDuCbb�<*'v� """"�4��"���QxPy�%�����F=(;�v��EF���B|� �zWG4��?6������f����?8{������aC�1�;A���4��[i,HJ~g�v�>�pt6>���|�sGB����@��!�R]n���].��"}pc�&xdd���U����������+h�������������e�<���<��I�'���7D�6��""��x�	y���u��x2�>��|;G�}s��~��ho��L<�������ib���Q�^v����;1w�\�F���z���c����++�Z!��l���N=�G-��G�/����n����U
����|��-Y97"/��k��#�qE�F����7�-.��@�CTCDDN�j��""��)��@)����K�����X�0��<"""""""��TZ`@�������(JO�]��<����Wp��hoW����cA��(�������`_����������B�h���i4H��-��!�]+��e��c@�L����?����p���|����!8�7Y�+)	FF�}e_�^�!��CpkB�um<fL�����M
�T���!��9SQ}�j��""��+��>*D,��A��y������YY�|PS�6�=/���N�*������<C������U0n�Qv��k?���A��N��v���d��T6T�l����������s��b�
�x����KM�6���c��W/�e/������0#�+��/N��),���a-$x6��J]Qq3�>���}����|J<1�%Fwl�����Od�!%$$�#`��i����g�XU]���e�=���W�v�(-�,I��5�&s"���"h�Y�f�#""""���$#9?NC����$�������I/ x�[�nX�����t0�-�����"��]��������Bl��,@zz����`������2V{���g��� ��G��:o�\��|�M��.���Mx��0wn��1�����,��������Q��q�j���D�=G�X�G�N�Q!�aM��v��j���i8y>_V�&�j��B�����������OV�^-+""""��!���x�J�Z,;��{MB�#�B��2��VAa	v������w�����=�vn�W�������F����Ov#^/�Abbb������.��������=;|t��>�|��������Kg�S���B��!�*0_PX�p!�D����DDD�c�����Z{���)�����X4���Q-eEDT7Y^|2x�`-�����N(J���/�A��o��k��q�o|A�?��0�u��@E�)P����7�a�Sk��{;��o)��l�����)d��'N`�����q#�F���'^3�0a�v�
//�K�������OMcB���d
X��O�����o�u=��{0dQbAa��U�*Z�b�L������.��"�PZ0`ADD�>�{��4�����k����K�`,(���F����I���&��Z�P*�E�����j�����-(<�Qv�r��	~��N�U
T��<P���X��R�s�[;fT���~K;Yi#//�����e�p��i��/""C����*�e�i�c������p�(J6m��o@x����2�����?�K;�*+"�+{������d���"`�N����@Pqq�b��!C������������LMMe����<x%%%����x��#� b>����>-�"���dU����]ol�����A�����r�������m[yD�-���'��z���5��1c����&2s�LL�:UVT���JQQ��X\����c:&�����k\qqq��t�c���������r��`ll,��������]�J��|Z�F������j���E�8p,�r��hv�q��
{.�������6����;v\Xu�x�w��:X����{��53{��}A�����������S��t���-\k�Mi���<�H, �����w�`�����}J�2m�G��E�X��1&D�<��<""���&BDDDDuB�!��@�w�8����C�%�#��O�X�@���l���8��� �}k.���G����~M�����Pa���&�,����o�>���_��m�Sq�����e��_w9��i����
XD5��C�`��4���U-� ""r��]�_��w�!����X�E}��^m�1�[CY���[�D��W���W�����h�>���(;�
�Jl*R�`�I$so�����M��Uq�r�c���+����b�����������qc�5
}��E@@��^4�Y��wN���D��_c�"��w8pf����������������Q=�-o�DDD�e���<�&?��eU��,����
���������Hs�gR�3�q���Js���m��Q��_;�����l��<��^\:mm�*�5������p���Su�N�������?c��5HK���a)00���3,D�����.?�9e_+Gto���be����(����b
X���� b��+_�%�<"qX����!C����y��^�Y'����:���(u�>�SV�����:��U�Y��^�f������O����(8'�j���QOX;������X�z��Y��
�,zr���d��������L�DU��i!%%������X��&���F��w���c��������w]�4{I��;UV�#"������E�|����{��y�������v�����]�u��]�tQ����D>_�������������o6���<�]C+��T�>������&������Z����#k����"`!�E[���v
S�"��)�[����f�r��������x����k���>�#0yT�������^����x����rI+t���UQXXh�Xm����;w.v���r��E����K��{w����������,�}�������_x��,&v���vYQ�T�}�m�de���e��f������[�N���������������y��������e����"������/;�RO���ww`�f�/�;�R��6�@����h��_�u���b�O?��
6��I��v(	3�T&vNW�Z\R�OW����'d��N�kqi�uXp��N�%e�[{n�=#�^#+�����J�-��X��&���#�,U��B��mZ,!"""""""�*����^��5���}��v��0�n��m�����E��l�q�u��w�@E�**�={[�l�o��f+/��TE@@�u�����#::Zv���6��?a���Q���B���|�oX~�o�����������wt������,������V�5*���Hx7����X��T��r������C��w�=�q�=";�y��8�i���tM���mb4��_%!/�Xv#}M����@�w��
�|k������={���c���HJJ��`����u���]+&M��N�:���eI�M��������7��k����8���"y�m��b���Yh����U?�,�7�W��U�#>6�1y��]�P��.+k�^W���s.��/>*+k�#��a$w� ��e��Y�[mV>F""""�F������n���Jv��w����_��J��3g���;����7�
T�b
TD�Q**������CX�b���l��
������&**
}���5�\����ha������E����V��~������Cv�!���B�w� �Y(������ �W�E����������S���B��m����^����RYY,^^� "�C�M�V�����]�"�D�Z����P��">w���7�M�`����������>(�6����C�d�6]���
]�2�C��m�'
x���X�������yc��m���=�5Pa����X�n~��'�]�����8",,]�t��	0r�H�i����w�<z���<��I��/�"�4�Z�-|}��=���z��2�}�d���0da�2���[��+�ZH """"�-���P��NV�j�.)�s���+3�����Q�""���:�X����y�B��N��'_m�E����u��k>DDDD������_����c��M{�7�v);���y�������:q����]1�{C��>b���c�0o�<,_�)))(.vn��9???S�b��Q?~�)d*��o��L<�{
�����t��8���Fp����N�{��+����z>*;D5�!�V����ceu1l������c#�'2nQ����W}��Y�Y�&���8��<""�[�N���3g���X�!��[��+��'>Nw�����	o�����Q}Utl7�>��~��|ZtA���E���o���'v�|��$�����=��M�wG���kv��m��"/�:����-[b�������McA7n,�q�w�N�����b�O�T��A�����^^y������U���������d��v�	Z�o[*�
��D^�$[�F���zs�$=11Q��w.$P��r�J�L.a��!��f���<���/�������:�:��������d�2�~�!p|�^���@�~�[V������wu������_m���GD�RG�	k��y�CE�@�X�3�
$;�#���-S]��>}���]��Q��P���0Z},�A{j�Z������_�_+�1QU$''_�Z7..:��/������ v��=���.�_���;�w�O������SRR�7�w��� �-c�q�����b����[U5l��Z�2����e�y'2�����{<Wv���v!@�_�$�q^�F�1(v�6w��)�Z�mJkY�!Ng���zECE��*���������w�� ���!��/�����MYY�otM���f<��.l;�)+ko=�]b�V�Y<�����E�%��+*��k������d�����7�B�z�X>���1dAZ����>�HY��� �s_E���d�6��hM|��}e�1�'
xc�~��������um�}<��h������f{GM[���M����hDDD�����d����#J��k���1�q^���H��1��d����lc��
�-*T'�D�B���sW�;0dAD�CDu_�����T��*���'�Hq��[���������^�1mrYi�'�T����]�b�b]��������;\!X��w,���q�r��S��Y��b8i��+�C�)L�`
X��=";��u���������yo���C�A�����W�x�'N`��
����$~�U�X��iS���6����gL�����]+D���+��s����& !f<���$�p��6�,�����;p4�CDT0dAT��_��������f���hY���������<�bZ�l�����x�K5�!�	[T�=�s4�%��g.p1��+b��*XR� -��p��W��,�����-~_V��q�b����,M�����=�����Gg}�����$l��EV�k���i�
�p���39���+v���}-����h
X�*2��i$�W���>����kM�)���'���Xpq��n�1����7� """�����wydM��E�,~_}�f�����4X�$�"�x�_�[�u-��k"���*`��:�-%��[��y���3������fv��N,JJJ1s�������7�������l���N,��=z���/��a�L�w,����~�NnFX�������������TL�%�������p�@�~w.��������@DDDDT�m@�.��5}���Q�(,*��%Gee-@��
#[������u���P��k"�"�y��YR�o�4������yDDDD��
�.G�G7� i������Z���=t�;��c����������dG����on�������q��Y������6l�1c���K.A���q&'�
���4,*����BX�'���)�uN�f�p���������%C�T�������81NL,OoU�5Wq5B�	3T]8.����!�����a�W��>u1��#eU��^p�-VY�1��Q=!n�H5��B�aG�E�������������e�������BH�@Z�����q!��-~�����m^����4�=&���V�<�Ys�`,����6-���u��m����=b]s��
������4�g���=�~�z�m�� ����o�]������c00�*4	mU��Z�kM�)�0dATO0dAD�CDuW��#P��!���=.C��dU��d���~����&
�������O|�&0dAT�2!v�����f���i�dU������/�P�����Y��b8i��+�C���F��WQth������7�.{���e�qs��9<dD�Fx��x���wMq��M���m	G��-���5y^m:���6��y�<�-��	��GBH>��A�d�j3�5���v�W������F��������G5���GU��Q��}(�������"D1n�8��c�.\h��E>� """�k���!��X��
���t����3gp8`q���x������������X�� ���7,��x��4���!��%�-uY�>h7^h��1� ����(�����5�����]V����\�[{BV��G��~Q�"""O&ve0'��a�!
qs�x?� """�+�����_�Gi�Av����!7�����������������dG����on���D�N�HMM5��;}�������po�=d��~�z�~]����Gh�������8���i�����E�����@ ��� ����-�E�[Ptt������$�j��V��bADT?X�	�O�.��O�!Y�j�����&z"�aN2���vE�{���M�_������~(B�������V�8c
X�;�#;�����u���
e�zl��
k���0N���
����[;?*��M����s2���="�^��o���1���L�
�Ax��,B��� �\YQ�����WP�z\&���y_Vn?#+k�:D`@��"""O���w�ba�;� �S�N�z|�W;����X"""��B+D�B-0�O~���e�q�B������Xv�����<����dG{yyyX�b��q�k��M�3aaa���3�G����h��R�H���F��!��E������!"""""�%�gL�X��{^!�j�l{�X��.DD�N)`!v��wA
�x;T��������-w���"""�m�H��_�3�q�w����#�CvWRR��s���Re��[�E����WW}/�����v!�uD�>}L7w�A�rq4H/�q<��@�w��Jf�eA����nVs8J����\������X 3���<���/�������7��D��������CYY�Ox7h!���t�i���~YY��1���������?��m��#"myy����<a��S?/r����M�6�|������Vl���iS�}Nna�\W�9N�
>�H+����{b7����,���A��J}�e�q������,���?z����E�8��T����#-���L�A���e�2W�S{��`����.rj�
��O6.	8��~�d��S�3�x���.2^q�����k�s��&�!��3��a�����b���<CDuK�[P������q$��CV���Y[�z2OV��~��5t~���������	k��yQ����� v�WCVpd��f�h��	�>'""�w����sde_N������]������l��
��7

F�H���xM&55�����"##���o��e�������9�s�
rI�A�x5�C�����dE��kM���xfCDDDDD��`�"�����Us�B~]�n3`q���5� ""�����0b�yT.11QU?]~&B�y���Eq�d�{���������,�}�������5`�����{�:�h��bbb��Xu�m9�4��
q~4���m,��<���[������NDTp'��#���Q��NV���� ��dU���q�+���S(;�����s}L��)���jw����rwa����:u��j�Y�fa��i�����N��O���A�+%%��Wll,���-��"-:t���A���'�,HZ���P�}Fvl��{
�?%+���}��2IV��2�%n�RV�C��o��YV��o���]����j��,������}4�-��4�uYP]w��y��&%��������j\Qz�j�B��],f/IS
X�G����iG)`!.X��%			��v������9��y����9��y����9��=�f��r�<�P���W��I/ p������m{r^�z�|Duz?o<wK{�<&Z�q��m�����E���M;�5m�T���n�,�;�U����'+ ��%�b4����dX����.�{��i���Vwo�<�,�q�����.�� �U��&""""���wydM,��{�L����|�Y�&+kbD�BDD�G-`1h� YiG�;�Q]cX���9���O�^�w6�=.��%�f��_�EI�������{w��n����������LNN��:w���C����=�H�z�~]�����d4�I�'�~�@��d�����!�����Q5�l?)��[X��O�]�8.����!��J��8��H��@v*��w����Ko�t�S_�x��6����j��PMAsKB��&��Aq��de����6>w�zS�v����q!�����5���8�q�[�yEZch*~�14��{0��� �lY��?�V�yDV�9|"O}�g���*����t�����[_+b�����/�K����g-;��|^mM����Nk;��]@�Se��X� O��&�����9�D�@,@8J��#A��<6Q}e��B%`!���.����XtlR+DD�^�\���VT��8l���B\�ADDDT]r~��P��+�B��O�'���WIv�
l��ol[��-[�`���,7nl�������s�������n4H�k�^[���2`Ad�!�zN)`!~��qD���d]��;2*C<��bE}~l"""����Y}T�o��4m'��5{�Qy�l�������<�8��<�����F�!��9z��3�Q���yDDDD�����}���u~�� ��������;���-�_�E�i��(��1�������X�t)���d��v��a��Q		���:R���G���
�`,�m9�� ��l�9����z���
[Wk(2�=W,�OQ{����5E|BD���B�j���������+����_����>����������N
���dU���#���1=a��S?/�O�|�����,�����X��8;f�:��3����q�i��+����)�<���ME��]��������\WPTb�3%Kv�
����R}m=z6l��h�u�o=1D�l���G����4��.�f4Hh��� ����\k�Mi���<hL�LV�j�1Y���ZL�Y��`k���l-V�����'>6Q}fk]�5�f/I�G�&��.DD����"������X=qN��M�X�3���\Wk5"Ha���k(�r������#""""mOB��w;��/�,��L����>�Z��h���#�
���tG�bY�)|���(�}���A;��z�e���A���
^��:q�+N��M�&;�]����e�L'�j/�kM|���0�9��[WKX. 8����C�}��c�$���ND����Q�Ur�2��BV�G=�!w���,�t
�����K�E��k�eU;���	��IBm<�q��~^5�b��2 .��:u��������"�n,w��
w�/X��8�����"�������;���"-x�N������T��g����I/@��2YU�K_'a����R�9&���	z��Y��kv��!+����_�~���8|.	�m������=V��:F��$�"u*�V����O��
����d����|��M�I�x�]��;rR\�� �^�_M�:���J�<)_s�;`���Ejy��PW�����>3nQ��B��������������y�Xxk"�K���m��X���G"l"k4������� ""���`�"d}������7���o�����E|�`�0�C�,6o��p��W�^��������$����W��Q�X�T�B��#d+n
����b^��2`A�"�,�@,����E5��jbQA����x���W�X�*Xn�)�p�
����DDDD���Q!b�;8RV����p����e��M�eEDDj��#���P��/@�<��-�:�X��� F)p!�a�]E�����%�����)Y��
i��)��������K���*c-��
��m�T=�������'+u!!!9r$��sm|����������������z���1�� vF��|���^��e��UYTQE��YJ��3f����r�GC�^Y!v����>6Q}!v�(����5}����"/��m�b����Z�����X��t�R�A<Fu]|".���������TqAL�E1�7q��kDDDD�2��y�^��:]T�)`��Mv��������e��A�^��Q�pQ���b�
>|Xv��l��������X�Z�xh>
���^��� �F��������N�_2��[��y�,�@-`QqU��QW%�
55:�����2E���*!�+5����&"""��l�b���|�������Ev^�l\%7�l�=�0�����b��V��b|�%��R��|Qey��e�J�O����)t
[�N�|��(~XvLV��}���h�$Pv������K�"==]v�u�����������W�]+*�
������g��lQU1dQ�����UW%X�0���Z��&N,?_$Qc�yT%|������MDDDT�nA�Q�����'����~6?,S���e�@\=�����H�������b��X��E����QZW���<�������_�#�lY���8����w`��T��+����#�R&���!���eG;���7,��=+;�z���n����]�V��a|��� c�n���?�=d����!Y^Y!^�we�R��r$GM���%��f>!������������WP�z\&��3g�z�B�<�""{,w�te�@��e��rM""""�^i���>�����:}�+r�"� ;U�������C�R'=���J;'N�0,rrrdG����}{Y�Ku��BAa;�CfE��Q
�AB���.���&�C.���b�����y������p[��#� """"�J����m����{^!�����l,�pRV����bT���"""5�c��N ��rtG�U������X#;������\=��������e����m���������jz}���@v��t::111�c�|������%%�������0���{#����	l������\6�H+Y��������2�����]�r���"������DDDDu�q��.���2��.7�l!���H��z@U���U���I��EQ�.�Q8�_����n��sx����R��5������s���Yc?h`���ys�]0��kEV�M8��r�&���opY�����_S �]�O��u�l���^>�����Z���TW\(���a[_Wg��:��F��#���=�y�jcR��>jo������>vM[����6m
oof�������5��ND5#����=-��
��!{�c��;���LYY���{��g���������H4h���KdM��V��y\�����=�5�����Tp�m���KEEE�c�����c:&�����k\qqq�������+�BJJ����������`a�z��8
��,�Q4���8�s��L<��n�������bp��f����]��c�Y�7�M*;���R�`c��*�*�kE~A_��QT�Dv�[��1�����op����tx�z���i��EU����S��m�UPZ;�����rg��!��RE���x�=b�7�x��o�����7����w8Q5`!��+�����[�������A��W�n�����UM��E���>���o�?�,��d��/��
X�26�Z�7ov(`��#GV
X>�������]+�V���<`qG�+,2����KQ������9E�/,G���1C���1C+u���������������h6�.���v_>�,���A���!&���������o�9?>%+u^!�:���";U�z2/~������c�\7�n�RV�ILL��}�d�.::�4"D���������������y((6���*)	A^�������{a,�)����&�bx���E�K��3N���"]Ty��j����**����7n���jc%�a��Hm���x��wv����V����i��BDX�[��'�{���������DT3���������p�������m�mA��|YU&����^hQ�XR��9���i#�����>J_��~��O���&v�<x����7-w�����F�X��p\������:���"-��q!�����#Y��E�#��Y�5l-;Uw*�hr��Av�]6�)�2VV���wJzz������G��}e�<�;~����\�/i�3yc���0w��H�vZ?4���������BlSZ;�+n`���+,�m�����r7K���������x�Q�@��7�x���9��y�����l�C��h��W�^��O��O+�U�
#[ ��������9��=�FD����*,w�d���������~����O�����[Y��x���v�{7�<`�����K�:����S����]����yc�*G#-�i�����	���7�>�L"�=�p�������U��Q,�"��
������C]}l""""OUj��q������y��d��lCf/I����`_L���DD��r�
�](�%��=�&""""������~/+u�G"��O�.;UWXT��J��c���lp�HL�A����?�%K��������g���������7��e����������x:t%v���=���{�=��A�m����|Bv��6a��E�WU�[A5�#&���y�-���"L���,C(���*=N]}l"""�����w��@V�D��:�Y�����m4�L�z_�9�rNW�������-�Oi�9�=�����N��J�\�F��{w��)Y�R��]������q��	�999��n��h�����1f��UOa�����!��4����~?���o�{w�P��U���B��o���
�=DT�p5�
,����;Z�����xL�9�Z�qV���^5b����e���>�'u��k�
Qu����o��������N���c����I &
i&+""r����X'��!����a����������$�������}�?�v]6]V���7I������u�	�S�
X����^+(P�(D��t:t(bbbL������	�?��T��.�'n�0/5M�e�I�ad5���O�?��|+e��|<�p]��m�-�<�,�@,(X^a!v����r��X@7q�XHP
6X.RT'G�&�\eb��Pc9:���I]}l""""OS��O%����W5�b�T}L�p������\�j�*yTN�s����a�� ��EO�����F�X^�BDDDD�):���/�AQ�.�Q8�_�����??�����r�5���u�t��`��5�R��#G�y���:��&|�r*Nf5���n��>�{��������
����f��EJ~gS�&��O^��:��"����"q��R�@,,X�(���9���Z�i�j�`�t�t�����Z���E�o�5���RW���������B���:����v�����de�[\F�l$+""r���@m-�r�C�7�����9��R��y�������^������,+����,��~N.4k��nk��`_�q�]�va����R�Q�F�a����/�'k��Ph���]��#-��$ip��N���c�������a:L���5	�"��p���3��Du�~`��X��7C�r��x���rqD\��X}�mBm�����MDDD������p�rYY���\U���v��],����V��Qbm�cB�����.������}(5d��
�Bn�?�{\&������}�qY)k���t@����~�7o��;d�.**��		1���f�[�6�������=0��=@Z��O+���� � Vv�5���KZ!��v_"�Cn"���-�������x	�](*���z� ����<-�8Q������"�`����*����5���MDDD���[�w��zV���5�����:�[C�j.+""��������=b
E�DDDDUS|�r~|
�����:��F����";�3{�Q�o�1Y)���S:���;7$&&b��}�Rm���g�������������0d=:�}(���p����8Y�Rv�5	���q���0hBD��*���^$^���/�+����!v;�-/����eX����a���=�\�RW�&�\�%%%��!C����+"�<���.>>�?����������)����1b�����m����[z~�Xw����j����h���<"���[���O��j�Xg���E8�~����"��XC���1UErr2���M�qqq��t�c���������r��`ll�f����/��~(+�tQ���
�����mU:>������?�_��zjt�Caa!��Y���t�Q'����k:.*.�7_���DSmK��y������sQ��+�:�>2�#eGY�=�5�%�B�������/\k�Mi��!2'�3f�p(T �bgB""tb9�U������������!"�� �b���_����;��OtwYi��u'��O���v���x�J��u�v<�������!�_'-�yEZ�������aX�!�O���������f�+���78�N�J�S�c`g�W������8{��������u�f:>�{�lxG�������n	�Qva}����o�����P�Q�:�������0dAZ�Z�mJk|��L�����q3'jq_�6��,@U�o���]	*���&"""�����B|Zv����0{i�<�����
#[����������)��Ar�<�p����H�N�����Y�v�Q\��F�O��V�������t�R�={���8tv���q�C�Sqk�v���nC�L��n�"�q��r���j�dATOp'"���Q�+J���������I/���e�����i���TYY�yL4nk{&j]���&p'"rw� -p���W�-v0���e��1��W"�������������X��������q�0���������9


dG��
�_�m�~����|%�����1"@y����x+�������m�$��n� }�~�p'���l�NDDDDD�V�v��
����Efnf/9*+k
B�0yTsY�b4H���8��B����X����p'}o�S�{&��R��h4"##����C��{�nl�����;w.�-[f7`���0�w������U�X��.|�`�������M9#�����,:5��q��� ���;Y���a���h�a�b$�;q'"���Q�*�>��7������)����������?������q�l�6W�T7^]@5�;Y�=�f�r�������SJ�w� -p���W�w�8P|&�e�`�b�q�_��q?t�U��1+����0�\q�\��:�����i��� ���t���G^^^�c�_QW���������.��A����;S���k�e�Y��.���������T�>{�;����uo��}
t��.�N��5���v��E�������}�= wc�����,���������CYY�Ox7h!+�9i�����Zl� |�DwY�}<������7n.\(+��9s&�N�*+��� -��p��W������h]T<G����d�5�rL�
�p��c�O1}�1�w�wskxB��;���
��9.�����.�dl&+u�t��%d;�t��c���f���'���{�����;��!���lSZ;�+U�U�������.�5*����j	Xs���	n��""Mh� """��
]
2��=�c��v�������oo�<'
�
1��Y���&wK���?o��]�����^�=r���9����,�����pU�{1���h�T1`�S���2{;���w�m��MZMGJ��x�����E��P�,���`��EXQ}V�kJ2��s���BikGJ�l>-+k=��cX�F�"""wa����������"��i���8�O%��}�.���z��8'�X���I��3�����a��y�c�6�����hS�_�W��
�*`�5zM��eG��������"eG]�c�;d3����:�<������c���e�z$[�����axhD�\xBD5��B\$f�N�6MV���k���=J��q!DTp\Q����~&��Ue��6{�YiK\ackh���LAO�-�&p\YR���1			�r�D<���8�����i�����	y?�\�����<�81���|=�Y�ec@�9YU������uY�K*l�o���Pj�o���pi��@�����s���o[N�������m�q��;f���BH\k�M���!Y~19/�j;�,��>`���z�'!�����x��������j�Y��u���
�����NV��'�T� sJ���FJ� -��p��W�G^/������t��8���#�����e�9���3+��v=�����%�/8,;�'�/#"_��w��([�����,+���� �N�U���,����g��}#;D`�����C��5���v�W��""""�/�[~�G��|�����Q!s���#e7����DD�A\xBDDDD��
���'�
X��� ���t�����9�{���}����E�&����X�29��"��F��m���>��Xd�u(`���������w"3�>���9�����"�=�p.&Q}Qj��q�z�B,��d��y�'p -GV���1M�dEDDZ��'DDDD�3����LB����c�
<�-_�*�8~AA�i>�{W?�������y������t�����
�a��m����'�����}{t���4~n��?~<���\w�u�8q"F��������|e��v��ymd�.2�	<��0�gr
���t�<�0vS_o������}��Md����,������a�],������],����m�b����Fq""""""�=�h�����a����1d�{�G��&��m�������)�o�O+�!7�|�3|t^�,�	>y�;f��	�`�����}�|eb����������Mc�z����;�u��h��	������[�������pGQ�7�����-eG]ld'<8�?�n�������4����S�us��x��6�����Q}������+�������[�X����i;Yi��%Gq:�(+k�G�D�?Y���r""""r�O�q~y��� a�� w��}�f������>�������s�7
�]Z���������|��m���B�t��W_}���	��E�rYX;[����a�!Pv�uo1��h��e���M!qA������'����""�Gv��>a��E"mWa��e�������s�_��S�������.��1g��.
��0y$w� "��X,7�z�jyDDDDD�w���?����Kd�>]�6���i4���� ������������0��M8���_=��o������-[�`��=�R��kW�����	��U�E���!8��nC����}����<�X��V��t��n��ZD����xrl4�D�t��b��E��F.\�""""�x�v�?~�F�J;s�����DV�n���^�"""���9S3f��GDDDD$�_���E���d�^��A�������c6&�������s.�������v����c/6m����$Y�#A:w�,+7I~8��,�m+h��3�"�P6l����o5�8���N��y����qC�(�~U����.�gYT��U��0x�`-�����c�IE�^��9�=/�G�9|"��J�����A�0�����HK����Q����q�����������=����#�e���hI���?��
R!�H6^�f���8B���E�����k��>:X�S��������F�H�:���YX[��_gw��e]��I���'N������������������CD��E���1+��5k�������<�q��.�����Bf�"L�RQu�>}�<���(DDDT�OB�/�F�G����F����;���:.��/}���QB��P<5�-�����dD�������k���>2�B�����}{Y�����C����
Ck���IV���������vuK��~Fv��U^���4A����)DD��G������R�� D���y��,�GD��r�J���g:�oof����<x������x��#r���
G�A�j}��4�Yic��L�lY5�������/�x�����m+�����e=���<���n"\!B���."�������YQQ��X|O}|�U�x���������8�t:�1QU�yE�Pj�B��w����`B�����.���)���w!�����z5����L!G�Y�����R��o_��[���s�,�-�����3"�5
n�����������GCS��6JII���Ull,��"��Z�mJkYT�R���= wc�����,��!v����%YY���Dw��6�}�[�g���[tqx�����/��,H�R���f��iGBuC��NZ����A�)�q����� p�}��h!;�+)��u���������-�����h��c�%1*�����R�����nu~�C=��gn;,�o-+u�]�v����S��������0�S���� -p��6����PZ,�����j�f�Q!>-�i��g����=���Qm�E������.��?�n��b4H�i4�������,��h����������?,������Z�~���P�"��GNNq*`1�k$���M�XQ���E0`ADDDD��(u�����5�����v~���<R6yTKyDDD�e��YX�1������2]�v{���*;�{���X������n�gnj'+��N.+V���c����;U����N1�\S��d�Fe��tEb��s�<�d�^++����b�Uq��o�}��)9��B\$�M�&�����"�WU�=J��q!DTp\�����<�����2��DL[*+mlL��3�����+�4�}����~��T8.�,)}����#FTy]�k"���BH�@Z���\U�gr�����6j����u���O���+l!�G�`����w�y\XXhz�������N����E�v��Rt�>��&��z�����Y�Xv���!�0FV���
4���"Hv��!-p��6��_�,\d�%�XHX�`���j�,��>`����J�� �
�
��S8�!Yi��/�b��s��L�������������_�	Y�9���bv7�d�!�_'-�yE�(>��������|��V���/�U��U�=8{I��;UV��5���vF����f4M��>}Zv��5��C��i����.e{��E�rY_d(����=p������+`�"+u
�|L����#d��a�����&�������,��d������<�q���H����H�rT�������Qm���(���]=� ""�������_����>�����r��k��
X������w8`a0L#B�,|}}1|�p
ev_���,�����
X�� �,���o_��N,���`��
�b��1nVY�u	]��y�U17��<R61��<""��4u�TyDDDDT?��2���e�,o�(��(+�-�z���"+e�"��ok��f����������q��Y�Q���1l�0DEE����8��,.:Y���z�Ha����������R6�M8���
���:o�����\��Y)��%�������.�3��{��<��� 2�OVDDDDDDD�#o�L�_-+e��FA���r����x����������-�~(A���6,222dGY@@�)`��Q#�q��'������E��8��)haKi�2�����]v�ul�g���=C��q����C.;v�<"""""�<�v��E��o����w� "���Q������� +e�n�0�^Y�n�����>Y�{��x�b�� �"`���%;����L#B"##e����
��G�� b���QVR���{QP�Av*��{�5�3�[�CS�v� "rC.1b�<�-[&����������$&���5�w�(*.��H��qn�JDD�7h� yTn�j�Wry��=����-Y)������^��������_'�`,�ewOl�K�96����sX�brrrdGYHH�)`.;n��p�YY\����iC��'�� 3�^���E~>^��wc�h�A����AD�*�,\d>ot���\P """"�a�����������,D�"�P$+k��������9S3f��GDDDD���xr�.+e�aQ��Y�.3�/}��4F��F��5�����3g��yyy��,,,��

�7:�p�~Y\�������x�����f����������]"���mpy���CD�-�,��|Aa���ZQ�Wj���/�Yi��.[��w�YQM���d��q�""""�,������9������+_�wxSY����/����������/m%+�N�:e
X�����#""L���`�q����=������-�]NWY�+,j���{P\�Hv�
k�7����~Q���]""�1dQbAa����*Z��5KVDDDDDu�)`QT +kZ�b�|�=����],��j�U�V���A^�BDDD�F�`Q|*YV���x�1�e�������3e�lx�Fx��8Y�v��	S���@�<_���4,e���7����Es�����N�RWP������4Dv�~1��qE��Q�~�KDT}��;�����Z�������S,&��_�1��_g�_B�+W�DII��X�t:������<x������xx{3OJ�����Q]<����[>��6�`'v�d���f�����^������/���m��#"myyYo��	k��yU��u���DL�6�tlN����&����A����2�<)**&��>>����.99�������8�q�[�yEjr���q��R0�n��WV���\�=k����?��
'e�L�����ee[zzz���4j�C����a�A`�( ?U6��3v�wC��s@v�� +g���.��0�[Ctl$;����+"Gp��6���,\���t'~��� ���!��)��
9�="+k����_���r��20�����v�e1�zh3Y�_<�������x*+�E�g��'��!�_'-�yEJ��C���d�L�m<��zYV�9�b�'s����R��e0f���z�����4�k�4i�C���wt����E�6S�^������b���a��gK���so0�5
��n���:�T�gY���d���_y """""�����5]��,�y�'���?G�Q�(���n���Uw���3f/9j7`���?����C�#G�8�h���
�����%��kL���F������:��
X�����p��fx��,��Va�������P|&����F��ry���yX�����M�z_������'!��ge��;�1���!+��M<�/�>"+e��>�����8B/;�>\i���-Z������[���>�G/�����=��k���3������`�5��&\�CDT{p\�����Z����n�V����������"��-z����������)+�{��������}�l/4i�/���[8RM��2'�e�~			��T�q\i�cH|^Q���ld��v�J�e�S>�OLoY)�7�a��3x��}�R&�\}����&;������[��J]�V�0p�@Yi`���ko�i4HvqD�'Q���/���>�B��R�i.��:o�����BH\k�Mi�@t� �� ���!"��m8J
���L��2MzAV���S�k�����D��1������'�T� "W0dAZ����>��B����`�*Y)���{^&+u�^��/Oj?t v����7n��d�.&&����X�_�Mn���-M�����_�����v#�xB����-Y���d���_y """"���[~W
X�^W�#m��Qm��)F�i)w�L���aw;������IDAT��%�H6^�:IV��6�n����+V�p(`!DZ,��da����oR����.���8����C����� �:�!""""�z���wyd��e7�Dw��6D�BM��h"+"""""""��O���?�J���x��WV�9v�������|�5wOl�K�E�J��s��x�b������6m��_�~�r�x��C�`Yg��.��;������;";�n��F�/+"���!""""�z�(u�����5�����6�%����YY���],������H;�{�#o�[�R���;��|YV���-4�`q:�(;�n��k.+eG�1,23�w����];���GV�a�H9m��r>�4��|����(�����������!"�;� """"��l�b��U���������6����SY�W��$d�����y�6F�U3d����R���>���e�B��/m%+e{������Q\l{7�K�.������n��p���s��B��!;���Bq��W��I_�!"�[� �������g%�g`�6OV��=��G�X������	M��{��g#�����|�Q|�x�7��k^�� ���������Uq�R�q�Fl��UV�
0��p��p��*�
A��a!�����32�	��b#;�Q�������������FDDDD�5��],�C�Q��0����=����o��YVk�gN�m�y�����������t�J����I/��uoY����H�uNV�z��37�������X��u���5jbbbd�u��%�������M7/��W�4�5��
Z����vY�S�F��u	]dKY���CY�r@�*�bADDDDDDZ��?�V�JY�������_��`�>�;;�k��om/+k����������~�B���c�����V��p��.
����	ELEx��.��n��
z
����Q���Q=QZZ*�������]�P���@����.sO�#k>:/�Z"""""""w�O���?�J���x��OV��kS6�nW�Q�YCS�"P�����9b
Xdf�5"��+�AAA������x���J�
����CH�w�ao!(p�|�vv���.���*B�#d���nc�����������.�
|����;m��-�eeM�b�#+"""""""�(��y���2�V�t���r��?��sd�,4����=G�e���{�b���(..�u]�t��d���p���������mGp��h���Zv�#��[��m�sR���@�;��}d����c�B�����fI�m�y#""""�JQz
����ZM�b!p"��3u�T�5
�3��6��Y�{DDDD�P|br~~VV��C#���r^��l<��=�k�I�Q���e��"����7n���[ee�W���+�����e8��"B�A����5����_ ��>�FcJ��dED�9� """"�g�[�w����C�S��E���f�bx�Fh��W�����g#���@a��(��ex�7����e��_���;;�a�y�U��-��-.LV`��8p�������#McB�����x��O�����S�0�C>A��R�������A�1����""�,Y�#��\�!����.� E�3[���[�/�Q|*YV��&���>�r��%i���M�gg��
�]�A]#eu��s��x�b��������c�����������c�����������`���a,�
~��/�stWD6�u=�U��""����1���FDDDD�)L��YY��Q!����%6TVDDT�&BDDD�&w�L�[%+e�����e�r����q��[����(�q1���'��%���G�1,233eG]ll,F��� �Q#BnA�[�����W��W������kP�l�V����C�&}qY��������o���8y/�gb�B����/�f�p}�Qmc�����o�������-�p
�2����],��j^bbb�u-����.���y�� +e~�.E���de���L<��.�>{?��6��}7�l�k�5��E{��5��U\\,;��t�����������x���>�Z|��$���9������7c��@���u�|������
-���[y6�,����������ln���.�����M1�[CY����Zd}y7�����2�V�|�c��8��7�w�����q����w���������6n���[�����BJ���������f ��N�u�&>9�S����K�H�z\��Y��V�Q���U"���f����W���q�L7q�������l}��|���|l"""��dk]���4ZV��~�y�;�#+k��#"""�
b�Am-�;�)+J���9�#��Pth��*�m�P����z�Q���f,�xJv3�c^�1W�|�[PP�+V�����.00#G�DLL��\��?o{o/{Nl����6���8�����+1-l
�
������*�4�M�5Q������sq�.��6m��^�p�B�M�'N��9�Wz<w��>�����@���MDDDT�����p�rYY���\i��.���BDDTF�/�u�� �,U�E��
��CDDD���F�o�F�����������^�wx3Y)�{�I���F|����8�_�<uuC\?8��/(=w�/^���t�Q��qc�=QQQ�s���?����bM���:��������,p�[�#V�����?�H��H��?Q�eADT?1dA���89W:yW#��6\5!�J��9���cU7��],}O�F��;���{���������M����<�3��X��Z�w%������|�J���]��'���>���y_}'��� Ng��}1M��-�0����������9b
Xdf�7�Q�F!((Hv�m>��-��v}��b�?6K�%1h~���<����=cx�a4���o����@��eADT1dQ�U,��;�V�Bii�����3M}s�-,�������������x{Gv��������j��Q!���+8RV�7w�	y���XQ}'���b��|B�K(�E0hADD�Qi�yK?D���"��uL����Wv����<���><��n�>d#l`!8��^�O���!��ne{��5��P\\,;��t�������\��]�h���n�,��N�]��D��?��;0 ����<:�t:5�������u9Q6���7q�Xi��E���>�2V���~qx(��x{����2�a��b��bA`��A���������~����]SV�\�1<���!C����y��^�Y��uD���"���de-��/��]V�u*��_V��;a@<ru�������G@��m���,��OX;����*����)��<��)�-���[�������SeU{������t,>�1QU$''_x�2..:��tLT|^�-�k��a��(���D%����n����J���#�q�1�q��C���1-P��\JJ�������7=�1`�������}�Naa��X�r ���PX���6����.�e����
��.�7��V�t�.T���Wb��}E���&�����C=&Nt�	X���rG[�7$&&��r�Z���7m->�������Y��&"""�	�v��i�M���07��X�2c�yT�^�B�>}�<*���""�����Wd�=y��������/����kN��W69��%?��]c�� �;p(`��#G^X���`������[�X��l�������qE�xwrL���<`aLv�����������,�1���	�� ��e���5��,�Ua�x`o�A�o���Z|��������[Q��!+k��'�#�+(*�9*D,H�6�<[���������:� ��rt��������Z���&#��(>�\B��J�=�'������[n��s���mx��dd���}�Z���;:����G��s���\$%%!3�~ �q��=z4���L�U���6,���S;�t@F�#���%%���W���
�p�<`���V`���<`:P6���Ct�#'��2_(1b�<��E�E5�����Z>6QM����WP��Zw����-,�p""����vBDD�	
$"������4O�]��u���f#����5h!����s��WIx���8��+��E����+c���]��c��Z��������{�n�U'F>�5
AAA�j+�[����m��G7EV�-����E��#:3����rw�,��|=�[�,B���D�"p�}���\�q�A���%1���C8�}7���,�������LQG��t�0�������J�J�|���r�J������oof����<x������x���z�$�2�#+k��� p�C�r��^��cg��:���������9�T���z����~^Uay^,�����]�cM��k'�B���3�	UY��.����.>6�'w[�������8�t:�1QU�yU���
��Da�?��8��(;����-;e����i�mU��8���q������?7�9���T=zTv������v*;
��fc�Q�������#�0y��Lu����i�a�B �[����4�P����� ��lPm���r��+���W�\k�Mi�@t������n��{`�5����;�!Z>vMc�����,�*3��_�~(+ka��]dKY���-����O�,=}c[���HV���RMP:O���O�������&��j#�,������>�j���)�_�%������|Zt���)�k_6����c������������D�F�S��S�.�+�F��:f��h��~����]��#�0������%C����>�����6k��r����A��Nq�A�
C��5���v�W�i����V�������8��+����&v�}G,[�\���?���cU7[�B�:��,`!�]s\Yk���""�*�\��m"""G�d�B����|�j������%����b�b��������d�a�b�����d�@�2`����]�va���X�d���Ig���9r$�Kw����9�((�����#'o�)`�,���i��7SXd�v^b?`�����""0dAN3��BHHH�G�DA)��*]#��}���o[�*������cU��]�P�����������������61��<""""W��w�a��Rc.K?@�[�"������?
a�}�	�{��S��u��v�D����8\����7����6\v���B��'"�8o�<�������^�5n���`��W������^�c_QQKdf����)(*nn�M��Y���gt���r~Iy��(C6T���.�������0dAN���B���])a�'�����@���.��6�m3PV�77Q}� &&4���r�����#""�����kd�9�>G���/�@�|���}��d��o��3��v�p��p��h|7�7.�%�@ZZ�i���������'�=�5�oG��o0!�I\��!�m�<z6�G����*���'�����`d�^��Y�BAagS�}�@�{bk\����j��_��b;!�������ADD�0da��y��}��D��rgO��\U1#��s=�+.D ���!DDDDTYQz
�������X���jYb?_�������q�:u���I��0�r�
!"�����d�5�E��4?Kv�?��?>C����+W�<�;fm�7���c��m�o���[��4�-��=��[����~���+q��a��(����7��v���^��faU�>9����\�V��3���g�o,�]�]����/
�'�F��@SO�������R;#R"F�,|.��ADD�qu������x�/�0#J,�G�,X`z<sJcE�j������|l""""g���b������v!�������.DDD��XbM�����+���^�����m�D��J��k.�<
��0��(�t>^�f^�*	GNd��m�������um�/���{M�W��Zql08�X��;C"�F��_e�'�^>?����e�1���8�1
���(-��z}�����qInw`k?`W�������������r��
��dz�"/��
x�k�"�,�.����J���%"H!��b�
��wU�x<�-86�����9b�����Xx�����e`������QhQ�8DDDD�Q
X��""��.o��!{��(>�_v��2���A�e���\v+�n�QLy}3Vn?#;��h��nl������sX�b���������o��&����9���d�w6�	�=�=����#
�Z#3�d������Q >�po�Sx����s0�Y���??���5����������
��|��G6���^e7�=�����y���$��}�"@-`������/X>���#H�w�����ib[�9��Y�fV_g""O`���?�����;A������	3Q��4���)���<uUD7�NU���u���h�����:)=�j�9�+<���
�s[G.� ��,j��F�+�oS��iSx{�z/�:�������.����>'���c
�uG~�+P����X�v���rq���qf����}�1 ������by���D,F���d,�o�?s���1������|c�)72����[��vn��-9��x�������"-�?���m+�����k�c�zP���P�9dQ�r��r��*����|�k��5�<d!6������3��}>�i����iWd�|FV�%��y����1���a�s����z[W"g����Q�S:���O����!����,��E�&M� "�G��+��s��Pv�+l������E/��v*�s7�bK�Qv3�m)��6�������U�����a�������d�%�V�e��<�h�����eZ��p]���5p���GV�e8������1daMi��g6�����#�Q���DyDDDDD������m*���N����,����9�.,����
��9B�~�p��8�%�>��q/�X��%/���S�-�����	9���'��Gll,t�a���y%��,��S���>8��,r
��UWF~�W��r{�"3�*,���D�.�.��N�(�q'���3gb�������U3��������&�|��f������"�D���~�����g�;9�{�{���2]�}�Y��]����y���]�`��HWYQU8p@m���G�wc�9�����N����/��O�S���,�
��KII����x�S\MNTU|^�GQ�.��{�����m�aQ������ekv��W� ���9����"L�a�gd�ub7�V�Z�n�������u�y/p�(��t���`����(y��(,���r�M�AZ����5�������C�]��*&&�_�[��5q'kJk�c�z����/T}Y(,�{A���NU����Z>vM3Y2�/<�G:x����u����YG�J��Td�;IV�G=�!w���V�8���N���'�����Q������<��/UO
#x��UY����]���C�y��D����p�����|^U]��9����]�� �����S_�9v&_����;���W�pLV�	

2�*Z�nm������}`��Q��r�����TqIC�F�h�#;��}�q]�0���
�����Z�e��rB����wdAu��U�}%Ba�����kM�)������q"-n�f9N�Q����h��DDDDZ2n�])���B�������ZT��"""��O	XQ�PZ�����;��5l���>A�-�X|��(n{m�S�~���g�Y�"\#^�>|8.��rt���b�"/	�:�R�by~k|�����EAQ��x�*`10>������M��6@����@�������}���q0(����O�}s�����O����@� �C�'V��^�>h�1DD`��L����( n���a~��`�1X�-W��"!!A]��ci��Y=d��q��#e�^;R������4��#"""��""�k
��#��`�������m<B���������<���������h�)	y������u\��M1`�\s�5����������-es�Sy���8w
��mg���������dU�Q�/��
k�A���]��0��`h1�?����8������!r{�B�,��!f��)��-[�L�6c�yT���O��&"""r��E�A=���U3�X��xcbBY�,����1��9��������m���B�U/��/@v*;v��W����J��y�k��^E���X���i�qLDD�i�
�c���"&&Fy������7�PP��Y���cc��IH/��o���$���#��[v����o\�~1���������}�P ��V�<�dV��`��wUC��2���2���#�y�B|���K�~���VxC��&"""���],|Zv�OtwY�W�iVlU��U,�9����.;<����"��a�kXQmT�u
��?
���c3l�i���~�~�������m�o��m��)1�	wb4����]�v� �%�\��;"((H�k�X��;/��=����Y���z|}���]?������!#�!���8��o���7��7CDDuC��X��;X����
B����QC�����c��b1B-!�����yh��DDDD�V��EGw�����X��G�.�Q!DDD�X������	""���`�2d}|#
����D�B,D�B��]�p�[�����	�/��A�:5�u���8q"z����H��4�.����6O�[��Gb�x���Dx���N��Rg4�AF�()�0��~������b���J������+-?�H\�@�O�ba�'��;P8J��_)�P�o�����	�F�X~���������&&&�|5Z>vMY�re�u%��!C�(o}FDT�<x������x���z!���a�6OV�yE b�RY�W����{����F�l��nl++r�����#�m[~��z���,y����~^T�X>���+�E�m��~RTTd:�������*���Q\\l:����N�����������]V}%+��H��	OC�}��T�~&_������
���Bxx8�v��-Z����9�X����I�q8+Lv��/GP�\Y��k���������7:6�B�H/����� �EJJ������"w�Z�mJk���E=�lp��sF)��{�
��_�#B�n���c��,��>`������3�xc������������,M������=��4�/[���jCD�pv����x�C��NZ��J]������*
m��|bz#h�3�5l-;�}��(�Z���B��tor���+��y��u3�Q#.Xw(�����h��V��kde[v�d�4�qa��i��b8�C��5���v�WHS��^&D�������W^�P��Wx��s&��c��q���H��g��
��6�"""""�:��m.2?�������N�T1`�n�9�%F�8��/*
��h����c��lU:��v>^qL1`�������X�F�k�g��e�����1�}����'�O�zJ���."0QX!
����O�1\	*� �fT<����v�j-���������C~GB�RV�����8�a����	M����],����-w�k����@���_��H�����N��.:������'�d��I=1�������c�
2t�P����`�-w�\>~�t
��x�����e�XT��%�Q���?�����ih'<�;��0M��d���<���BD���P}R�kr~|JV�Bn~�m����{;�)_E�,?�]V�n�H5��B��BZ�X��W��B���P�n?` ��f��Pv.��-�[?���ge����%���<
�4d��,,�uN:����@H����,�u}�Gar�'de�cH|^���d��y[�X���h�X���j�B���TQ]b\��>����E��<�-������x���,����� �"�:�P
��X.^��-Ms8`����_9���&�""�lY�1E�I(L^'+k�^W�#���x\Yk����dEDDDDDDuAiAr����3e�6��h���1��!;����1<��.?�/;�M�Y�+;�;=D��l9$��o/>���b�q����������M�+�u�IVDDT1dADDDDT����b������6!��#����!+k��Q�R���M�q�_�c�_�Kv�l��������K1k�|��a��M��{�Y�{��*F�\z��h�����2����c����95[v����CX�g���u�����Y�1!DDT�1dADDDDT��sm�,D���/@V�57��<�&�&&4��v���#��{Qr����8~���Qv�(;�;���o;o:%;�B����k���A:u�$;���B|Sv�:��d�>�)����Ex�����?%�a<<����v�""��� """"�CL�"���Z�
9q.�6�/���Ex�����������*�>�����������Y���-��]';�����)`���+;�����U���w~�y���@��i��C��h��M����+�6�u�jti��vKFB��>���4��������#���h�9���k�7"��:��P�����!o "���Q}�Uv+-?�����EDu���+QRRb:������""�s���?�������<R�����T��*����[[$s�g�����[�~�d�nb}5��������m[yD�-�[�%OX;������X�z5���L��
�������*���Q\\l:����N�3UE}z^�]�����$�������ZNxJV���5�9.+u�bq}�lD����WaY�����������DNI��8F�e������k���k��7a\��)))~����� ��W��5���v�W����������T�V�X��2�[C,������j9���=�q�>zMzQ5`q������n��y����w������%vft-`�<�j<vx��y������/�N�(��p�br�'\X�gc��������0n�]Y�5h�N�e�^"`a0�_��D�
!"""""�����S��{]V����F��s��1Qv*[��4����}8[v�5
2bd�i\�q.m����f}�8<{�g|~�E�*l)���3�j=�7xsrc��-�Q��eBDD��!""""�:��L�i[W5����#����E��Pt���6y����ee[���:�S�����>���W�����/���g0��	�78���+��#o ^I���x�����~!0#�j����0ze�����S�H��.�a<<����v�"""kb����L�?j��Y����%$$���y�D��r�J�������8?����;x����u�����f��<G��w���kYY���^���r�%�Oc���s-=sS[�a����s2�&(�?x����~^Z[�z5e�~�"\���s�bf��~rf8�Crr2���_�����N�3U�'?�r����d����|2h�3��0\v*;�a��?�����,��]�d�c���[k�4�o����w��swbs�����+^�����Bk��h�
z�)d���DQ0N�94�D�\!F��KJJ��������=Hn��i�kM�)�0d���|�z����..PU0dAD�C����6��LYU��q�&� +�z����s8KV��l�/���U��RM`����O�M�&+m�;#F����Se���,HY�<�yUZ��������`�_����4�C�Ne����[?��,�)|�K��I�)`�m��U����6h���^�?����Y�;5��'t^Y��Z�te7����U1��MWvs'�NZ������&�����C .l:1��g�>����?�����������*nmkmm�_�����[�u�i�u����.TV��+{_��{���\��������������q��������x����3`a��:�����Uy>���^v@h��?��.2e�;R��M���:��E��)���},�yw�f��[���=
4m�f�������7�,J������U���I�az�|���{�f�9_+�Q���T��3���LuN�Wi��+9�%&��������R�Q���������0�+���7B�A4Wc�~���y��w���i����t�yJN������ �L���
sn[���DU[��3�%L���O��V�*.�����Rs��9(�k���E���S\tmG�:��?�Q�O���u�i�O�M�{�n�7Is����}C���z�(Q�j6=<����n�n��6!4���]H�;�����v�u�������[[M�-��`���f�����um������v!��� UmX���]e+O���F�G��R
��[��������KN�����VhI�pDk`���[�5k������O��[1��S��R�&bx{\s�y���
m�� �.�.����e�-Bj����oI����#������-������]�v���N���@]<�Z�����n�ST���/)���J���m��:B�s�:%x��l��P�y�P`��?ok�,��\EQ���1'��{�z�Xs2��`����������Q���V����P���i���
��_X�7�����g~=Z]Scl����/Z!xc�,�/<	��o��>�}��������B��B!�W_�W�������X%]8G�Y���.���_��U�>)��3_��������b���wJ��bgj�/����L[�
�s��"$�x3���
��Z���x����$�1������>��y,�xP�95�i�u���I*�[u�n�#��s�(��T�
X�vl���,���	�~�����S����a������(`��C�?|�k��z����[sH��[b�N���S���0�;=��x���6���0]p��[$`o�,��~����l�U�c�/��3�(f��iG��ZlM��Y���_�#�B���t�yw�����Q��K4���{��\������_|j�����_��V�9zf)���+��;s��-���O_i��;]uj\����3���i%�9�I{R��>�U���FjQ�[�VLT��&�Rz��ud�	��y��<�J�v ?w�f����������?i����G�t�,�����\Y�T��o
��������3�!��a'�������Y]��>�f+Oc���>��tD����[x�O�c��Iv�;@Q�>>�J��W���o���]��.���Ao�S���K��\��h����W��M�]��=�������������(�����o(<q�)O���/j�Y�������'�Q�s���S�4���$eu?J=S(>:�>*�G�"�_M�T�$���S�>\��*V�������;*t],
Kj���fjvO;�Vvv�5���+�[�����'��+zq���=c+�b?E�?x\��v��G^�����Q��ut�<M�Y���[�/������}��z�p����jg|�0���UQ9H�G��l��K�TB�t��_i�	O��px�Gr%��BMT?��~5�\��X8zV���m\��
: Y��d+@G�ph����ij*JT���T���v�����+�B��S[w���GV����4�G��
��#z�+2��!��7��.�hl�J�#��hc�g��]Q�����N�^���+�s��8�r�y�E:c���i�1�'m!����h�_��UUm]���e���.��[w��o�.�z�����W�rM���'�H�So��Ay}��{�K��o����ht�}�����f�a}tr�'YS��WUv�#���:�U�SXt�J�&����t����s~��
n8�@[A���X�`���+_���EDt�b��V!���S���:���� x�,YbG����*��OT��;�[��_*��kmU���Zza�>�l�N�I����X�[Ez������:K#��UD�����+z���cTZ��}���Q~�U�?���zX�D����:��������EM�<���3f4{A���#����A_�@�VS^�7da1��
�/s
�����������egg�Q�)S��Q��o?����������L�����D�������Cq�����������~��V����;�%����������N��1c���Vi���%�����#\c��S�_�SUTs�WO���� Gc�'�
!d�D�&M��Z�f����s_�p�:��O��E���Pm��[��S\L��:�����q����Y�h��'����+��?�������z�
��F5�Ev���X%_��b�<�N�^���TL�>M����^�z���Nr�6w��I��Gi�C����d������?Ngo�^�����������l
h�Y4��fA�)Wn��qOL�>���3g����@�V���.��c��9�V��aG�}��V�L���{�����7:ha�o>����g���	bn�?z^E/�j+�"Sz(��'�u�u��{��v�Wr�&����4�k���0a�N<�D����vr��R�M��+%�i^i�k�OEU���LNg7�3A��:HC{%�[h�X)m"s2�~�o�-*�������m@�}��1��U�^,��[y
U��K���wS�{�����gG��D ��u����0�/j1�uL_A�I��O���{l���g�������p���Wl�[�n�:)c�"��#%%Ec��������}�����>����5|�h��p��)�8B����&Q���4�����mo�}3/�5���jj<�����D}�B��o���H�+��m1�g��E���v�M
92�����v����233�]�v���_����lu(G��J��5[O~q�.��'�������c�k���m��`���v$eee�Z��w��v�_WK���������/>�C���w��.�3���r���,**�5�#''GN��5������p���hk���7�U��gl�[T�h%]0G�	��o�������0:�W���;�]����a��i�/���I_�Wy�J=Yt����jo���|�
�������utD���^���yDP��B(�������yh&v��hP�,��4&8A� �8wo��0bG�mG�e�X�
Xt��b�47�_]1
N�.,�����
X�~�R~��+`����H������
,�t��s�9�{����r�+��#��
X���| `q��>6`o�,�������|l �	���x���|��v�]��-�U�1C�4t@���T��w�?uk�,�>�j*JT���T��[v�����+����$L������X��UqQ�:)c�Rbk�v��\o^��]����zm��X�������2�)*9[����Kb�n?c��n�ch�Y�9�7���-�.���
iCu�5A��O���Y�u�"���*x^�p��T���Y���#|3ku��0�5�(��:�.]z`M���� \U�mS�?�V���v����?R����v�05���K]|������q�g+/6��U���#���:�N�VXt�J�&���	�����+��@�1�D����?�|f�I�,�cQ�a��E���v�'L���H2V����k������w�
�����;l�)�GS���l<W���n-���2�$�������Dk�vuc8������uok"���\@����A�����������9���ddd��p��@s�����m�
��Y�������Rq����W<���C�uN�����������K'�x������>_~��Ut�����&Z��WE�0����N�coAnn���A�5��A�+�kM�y[; dt�,t�,�^�������������
�V��o����<�0-C���VhK8�Ek d�)Y Y Z�yU��L���PuY���-��;T=�t=���C��������=H_�{���������s}���X�a'|�v���Uu���\��p��+�kM�y[;���UmX�3`a��:���k��mv��kj��y����kXD�*��?�c�X����},���`��S�N?~���E�>����,����Wt�+`q��>,
!����y�H������U�|��P����������L5�e*�{�J�>eg|�L������_�������� ���{����V�%''����v��+�/�[�)����*Sy�(-��n?c��N���1�ZIu�n����<���],���M����S�C�T���v�7G�,���GW?�����8��>��7���x��IIIv����M����-X�)�8B��hH�n�����+��@�A�"H��s����)S\��4�@�+_����������z��]��d�X$'��'�q��D��.b�pR�����_�z�;�[��c��N�����|v�0����=l�]tt���Ej��n�U�zb�e�t_���WV>VE������y�@uI������h&�0���3fh�����?[��;I�.�l<s�n�#��bh��PE���"�1�+���u�������v����������4��������9���z����]�n;�[I��*,�������N�cg��Y4�	X�?P���X�������S���w�()w�u?!����~��m�&`A�����X���x��~�WY]5��>[�f����{�~K~�]x�6��3�������u��>��!�f `�������c��o��1��J�{���(���kl�
c��\�z�Y��-�s����V�egg�O�'V��\-����
?Na��Jp��������o9@G����TS�17��X��=���Ks�7����X�h���k�T1	��H2V����k������w����+U��%���p���;�b[��w~����#3Su����
m�����H����# �L�iw��v�_WK���b����4iR��EX	f�����56?���(�h���9�N�8##C��5�#X�+���d�}�
W�H:\��:S�����������]����c�u}�*�z��G�l�<;�[MM�
����.������%)���@���xLOO�uA��
��Z���Y4��b��7Z�,$����&@k!d�# d�����{T����:TDt��f,PDLp��x������5����+�h����E���/Z!�s��-^��p<�@(�@(�ye�W��/��A�����I���Wr�&���5�N�p��G����U�
{W�������*�[����*(���t�~:�{74�7�
<�
�5��m��w��~���9s�����|���Bb�>'�c���v�i`����O�3��tT�{E���T��/X�L�[�f���3��i0`1r����bi����������O��P����Ky��h���,���E�-BXL@ \��
[y�u�����������45���xt�tT�{E�C��=�������W+���� �U����n[��W��Kt�Q���4.s��'>�
_\�����_;S/|����m��lXEU��
��U���yGw���!�����Et�X9z�U��]���<�&Fijv/[TU���{�F��W�}�	v��#3S���YJ��\qU��7�S����I��N�r���q�.��}��������y�������r��AA�*k��Oy��*���N����S�,����B*V/�sg��<������EZ��>[y2��Z�j��y�����R��g[~��{E����M���~0�/���?j�����1K�%_�����	]����O++����R��jSu�^,���M���]W�f�q�����s�~}r������BMd���`�;|�������b?�V�3w�v;�n��t�4Nvv��Z�d��������H���=�m��9������o�[g���O����}�n:�n]:�I
�xE�E)6"������H--��?�?����+k�����NRQ��:��_�T�.�=?O���EM�4����9wop]��K��g�Q���+�[����3��T��h[�q���CS�W|��"�x�������9�G��������)*���������FEU�
�.SI�t����~:�T��C{+h!�&�>}�I�����9slx*_����{t��
i�����=����={�I����#�L�Ww�9�3u�����A��7����������U
n�����/�*�.��I��#f���H���� d��f��A{L��o�����RDR[GeU�����#�(�w��h���S�L�#�CE�W|��R]����,���9��~����+ud��v���U�k���u���w��+���L��s������F�-u�5��"d�fAa������r��pg�X�����SL�
1���*[y�J@3-^���j�|����=�����+�{�G��;�ggu��?j�Q�Ufg<��Z�@~��V��T5��������F�����i��������"�5���jj<�������Y�\�	���/&M��������k��E����v�'L���H2V����k������w����2<4MN�sQ��P���a����=��yW��5l`��~�����^������,;B+"�,*����j	u�"K�.uu�tg�E��&����q���
��y�TU��>��4**�5�#''GN��5�������7�����W1�>Q�W��0V�\�Z1�V����~1�����x2]+>-��O�{�����:�kEy��U����#u��.:}�������{�u0==��A�+�kM�y[; d�D�����Z?s��m��~h������5��c�$B:Bh������O^����s~�� w�x�����)��x3.��Gq�R{��/Z!�g�xzV�����O�!�!�B�����?��v�a�>G������:�[y�=�%]7�~[yZY�Uo�f4k+�t�(+�����r�v,$\�6�f8B��B��&�����
s�k�z�-O�-��N�0��'��c��]��M�n��>6���
XD$v
z��x��mv��w�8`U|���_��Q��C~��^�5`U�G��o�����z�`T��kEa�e��w��K��&\����,��,
^a�
L� �	
��?���	ox)xc�g�������v�d��E5#��+�(x>_���
l�ijv��M����������E;�����+�����G�����f}Gu�L�L�R'�����b�P�V<�V�c�V����}���_��C�!\@�a��&
u���j�Y���l�1s��C�?5_��<��/��O���w����a�@Z����n-l�#`��%%o�Ae��V����>a������Z��[*!���;F�,��k�pDk����B:.s�l�����l�k
h?�s��Bll�`�.����f��Xj��+u����+���������C�����h=U4R�Uv�3�3]+���U�a[���m
<�
�5��m���Eg�/�N����y�l��[������I�+����5��c�B:Bh+*�,U�����w)?}ZQ���*8�m+�O����<]8���:c���^q����a�p������B����2�#W���o���/j�(��p���I��9T��=���?(��b;�iSU��.��D;�0����b���G����pE�����W�����v��ofNt�0�	��_}��z���
*f��^���5��c���d��mBN�6�c���v�[����>��
��Y���Q?�-y��X�s���C��/+z���������K�'��L=$`�� �
&v�q�Wmt&P�aw�;�����@�W��5|}L{}��D'�,������>���<E�?R)W��V��WT�i���V�N�]7_4�Vh������Um��������
:Y �d�����P���/��3�����\�?-��{�G��E�O��������!����j���/��Za���}��B��B��&����j�
X��D�7{�l;�o��IvT�[W���� �U�~�7`a$L���������wg��������B�_p�����t����~�uo��X�X<4��Ea�%�+���mA�\@��+r�`�W
��?�a���vT+;;����>}���^���y��	�?�jE�a����U����4�_���c(��e<q��;���j���������OV����:��<=:�J�u������U����������i�/�Fe����Z�+h�xe���uG���6�K�	�4��>vk���4O�;��}��<E��
Y��	X��B|����n�Y�����Y�����H�5�V���UT�nC���#���G�RlT��9���azx�M�����G�v�����\���L;C����Wh/����;o�	����o���U����AC�5��c��U�~�J��V��j��_��~I:nXg[�����}��}KLo�	�����p�^���]�����3���}�n��3���`g����I>��O��N����=�'E'k���5k�L��X)�c��������+�V���\uJ\�
�^��h�����Z����?�5/|P�q������d��mB�&\��G�*��G����],t+�V����j�v���8���Z�\s��V��Z}R�t�������-G\���9��Ze�	z+����~�����VW*%�IE�w��:���UXt�k<�k���K�\F����Wl4�{Dh��hY�����t�crn��V��z�����U���b��S����a+_f���oQM��z����tS�
�K�avF�_�Q}�t��-:=k��?������Z�+2���[uM�b=�k��V�+)�e%&���������N���)�>��~� M:���h?Y `&`���b���v��Tm�Z��?a+�B�M����|M��<M��eG��v����7��c�e�'��m�jSMo��Vix�]v�F�3l�F�����/uZ���p���/�'�����
���}���&N�%JM�����0�gu�J����Y�u��L�����7��
�B^�+��w��������={vH�XL�4�����>6h�J�5�M��W(*}���o��mv�):*��B�6}�t�5
3W����p���K��%��A*�|`g��k���[���`������o������]�?<�5M�z�����q����t�������������QQ�����bb��3�E*S�2������K���^�1��^E�?<��7'�@o�M��y�l�)"�<�j5t_ws����3l��|l����.\x�s�����_7������B��/+���l����OyS�`������o��c+O�F&��l��
G�w]�.]��sg[�%y{���s����h+��RUU�kl.����r���������t�322�p8\ct<�s��"$9QY�_�T%���{b��=(����iW�����������
����b�qPl��JN��"Tig|K�r���x�������xLOO�uA��
��z�j;�����u����~5%`���,rppp��Q���98�uD����0�G]��c�u,\Qb�K�����8��Q�����@�VS^���oi0`Q��u�G�~�5�gi`����q�������:Zm��n\��^��#����JIz2������� L��O��X�`�_{}l��$|�o;�����T�k������Z��)�������=�+����
_���X���9T]�bY���(�*��>Q�x��D����)2z��������7-+<���))���������cn�����
�����R �Bs���46�1e���?�V���������v!�g���
�w�]^uu�kl��EF�'E��-yRe�<h+O�](����Brxv���������k��A����]������L;����z��?���A�~]@[�v!����?~^���c��J���#y�v$e*/!�m����U�Hm�dg���(VJ�����--��.;�fet]���: x^!�.�?ok�P��	�,f������	(�J{}���,Nsppp��Q���98�{�����0�����h����w�������42#���q��Q�����@�T���C&Xq�cEFm��@U5����Ee���v����M���P@���#���$`@�*U��d��9s\�fi��Z��1�/����m�x������o(��^�O%���#���\��!lo}�C���m�ijvO;�����c]�<V[R�5�C�|����@W�k����*���&+���}W�[������TT�p����/�����!�:����&��N	��g�,���+�����0c��F�1���
X�v��5i�$;j����������[��O�2g��<Ev����7�*t�.�nG��J��]l@����D����7�|���&�=T������k��4�I�
��:^_W��{�������5��%�@)IO*B�v���C/�%�~i+��hs����F{��������W�dgg��A���@����N%o�g+�\��X[���o�i��"[y���%y[K'����Iaa��z�q���wZ4��V{����b���
������^��n+�.9�&Mz��@Ga�	��������l���?�`,������3���g+��?_������i���>��'����e������v�333��AS���U����U���:W�g��V�s�_��'+���P�����o��
�l���v$eee�Z����yOc�����E	���0{��fm[,���@���=�a����_�������56�LQQQ�1�999r:��qFF��k��/??_�|��v���3�����
�]�;��@�ED�?�zJ��Uv����n������m��<���xLOO�uA��
��Z�������

�u5�9	F���mQ$X?��n(��mA��"A{}��D�@G@��P��s*~���)=�z����M�3��rc���+[y�br]~j?[!�q���@���]�aBf���������/�<��Y^���
Y�����|]�,l�,���-[�x�BU7�exKU'mpvS�3��4N����KzS��{��o]G��cnV���v��7�
<�
�5�G�"������v�����>�w��z��|.���[!!�s�&<<M5Uv�S��(f���
�{�]��?�i�C����;ZiI�v��_�o��,:�@���o��uB�����.�C��$d�`#d~J�n���o�����#�Wv�Fg7U7m���+�;�Y��.����y������Q���o�#x^!Xk�����<4Q�_c���v�~,]���j��3gN�{�����,T���9�6������g��c���d��~1GNm�����e>����,-����a���\��y~KY�`��~?��(��%}������qhme�S2\�V�mR�bh�'���
��Q��*(`1y�e,���]xD��j�a�+�������/���h
�o-D�i��!���[�,tt�@0������i+O�I]�r��LH�3������������_n>J{6�-.��.@k����u��	#�S�_k$�;G�mCBy�����|1�[:Y ��d�;����?k��r��>���WZ����T�E5��6oT��K}Vj������[����4����C���+�kM�y[;����Y�p����_c,��c���:����?	�oh��Eye��.�n+O�Gv!`@3�,L��GZ�;�	"����mG�J�U�]����(���'�}�^���X��I��)���.��N������i��t��9,�!YtP�[�����,P�0�Y�pg��"M�R��>6h�J�����[y�y�b�8�V�����*-������{�h.s��EU������T��#rF8�m�S�-�WD��}�z�����fgQ��iO�O����\Y�^/�rm7���#���TF��v�!�&���{�}3����@��@���Y�p�x3��+D��c���|�U��;�����b�R^�`�y���#3Sm@�p������=2�
s���������|O��,�-m��lmj��=��,W�sT���U��[��	X�tq���p����AZXu�6����Kr��������u|�cz����]4R;���
=�]7�^uJ�ng"d�D�&M�#�����
n�x���L�j��z��]����V��f��#ZN�]l�Z�
o�E=&Ha�0�1c��=h����y� l�=��z���|��?z��H��FjK�p[�����eY��<];�&��Y�v���M�I���7�z���1ZU�=���#"J���.�K;���E�u�	.'��_���USZ`+O1�������U��]���<����j|�\��l�Y��t��f���0g�U�mS���h�Q����0���mB6Tu�'����N�3��RWu���8YS���������������G`�|�.�M��fg�#d����m8��D�`���*��
����<E�$(aJ�m���<}��w��.��T���7����9�maRw�����\��-*[���<O�_{����q�ja�Zk*z�����
���O���7�����	�/���>��7A�J3���<������5m�����B%D�#h!�f0
�On�����?5%y
n?�E��\�a��mv�))�����l@�0!�:��~�������2�W��������7o�!�����P����j��y�j*���A�u?Qy��mU���>ZS��s�Q����>W���?����������g��kou��W�z��������{�Hhgf����s�{�����5���c]�b��I������.\x���&LPd$+�g������v�333�]�����>�V�b������h����Z������<]rr?�����BG�z�j;���������A�a� \���P�.�t�R�o��u����dgg{Z�	����������������E _{������\�yRUU���+**�5�#''GN��5������p���J�}X��f+O�S����U�/+jKU��6��Nu�\�������D�]�������J���������3<����^���yDP��B(������BM���L�l�,t�,�����+����]��";7�}mS����zc�[yz����55�V�h8�Ek d��L�PvO0����'-�h*��o[|��Y����������^�E���,W��g���hg<�F�j���U��s�U5���b�v9S\�?�G<�w�N�������5W�����d���tW�#�L�1=NS�#����y�P���`�9�P���b������qft������H�
l�`�	i�����
�����A���Z���#m�Y����Q���9Z���(R��O(e�l���'X���������S#t��]������iV��F,%����u|��)62������6���y�����G}�n��h���#d��d���.�i+O���)��m�2�.�nG������@G�����ZB��������];�������4�5.�Np,�����S�#��#T�;u-|@�Vv�������a�'���a���iJ�k�����[.<�����#�Bb�a�5f(�����-:�B��B�+�AcT~�@���d+/""�v����:�N�^uu����c����v7��������BG�v!h
�:����A�~]�f:&��
���l�7�V��-o��mK[�f�N��B���=�������t�
$���#��woP�;�r��v�?�����j��$-/�*5�����^7(F+/�������}Y������T�:��>�����
��~�����uA��
�`���������!�� d�# d�@�T����ir��bg<%L����/�U����mz��\[y����4��N�BGE����:���X�xq�	����0�o rrr�������-�l��*��������l���K1��Z��/*��DE��I}tT�]*�������e��O|�Nz�b���L�x^!rss�����:���y�P`��?ok����d��~���x����U���I,C&�`h��6|����P%o?p�b}u_���g������,:'F������/���������X���8�	MqU��E�J��E*��[y?�;j9���
;Jl����{���������5�1�w�0�Z�\�S%��W��~���_����Pq����3uJshmeO}S����[�N����oU��=������S�5����
�2LW�.}�z�����C��Y(�w�-�K8���9�V-�_��cu�1�m�����N��$�p�|1{�l;����v���|��>���i���#4�l����1������Z]��������ta�izy��zr[��:��-
K������~>�>�c��=B�0���'��
��5������Z�W9�2'�V��f7�8	��I�&�Q�@�Y��5��jegg�4]ME�J����go�s�&;�_Dl����R?�z��;�(��^}Y1@�+��{�vD����<GO����v60�v�fN���:��������Fj5��O�J��K�����O�nG@p,Z�H������	I�
@�Y�v���u������!*�,U�����w)?}ZQ��������Uz����:TlL�^������/`�^������,;B�li�.����j-u���.bme�z��0a�N��7�5&��aC�������+�s����56?���(�h���9�N�8##C��5F���%��Su�V;�������=�iQN���q��$��vTur��D9vjP������n��St��K�5)��)��B(���xLOO�uA��
��Z���Y��9s�`��F�ul?!!����49w���S�I�(~�U�j9w��Gs>����O�������/Z!�Rw���3�L���/���'�	Y�?�|=���p�����e�[��E������/n3�w�,
�\�{E���T��Kv�a��}Up�������*Z��;��{�����i}�{/O��J�W����L�2���������v&�x^!x3���
��Z���x������YHf��U:�O~Q��l���1w�v;���BmU����XtD&|`�%��u&�}5�yuG{Xh�L�
FoL�"a�D��?N��������t��
}Y��_�3`Q�����%���Xt���%���k'�1d��"d�����T�~���e+�&�`G-����o���c��w�8[�v�����1+L`�t������A�@S��%s�T��7��$�Q�����O����;���U��������{��nq;��t�������g?�P	q��s�J������SD �#�uz������5���v�m`��f����������&�/���]���b9������~��I?�U�z����ym���������j������/��tlf�T_,��.2s��6h��B��mKk�_�c:^dgg��p�� ���yL���y��
WDw�W\����\`g�[Vx���[*.v�������cgw��u��?Ubl��	=�W�u@(��B(������3C��	�-&��=Cw�,t�,�����T������w�R~���E(]��O����V�5$M���p[q���@����w&����hY x3�iL�����5�5HB�V�
�a+����X��9�V��|�
WDEm�3�;n��<�"uIl�-y^!x3���
��Z���x�����(�C��m�\��o��h�mB�7����0�f���"�0o��3X@�d�W�?4�o���P�����xu��<1������R��lt���>�u�I�����U�E�����k4Wk�h[J��oG��M�RQ��U�{��mv�)�O����V�-K�.��Z�/�#@[b�W���SE���w{��^{�2f��;�����V���:[IQ�������c��Zkg3����v������'5���}�,�`��Iv���t�crn��V�=�(����-m�����Zl+Ot��'t���'��F��MJ�A���7�yt�=���2�8��S�I��S�����5�����Gc����K�]G�Y�����J���U����.����u�]���<uI�����x�v��J)Q��U������VV��{�>�
�*2�@I���S�=�����#0=����cn�/N�_�{����!������#����BQ�F���}��P���g+Ot���v������0A�������n|Z_������%����m�6Q��]t�?�-�<���O���!�&�={�I,�#����U��/m���=C	���V�c��mv�)r�_�S���m��������%K���^a�I<|��l�3�}�w�~�;W{[�9�%��o������D���a���&?��g�Y����Eegg��4�|Z�s�w*}�[y�0�;j������v���	X�$F�
��k���v$��5��--�����r�J���kg������X��J��G%&����b{K`N�:_���/�r��rDr��!�&7n
mH���������(z���lkx��],������}�9s�q�	��@�W�w(���r������ct���Ql�����#2�PF���N�����Y��RBL��/�,��}Aa��)�����; <�.��*�}j+O�.Z��Ei�Ss�n����GvU����ms��d���-����"2�RIG�*>�������;N��w�J��Qt�{K`��;A7���.8�������'B�`/^l����y�z��9��B��c�J��g[y?����:l�:L�����V���e!����7�#ha.@�[Wc�W�t�W��U���og<�Vv�K%C5s�$=Yx�rj�-�K����[}�m���ag�!�&2��0���_P0f���ZX0�	]4�@`J���&$v�4��`�������:"3�V�m&@Q�.��\�R�.�m�#���4�
�{����UI#sSig��L��%��w�,�%��U\mo
\RuW
�<Q�u�F�?��t�,��,�?���?|Z�9�l�����O��V���s�}o��<M�����X�t)k"�B����P����(n�;S��&J���������z�4S��I�����I����5��t�N����t{@�A�� jjj������N�o�g+��'� E���u�XW�7���b��G�&��VP�1�+b{�U�����Tdg�o*������m�$=_|�VWv��4^DM��W���������N�:i����V��������f��!��Q�*f�$[������W�_����^v�J�~��{Eu�CU�XUD%�4:U�1�U�CQ��U��
�pTkcU��[<D��w��Zx�>+��j5}{���$���������9�������_WK�\���������]��@�Y�v����]���LEF�'
W�=��7f��SdJ�^��"���7��kt��_����v�SJB�^�����U^����W�����eG@hy;���p����b��%���r������(�h���9�N�8##C��5��}s�R����=y����=	�����uFz��%&W�S����mZ^�S_T����{k��W%g����;[k��Q2d���C�?��:rss���uxD0��B(�������y@�����v��-a�
��0L��S���R��k��b���W��@_����I�rFF{,2���K��z���~�o���iv���2CE��k��;Sr��u�x,v��"d�v�,j�*l�)����~��Z������_���w��C�M�m+��^�u��Z��t}��{�����r�"�'�]/9����������i���*.�����(��Z��gkPT����*.������$�	������O_R���m�)"�����`������zq��}��sX'�0-�V:���_k�?�'�����K�3��<�42O��>��q�����������ogQ]�����+���()��
[8T�#c7��h��3&`m+�����g�6����MH����f������z��u��n`��r){]��j��O����5�p;{���\}3O+b^����U�r{K��WWA�e��w�
����*��"u�,Qv�j�v��3�9r�z��e+����E�d���3G��
���lRQb+O1#OS�������X���^c+����t�eC��eg@�&2e�����J
��s���U���h;{���UZ�_��Y����v�i�*���l���;]��
��^����Z��ev����4�5J���30��������B���l������'~��x^M�8�0���v�ZUWW�������$O.���W����V�"�S�z���L�jgZ���
���
m�]jg����a3�����Y�z�IYYtEA��v�����u�4s���Y�l8_�"�g�����m���<7���\�q��)*��)�/''GN��5�������m3Z�7��o�mQed����T�v8Vjg�*U��yEC��;���(�W�*g;��y�4.=��}�vt�����������������"(x^!Xk����!�f2'�����U��3@����O5�������)-�3����b�>�V-��GV���|[y���3t������_�B��t��wIS��,
���p�y��l���}���,�N�JW��Z�_WSD�D���HUG�VE�av����b��q�4�G����Y x3���
��Z���x��B��A%f�?���Oi������i0`q�)�X�N(n6n���^���>����EI�>���P_���m�o��H��R�������*s^P�";#Ew��N���Y4C��'Ov]��x�b;�������z�V�"b�0�[�����A�|���S���L�o+���65��&R�.B�`���z��w\��+(��R�c�rb��������6NoG��LX�S�k�Gw*�q��RL\<���9��b�Wd 0�,���q��,��D�a�:�o3�p7s�L�B�i��������X��0�E��N���K���w7���������
���`�;:����������q�5�:���#`����g�-Z��=�s��@v^�&������������l��#*5!n�~���n��V����k�O�i@��b���KR�f�6@g��bg�T�,�h���vT�,����;&M�do�U7o�a�y���0k�,;�k������3�D�����Z��o���/�������u���aO�],Lx����������C�E�J� �y<o�@{QPP�?����l���a���\}�������6;�.�R���Jwt~_�&������������SJ��}�?Yw�=H#�$�4!�&r�b�t���WM��P����������������SDT���Ek��R���Ze+�b�#]�N��v����0���*��_�b�����3���@K(--���~��_]�������J�����E/QQ�.;�>�B]���f�-������j���y�=Z�3E�i1�wl�9Gu����Sj�y!�&���������W������3��c�)+Ri����mB:��U�)(���O�Vi���xw�eC��/�V�����OY�t��������J����V�^];�
mu|�/�^����U���o�����A��)m����1z\�=�}�k����5Rt����H����:Tw;�`!d���4
�mB��]�.:k�����V-�t�����V�]{n����V����X4��O�7+W��+���+V���.�(�����p���/T�R{��
�����|��R?�1;j'G����Z���nx�\��ED�����^���9�3(�� �Y���rQ�}����@8��v��?{�V�"""[m����_��V����O��s���[vv��r�f�N���������^{M��/WEE�J"�i}���*�%ms�P��w��ft�V���L?J�\C����Hi���1_������+�T��V���w��gP����@���@�\4�F�M�2E��t�0��#Pf���|������T��x�}��.~�
rtd�����7�����d>�xTW]u�[�@;\4��Znn��z�-}��G***R�c�rb��������+p5�A��-�%I_���mE>�;]3���~��)��������r�9���E���O&������3BMT��D����oG�z��bB���B��������c>o3oBM	D�����J���������q���^j��c�����V��HO�m�
�����D��.]jG�z?���}����mA�-[�}��)/r�VE�O�E��������������9�#�}���R��"�-���?�G�.�M����H)�[�"�W�gJ�~� M�J�3%BA��{���	�U��D�!s��t��c�CU�Z��O^��w��M���l�O������,[�����p�{�nW���W_u�,JKK�����b�����T��f��.�R���Jwt~_�%�URd��E���������~�I}�}����yP�:��{�7���L�������G����o����������As�-�g�n�����/�bRs�ps�@~�|lp�g�J��o+�N��=��el�Q���Ze+�"#�
XtK��3�?��=�u���	4��5l��A�����~�m�� u6F}�5���(b��	\G�.J�F3������R�����������3k�p�	]��y@����8������~��K�v�g����d�@F��6u����y�lu�@�W�>Fk�L�`��YA_�7�q�5LP!X{���|=���`4��
�c�����&N���5@8X�v���k�233I��=(~�^�/{�V����>a��QR�����-�v���W��#��
h�W��#)+�.*h���a� \���`.2���u�����c.j�>}�����u���*�������\c�9rrr�t:]���9��)//w=��5kT\|���E�;�!�c�D��3�������uD�;s(�5�?W�������q(�G�b��]uCb��|�|�@
��`g�\�|^uL`��u0==��A�+�kM�y[;���f�5�	#��"�u2p��9���}����o
u���ES�_��m��)�����f��^���S�P>68T���~F|+lr�S�X\=u @������#��a�5���:�3�w�p��1l{���'�|��_~Y_|��G�b[�
}3����1;���Ou]�'^��A�=�5�����),:EG�����hE�,�a���vT�����{����"�Yx�d�����oI�sr���,����G�`
o������?��P>68TC����t��z�U���K9Z��>[yw���:��>���q?6��p]����9�Y1�&�>�� :����i��{�=W�^����{QYD�V����Q��L`F�n�
������������� kk����PJ�D��J��{�5�����������,�&B��mA�p�3-.�����E�FKLs��Y�p�.
���@����q�*��
�����O�P>68T������c+OQ��T���l�2�yw��.�n+�������=�Vt\���q�D���0��.R�<eZ��Z�J��������m��-���X�1s�����Q�	�tk�b]����G��[���0�^����<����O��&������c��_��q�������v���E3��s ��y[xh	�W��`�Ir7����Z�6��$�>n ��P>68�2�c�-����K��������N���
��nH�$�z)�`�5�@.��u��;s��K~~��/_�����>SAA���PUZ�D��?TM����78z��H{_�&�T��;�.Rx���������v�&`Q������������)�;��������A�
BA`L ��-�����
u����j�y��H{���Y��L|	�c���.����E�a���2'_��Yc+��������(:�?��c����YK�������������=�Eh��n��E���7����+]�,|��Q������\;�_G��O�X��|���
;�E�K�c��_��NH�w�h���4o�^;�_��h����t�q=��
V����-�����.�"C�0F�a���0�Cs�W����<��na��0[��?�L���	0�[�1B���V�;��}��<9���I?�U�m�]����������,��g+P�9������a�u����Y�v��z�-������y�����Q�jm����(�3�M�����>Tz�>;�E�Hi���OKq�vR���u���Z���#�����t30���-��x���;!F�c���w���G�&4Q��j]��q�1�+����PF��|>F���p����[���|N'N<�k�pa���k[�fff*2�<i[S�~�
�~���K���5�([�VeU�n|x�Vn,�3��rI�N��V@�Z�z�IYYl_�����!�����
s�M]����r��������Y��G�=�n�:�\yy�k�!E�;�!�c�D�Q"�Q�������v��5���\al����>���6�����bl�xX';��R�y���!�������{�u0==��A�+�kM�y[;���L]��0��@��8X��t~��L�����O�i0`q��XmHaa��M&�%���~p�b[�
}3/�����m�9e������A��V���/���7Iw��N�� d�U�5��2���kZ�	d�}hJ<��-��������|��B��W�i�W�m������������v���o��F�V�����%�����yG������#�uA�7�,�+%DV�Y7>�).w�����oK���Xw�i�t��)��35���-#d��0� ��p���������m9L�����0�e��Iv|�|l���M_���'l�]���(�^|�^Y��V��=��~��[hM6l�;������:C���X�1s�����:C�w�������x�v�t��R���D��6���s���<;���������:���v���@�#N���mL���0[w�m�a:N�@D ���0k��
o���o�����4��1���~x�����nm.<�9M�8���A���]�V����qff�"#������6}i+Oq�_����g��z�������l�]F�D�w�%��/���Dk�v�k��u5��hb����j����!��3���=����=��T��m�{��g��TEThS����kgvF������i�?�n����Z�]�V�]c��������Bk������t�322�pp.��3�������:���y�P`��?okf�c���/(��7������!��r���/��N�"p�C}���@x�X,����_V������3�������B+g{����������Iq������3�E5�M�w]���]Z�����v����u5���mk0�.���'h:B�={�h������m���7h[��*���3��q�{I+5(j���"�(���nR��%�����~w����KR���S�$���,
��P�y�P d�����}@�c�
�y�z���	Y������,�sppp��Q���9Z�p����0���������_��������LJV������h+G}�n�����
@GSXX�Z4`�W�me��.aa���qtS����/����#`��/v����Xdg����2X�s�,��L�4��j�v�R�t	�����+=�4U�>�V�c�����B�)��2��K'&kh�[h-�����Eqq���-wo�vm�R�	���v��4G��L�\�$��3>��4�?Rd�����Wh���z���v��hG�~4���9��cX�h�Lo�K�h�9��ZO[j�������&uo���������P>vk��]�i�I}����3W:�]��ZW��'U�������2@��>/E��
��O���_���w���O����Zh���]kGRff�u�^o=l�q�74[�b���l�y+�?�AS����k���&�;���*Uy�I�Q�"k��)/S��7U����W�F�n�y	�)!�O�G�4�R��D��V�smR���bd�$�� =S	s�el�P`[��+�����m�����!�������0C��x�ZB����~�b��	,PK������y���u���c����V�%_�'E�`��������{[l��i����t�7��~q���@�@S�@S-Z�H�7o���W����=���bgj9�����b�W���D��d��+S��`������="�\B'%��J��J�u(1���^�g?�����>T��]g��Vh�Y x3���
��Z���x�!cN��l�����s��8�|l:��y���w����H���%�X3���X�l���EyM������eY���e������Y���J��)��U[+����(}Q2QN��y����?�S�n����}�����u�K9��?�u�3kX��_OD� L�@H�D����]�����X�-B��Ri��.����P>6E���T���V���(a�
�
�%_��������=t�e����`����+t���z�t�6Tuq�u""K������WD���>�).=]��?�����4���}�2��f�3HY=���
!���.�N��7��b���v����1�f���Z����� 9w�k��E��g��X��H�<}���7���zi�Rh���+Vh����R�I��jUe/U�-_�D�N)�*6�3;�_eU�\�����L�tI��
���e���3W�,�t60��S�bK���}��@�Y��|u���0!��~���a��P>vk��_�V2�,|�e;�\���Z�
+u���U^Ymg����!���h+�s��m]�k��5Z�E�>.���T\ko9(>~�R��!Gd�����l��
~���Av&t�3Su���5 �� ��@��y��lea�P�;Lm��0���p����{�P���^?�`�a������g�+a�������#��
@8+��9U��N�H������&���Vi��R[y���e��a�l|�V�m�m]��f��}�;�?_�\��}�C�ej��{H!1�u%��j+��5i�/���J��3��������5'�QR��� ��h�Y$�[@hH���
�/^lG����|?��(��e�o����3g��w�#�bO���[rA��kh;�P>6���w����g+������$[������k�m��%'���lZ��s�[�h�9�7�m��SS#�k�:�qI�6Wu�����VB\`[������#�jb��:>�u��H���P��uJu�QTD��g�����������tf��~�5�/\xtVh0�7��Lw�y�����0A�@c���k���1�(�5,Z�H���m�'L���H2V����k������w]*����\���<�9U����V����6�?�l��w'������>�^������,;B������A�~]�P�h��~~��c�GUUU��Y����r��������;���ig<EF)%����Zcg|��t��N��/~+����O�8�W8U\n���5.9d�v�U�5�'Q#�&��]�#"������}>fdd���3	�/77���`zz:��
�W�����v�;>�p��7����?M]�7f�
�h��U���,hP1�6�]'|1�k�"O(�pR��K~���0%�������Gd�� @
�k�;�7�)��A@���������E[�,�[���p@�!����N��P�Q�=z�:�?O�:����	5 Y��t��.:T7}?��~vB�4��~s� �9?C_��^9TO�?n9m������/��a�P��
��P�&�����{en3���L�����]�c��5V(�5yK\���m*�w���K�|�"B�B������sl�]��q���!���Z�0��uk!��E�����t����W����h�[�zG���.&�;��<,�c�������������v�?�_,���8�N4�#�55��B�����YHh�����%:��mG��B&N�H�@Xb���W��tU|���<��<M���i����Z��Z�e�������}���a����������B:��������"�m/b��������Y�W����/w���b�)9�y[�7�S�&E�n���4���<�]B�m
<�
�5��m��w`������9�G����7`������&���Jw?��o��0,X�t�t�4�&�-,X`G:�w����[p�"1�,�#�tY����i�s,T�,�p_h�U�
K�.�#�uq5 �j
w�d~���D&w�U����*��^b+��9g���_2s�X��V �����w��1|��H��o���t��Jj��mHR�KJ��V�u�q����������tT�� �.�@p�h�i�����v�������[y�>�d�}���������y������u����M��u���������w7��ooT��2;�_DD���������o���t}����gg��~qm�"q������������ ����*���6<"&A�Sn�U�����zs�[yw��]u�Y�l���l�
���r���h�n}9G����v�����)(:�;��Q��u]��J� �1�w���H��v.BAf� W5%y*��6!�=m\�?����d+��
L���f�
��I�&�����{t�sk���{�L`z�~���������61n��H�Rv�G��������@h�@�J��������}�	��=[��5y��skm�]�N����!��lh@S}��P�������PIE��mX'G��%��3����(���MMX�sW�����j��v��B��R��|�>�V�"�b\],Ba��R��������*L��h����r=��f���M��7��;���J������������j�,�+M�_g+?�_\�Hi'��"d����t~���8���U��UT���W+����xw�eY>(�V����w��r���;�a1��Y��4��o���pW���
]���F���J�*�����(
��I�-����&�`��<Eg�S���*��zj��l.��w?>s�N<��������r��g��L`��J3��T�
���"?���T{�o}��.�c
���EaLO$]���������/�
@�!d�J��W���b+o"B�M��/���o|,�Z����N�c+�Pk�����l������}�[�]��q�����������a1�tm����(�3VLO����������+�w*�goZ!�T���}����K8�F9�
�U�<�`�^�����Gt�����
 |-Y�D����Pj�� =�7��=o���]����hKU�,�5���=|;6v�~��\�U�n�

~D�tj�m@+#d��[V������.:�8����V����]���m�]V�$�zi�����Y����l
Q�i]��?<M��_s�}W�U��Ng������]��
�
��,��z����Ee���.aJ��	�:�@�<��V�uJ�v,b��s��)S�h�������[��M~Cp������������TZeg|���Y�e�B��
��8DMY�
������v���S!G���
�m{�t�S�l��m�Q�n��_s��!`��j�� �cW��^������=z����.��s�������]9�le���`�B
0'�u�]z����7@k)zn���V����c/T����*8��5������_ag�����:23�V �����f���1c���Z��xs�Pj�������m
R�i]�]����I���Z/�[%����Kb/];�������B8���[T������1E	g��,��Ph+�~x��:������"��B��5���o
��b�9]gw~��������
��e���o�]����P�NYvhY����{T��m[y�9VI���U�<��uZ��n[yw��������OK�,q,�"�p`k���o
rM�C�1vW'���1ZQ�p���u��9J��bg����T��Q�������=TI��U���h���g��N���3l�L��l�R?`1y�d-^��V@���Af���A���Nz(o�6V5�����su��[�?&��h�"�5���jj<�:����������������]��@�Y�v����]���LEF���������`+�"�z+����9��$L����k���K�������8��:���W����E�m�o���v�_Ws�.
K�.�U������q�l�q��(��AL��|/����/����u�����r��������AL��1�+���7t~�=:W�y�d�����\X�b���4)�|[5_NN��N�k���!���4�+�Bnn������t^<�
�5��m����A���h��/�T�K3m�]Dl������ng���
�����U����gR|���n��L�38�Ek d�^����^1o�<�� d��x���z����
��������"�:V����e�TPcg��l�t��7�V�����W�G(��B(�������y��*�|�`��H�`N����u�S��,��.B�t&\a�>�@c�$��E�� ��5���4=S4\��w���d�H���k�����������P��
oO���Y��m����������V��0-S�K�@x3[��@sl����6�O�l��<�k�g�����tv����Z�+z����z��X}\���6�Oj��?G��ig��B������YE&`QYjg�K<c�b�8�V�s����u{B_vj�~\[��l
r�k�Qg�g����u���tu����+���;���}���p��Vuv�jX���v�uO�gg��C���)+T�s7�:���.���{�E�
����^�.�e+�&����OfQh�����5�]�dsU�^(�_�;Qo��^g�k�1�0Y?�������Yt E�M�s�*[yw����E����6=�`���;zp�n�0�V|Y��P���I[����vk���{���Q�c�X--�����|c$�����W��Q7� ����kj���xLh�.\x���'NTDD�O���[�v����]���LEF�'����[T��m[y;b���e��Y�b�~������_�x���&F���^������,;B���C8�������d��?�V����5n�8[�]�����r�{�����AT��������+�wV��U:�����rhii_-+����w���0@�:���]Z��Xw�g��B���B(��+���4w�&�`��B�����&O�~����YP�!!��_�G�?o+��3�*���m<k6���W���ig<��D��kGhp�$;�Bh
�F���'B={���� zu�S_�h��U��5���]�+�;��a�	W�UE�����2R���N���t��@�`������l�\��GXD���f�*x��+u��k�,�[/�"`���5,\[�<M������Giv��ZT:�I���X��D]4�v���J���dtt�����S���Q�[��w�i����������������5y������u���l�!t�@k�������Evv����\c4���
�|u����tv��l
��A�E���e}���boi�^)u��)��������k��ur:kC��
�����@�W����x8p ��
�W��^��&O��Y!!�CU|���^�i+�"b������p;<xn����V�]8���:c����Z!�u�C��|��	�|����/7��S���5�������Jk��l�
�9Fc��������#''��������y�P���=�:����� ���B��&���py'@�\�A��l�����66�8��n,����f����5JM~R5	�������pDF���3u�I����w�����3U[V������-�{�=8�V����z��l�����r)�h o/�F�~U`�Z�E��[�������b����4N����:�G�����#�S��t{oY�����Ud��v����oV���*x>Y���_8���7����V@�v���y��i�w�����R)I�P�N�VR�+���`oi���G������O��&eMS|t���?�,�DMY�
��Y�����w�'^���.�U���^���Ze+����E��X;�����g�}�w�}WKw��(b��^R�N�RJ����������0Y�w����q���>�,�@�E�M�s���C���B�V\���O�VQi����,�H�w7n�����/W�5�U���O��<���Ql��8)��:�,���gt��4��a��E� =�*s>��w�#�(���
.��"w�����=H��j+�k��z��hy�+�:�U�E�(��Nu��+����i��iO��Q�(%���@S�h��_�G+���w��c�8�.[�/���������M��:xz�����y�����j}��*�lz����LW������h���PDR{��"d���.xT�?o+��zU��m\O�o�^�����^a�X8h���z��u�������hwD�j�������5ng���v���P����+���("&��@0�h��>��J������L���g+".�������?�m��wC$��K�ltly%����u���u�{?����PYu���i�W������u��b�U8s�T%]t���`"d�U|��J�������MT���������k�u��kl�]��W�"�ag���Y]�O7���,��~7�r���_�)����
����s+7���w��
���/��Hq�^���~g+�F����\���^�i+��!Q}�U�l�U���Ze+�n�l�zu���j��q���9p�m��������u�����������[�������S���M�i�

�6Vq�C��?�j%�1�VB��@;R�e�
��n+��7K���m<��������WXig����,�HO��2>�f�nzt�������O_����-����I���C=��6-[?_�2{k�UW���|��
����[t~�g:"�[��'��1.��;\!�E������YE&`QYjg�K8�f�q������Vi��"[yw�u���l��%_�������\����J������;��=B�t����5����+����m���)������'����NSeU��M��S��Q��>��K���"�z)�GS��g��D���)+T�s7�:���.���(���m\���V�����{��I}l����dG���[��k�m|����Z�n��v�i�����l���;]�����-RJ�^]��.U��������5��\�wE8��5B�@�s�����V��{�&��V���{[���W��=���?/�V@����w��n��g��*x^���z���m�x15�J�<Z���}�Pi�D��$�[���]J�,��&��=�,���	E���3Z!�6���[T������9E	g��Up���v��u�W�
����.b+��
��`G������������ig������{k^�U�"�Pg��*N���i�Sy�����������K~K�+���������3%��[�@�"d���~�*V�m+��3�*���l\�}�M��c+�R��u��Y���OK���&��#�>�n�nzt�v�����+(���O��M�����V�����ut�EJ������T��X�+�����'6G�w�K��z�rO�k.�{���]c-��p�6�t��*��y[y�{��.�m��zy�V=�r��|3,�u���:N�M?>s��|[��H7?�Bk6���m��Z-�Ykw}eg�������Gid�y��8Y]�����1������t��-N�+����=	*����]���	�q��@k d��}����[y��[��VD\��	����G���V��t�`=8�V@����>������m��2���7�lU��i��m������U�����T�U�*N�������������3�<S]���>��8�v~L��?Q��~Z��R�9����c��Z!����������|S%o��V�E�&*���rt�kg���w7����m����k������)cz��+�*:���gqY�ny�-X�����t�����UEU���m@�qZ~����)11Q���Svv���y_������a	��{]T�w��N�	�5�>uT��@k"d��L�2��������,Y�9s����a��7G{}l�U��@�/���of���>��*x���&���
��������S��
h[��Y��puI��3����j��d��UZZ���^��A;��C�\q��;��L�!C���W����U�}�
-����ry��T������rm<����	��-Z!4�����o��1a����k��v� ��f��Zh��
����+T��t[����Y��m��yr�F����N��+O`+�m60Y�f��{%��~%WO�?����]��l�2�����r��v����V1Yi���]�t�I'��Q�F��p��V�����=�5����w�O�&���v��3M7^���'Z!�e&d�������7�~����^�5�����-���YE�M�*K��w	����#N�U�����EC�?��������t�������v���������H���������m~B�k����T�����J����cccu�Gh�������k��`�
��=������$��)�Ok�W����c���Xm
!������.�/v��_w��=��R��?�����pWSV���nVu�v;�]�	?Q�q�*x���^����V��������$F�����#���C�EWkd�|M�E�U9��{����������uq�kH���Q�:w��1c�����������j�*���zu�@���������]z����~��ta��u���h+Y�+2Fg���	*�7�V��O����/����pg:X8����w�c.P����*x��:���[����u�I}m�?3/���=m%uK,�������4�O��b�T������U�g��[����^9N�����I�\ki�����e�T|��
�v�V�u���s��wWt�Km�7\��6@���bo������-B�;94�{WoA�:f�=���+D{}l�=�*s>��w�#�(��[l<����5|u����i'���vU|�?<�}��9E�:[�O^���f�l�?U�����2�p�4y��y�veu-�)�v�hU�;��(�3�
�>N��LS�Nu���y0�Q���'T���T������������D|�?}�&�M��L�h�L���vx��
�	���`���7o������7y^���p���i������};�&N���u@8X�v����]csYdd����~��?~�V�Eg�U��*xx1G��{����u���l�-X�z�IYYYv������x��X��um�������56A���(�8T�����o���*���6B�SG�$&�������Da+�"�S��P�G��wm��������\�:�P��Or����^w�?W�?��J����_�Tt������#���gdd��p��@s��B(���xLOO�� :�W�����w?������'�{��ti0���2�W������?�Z�>o]!��cP_��GXD���{-j�?>�&���/�� `�v������z����hA�5Z�m�������
X�8Ru�1�k�1�|,��w���?>�X�p����qE�����~���G���2	Xm���lR�	V��w|m����K��Q���l;�o���v�[{}l��}����[y��[��VD\��	�9�����w���_^��3�z�<�*��5�������*e�w�:~�
G���[�j��"���������������R��*W��z���q����
���J�����O����T��U���r�g��)t������	9��_����^��.��>���@�����"�-A����������������7U��l�]Dl��.�-G����|�S���OX��$KS����>���R��u��\���2��m���Wtj����^����'�?�cEE��<�P+c�)���~�o��������VY��@MU�����r��}��J����g~��W~'g��}�;_���[y��NH|F|}�.9��N����-#d�L��9�+�kN����I��cP���4�V��-B��n�����V���w����.���,��r��(������������K������=�V���.����r��	�o^�|�>�?�V��mB���/5"+]?<�������K��u���oG���:���[���B-�j�
>���R�������mW����;-��������N<���'G�6;�myy/=T0F�5
/�N�V����l�m���b'���\�u�>�0Z[u�k��!����YE�M�*K��w	����#N�U�U���W�N��kg|�����]l�/����^�������w�H[�w~�6��Sh��y�������]�NzO�+���+�r,����!��)+T�s�U����x�Ow���j�*g�n���Z�����Y��u�0?���/��s���g�'�7������t������O��m�]���K��{��Y�kR>���_�����|�Z�
�)Gb���o���5��l�)!�H�wz@|u�n� S�&�[��,��L�4�����>6��2,��V����1(~�Om�|eN���o���<;�]TT����0;�����
)����VN��;�hWe�)�U��];��|��IQ��1���"-�����'hSB��R����p�������;\���
Y�sE����7�*��ct��=�,����@�=�*s>��w1#&+��[l�|�e�����X�FG���1CX <,�y�����JM~X�1���**�ik��� �
���Z�}�ou���������ut�����o�n=��{�R/>M�eN�3�.�s	��~��z�����y71�e����������Y���]S;<��a��2e�����O�<Y���s��1-��1u����3f�����^��-\������O�C�V���vdT[7K������������G����m�|��5z��}��^ig������NO��^�v@{��~�����;��<o���v�_�V,Y�DUUU���q������\���������JN���{\�?��cUT<�V��#z���r��Y������coAc�����t��r8
�M��
����{�u0===����x^!V�^mGRVV�����:Y��Y�`�_{}�`3��x�����:�>���EU�A*����#��J���`�"16B�����������������o~���P�&Cl��JM~(��EI���,�����G��^7]8��������o)���m�]ub7��?��$����+��#o�k����/)�����4������YGvU|�JI��""��ml_�����
�3;�]�r�h������4;��2�-<.�� ����B��1u����|l�����o2~�x��L���g�N�%��C�����`PP�T4.13a�8���aL4��}u0�D��8��<�gq��$���h4��3H��MAAff�K�������[��z�{ow��9�U��������s�W��;���]�bw��K��?�{�\���Ua���e=��INg���`x������?��w����^��������W�x���Tg�}v25mx���p!@;{����vMx�g���5V����}Q8<��$�<gO~)�X�}���o	����I.�0�eP�(�a(�rE���BX���"���@�fzu�K-6NK�$�cJ�{��4s��-`��hk�����t�=��e��~��SN�t���9���]�$I��������|��H�y`8a����+V��l��UW��{��#Z�(��M�����;�������M���p���$s��z�a��
�\g�����u��j�����|��4�t�y��S�34	,Ww�{k2����=:���?������|����/yh��j����e�<#���S���pr��G�6��6<�����ua�D{w����?	W�����u�;`ydA)�l9�B��m����l�0�BT����4s��a�������+���0���'s������w������&9�|����Kt�I��W��L�����~q��������9�c������COY�p��a�#N/x����������������m/~H�������3���?���kO:!����	?�����k>�rgx���a��]arx_X?�����6�q�Dx����8���Y�(v���L5w�W$Ss�x���u��*��X���3�����K��^��u������`��]���|g= X��A������\2u�'�{Q����d�=�#C���c�����[J����Dx����~�k��N�_����~����?8���'�6l���a�C^����5�B����uk2��W_=oH���|\�.���^]7�%X��q}2�o����^��u��o�
�{�����Nr��s��Z�_;��@�{�iO/~�k����F&�)��~�������%K���'>'�����On�P���|(���	O�����NK��� J�zc���
b~|>�����n����6L���\���0�z�k���|��{�����a���$'�CO_��������'908~�A��x�[����H��)��^,�}��������?|B��O8%y�7�,(U�W���m��dn.H!�g��r�W�
@����7��_�d2�ot�����Q2��/��'��;���Nr�=�����^��p���$���j��MU���b�m����p^xa��}{m�����h�=zu�K��������'>��ah(����o�1������>��0<��x�;�*����\��<4w�;����$�}�}������z8<=�]�<j����/XX11�����nH�B8��s�)(W^����u�`��7�LO�
��RFGGk����n�)�������_FF����rEn���#����:�q��P�(�������dA�b�A�g�F�r�*����M����X������`���w�z�X(�����
oz�y,�d�,X[�l��
�b�Y1�����Z�������?�����d.��������a��������W�	�{����l��i��u�!B����@��p!� (k���o~&���_I�[��?c�lH��w����_u}2���=���/{X2
]8���p!����A���u��e�����BZ4}�W�}�_���U���+�|��B����,`�	�h`�����l	a�@��o��_&��d�}����?|�7���.x�I��/}h2,A9*��X���3�����K��^�������~x������'�������!���Y��3w4�c����^��������?���d���>����?gL<X*�,2�~��a���H������a�3_�������#��oL�{���^�3�$s�Rd���co����d.���'�����d�}�x������d����pJ����N���"� q`�_�C���d.����`[2��}�;�/�a��go|`����O���$������]8p�;��|#�V�`[�\�������Nx�GoM�{�~���L���&�x����a�'�8��74�*��hk9��$�=��n����/�tZx��X�r"�hS��L��.K�[}��0z����������+?��d���}�i��xp2,�,��5}�W�}����5���W��s6$s����
��z������p��X�r$�H3���~`KS��|+���0���'s����+����=�k����^��%s�r#�8����,f�����[q�%a�	/L������>�c��W>����O`�� `����;�O��M<���b�������?�>���&s���Yg���tj2,W�,������
S7�G2�o��.����d�=o�?7���p��/=����'>0��3A�����7��_�d2�ot�����Q2��?��7����P$��>w}������� ` ��W��~0��7����5/�����������}��������NI��^ ��{�����k����^���������$�u[�������~2��k~����=9�z� ��������|C�j#'�����������{����������2�A0�����)��5{���}���p�_���4����a���'s������5_�+�k�w_�����_2�A@������w�$L�pm�����^�������������/���5���>4\����9�	����/�C8�S���}?Hr[�����G==�k��l�{����}��$��?x����?tb2�*A0 *�J2��f�V�O��/~ �in����'�0�k������w~5|��]IN���������=��$�e�,��7u�g�}oq���'9����_	+6�:�k�����u��Z��v'9������.yxx�C�%9@�d�����m���W��{nKr>�����Vl�8�i�}����������$9�V���z�x��k���z��T�������I����C�G8�U�c�lHrZ�g�T����ro��o�����K��Z|��A@�����a��^}�cINs+.xeX�s�+�>1�i����������}_����Uc�!Bq�qI�OY=��g�O��/	3��f������������b�/$9����C�!B�y��$'�����?������W'9@�d��}�������0�kn�����_��0~��$�u���`m������������Z��&��� `����M��+}�CINs�{fX���
��NKrZw��z�����X<�����K�g=`e��+A��v��������_Nr�{����~Q���[��_���;wLr��v��,|��^w����m������c����k�e�{��	��#�X��o������*�����F�����o
�<?�i�M��~�_
w��<���SV�?���p�I�I�*8l��1\z��I�QW_}u-?3�l���,?�,�egv�w��W�*��}INs��~8��W����'9���m{k=X���yP���
t���)'��u����q��<�+�'X��<�R����k��&�
��.H������{?��P9�'�in��.?�����7�fggk�g�}v.O��o�~�����?���{��V�?x���	k����u�
7$S!�{����+o8�~h;�����8DH��"m���������{��al��5l��%��\�s���������vMO��_��ttt�6
��������Lmz���add�6
�P�(��7�|�8x�Yg9��e���\^�� �,�^��_�2��_'s���rnX�S���S���d����
��7_�4�x���,�[��,_�� �U�d,���1�YV��o�9z� ��b8eP�(����A������k;0\��f�����_+`1��������X��7�	�{�W������J�"�P��,�!��������u�]�L����~%�XRS��.�����o�h�����vX���CKr��_��^����C��z�h��g�����U�,�Ev����^xa27�����cG2�y�A��9���p�{9����$����{�;����l�����=�,ff�w��#Y[��s��/�0i[�nM����iS25�[�Y\}������`yq�Xt�C���^����/�in��O	���}a��Ir:����~�]_
����No���atd��k����6lH����eK2�=�������!�XT��~.�y�������$��O���������$�3�������z2���?�����?,��
�Hz�� `�������+����Jr^s���Eo	+���$�s�������d����<1����%s��N�#.���d�;��j�����E�dhh���y���
Q@{Y���	���
a�?��$���s��
2~��$�s�����������������C�9���W_�Lu4�^O7��)6n�.���$���~1?\�X�,�RM�k��A}��INs���ea������/���[?|S����L����x���M��|�`�DQ4p#�.d��Y��T*�@���?�{���0s��CtM�	�_�5�|�/'9������?x����>sg����������d����[���;k��z�y�^7b@�-� ��?����#oL��=�G�q�zo�O$9�{�'o/�����Kw'9�=��N	�����9hl��M�T�����d��\�e��p���'9sb����k�iy��PA@W�|��p�_�,��&9�M�����.~G9��$�3���p��������������������,�H{���P,��Y1�"���aC�P5�7�P�����d*�'>��ah(��t��/}"����r�@����XX�S���G�d���84���k��7Jr������?uf2�n���d*�s�=7��r������_������{�(*���=b��L��.��{�����M?�������s����TP����2�cs����y���*�+��.W�������y50
�>��O��d�a�'�^��d���SV��ea��������o��i��/85\��g$s�C�K!���m��],,���d{��������+3��Q�����8��S\�eN[�|ymj6@Gfw7���_,`1�#�	���=]	���/�^����`12<^��3X��6m��L������)��.����k���,�2������^f��Jr�[���	�~2��84�U��v~��$��'?�~���O��0���=Y��dA�I���=Yd�-��uk��eK2�<�{���aC�MC'n���033S�>��3���Hm:�\Q�[o���q��3�p�+�+�p��7&S�������!����z{-1r����8<��������A�?pU���?��uI@�d�RdA��$P"]nZ
���l��r.$~6���n���������NW(W����o>r<����
��2hkj.���p!�.V�c�k'+�y�Mb~'�\7@/��wO��w�Y8�b�QO����X�34���Px�3�o��G����PE�6��v����]/�� J����1�a������J��1(����2�
���n��p��__M����#�z�ahl"�i]��}}xc5���I��6��I����px��S���V��z}��M!�;�o�hE��b��b��Y��bD^D��\+
e���������R��}G����������-L��������A^���a���Nrv�����v|�����SN�Lr��;v$S�]q����N�p#u��K���b����edA��*���f���
q��8�j=e��/rwJ���9S��������oI2�?oSmx������.
rqC�<�	k��<o]x���I.�K��c�~�!;�m�[�n��
�`�N{����j��M/ C7����e]T�V���K��F��~�h�2^�����?��#���O|��m�3������"����$���~!�����\��� Wm�vK=WDO~���K7���s{������}��axX<)�n���d*�s�=7��r������_���:��w��#�b���oO�����`���_J�{����M��jtt�6
��������Lmz���add�6
�P�(��7�|�8x�Yg9��e���\^��+�����K��t��!����a#������8Q��z����@��]//`1�������WG������Y��U��W�^�����e{����z}��������8bC\:5�mW�1�"��?K\O:�"�����)C����E�]tK���n�Y�m�(Rf�wz4zM��^Jz����}���p�s���77v����Y���?9�iM���V�������A^�����NMr��x��z����X
y���X�iU�n����R�}����Pq���&s�5�1d9��e��eP�(�(�rE�55��v���J7Rd�mW���5��y=N��n�^0s�������p�����5/���,�� ��������84�����y��0Q���+ch�-[��&bGq����,�� J�
X��aC2���$E�,R����C��H������~=�ib|eX}�������$�5�`9�����(�b~�(��&��34�ezP� z^�wm���#�j��u,'�>�-����CH��hf���	���o��#�����������������A^��3��~���G�.����A1�!\�S�o�� ������[���dAi��B�F���EJ#�]�Nq���Clt��P��e�`9���[��w�<���'9�M�����^��0r����b
�A,��)6n�.���$�������.
����u��^O]��Xn������\������&����g�!�zF��#��,(M:�"
<����g�'��~���rh���������c����*�����%A,��[���;w3�h���2�@q�{��{�/���^��47��?]�y�C���������A�����l:-�X^YP�k��6�:*Wl��%���I����}��Z�EZ��"���iS2�}e������a��_�n��$�����4�����N
�e��� '��Hr��x��27yT�]:U�#�J,��y�k����.bF�����}y�7{�Hod�z��^j�����|�SO=��m���/��0��$s��j��c�
��/�{��n:>�{����$9�Y�_���UIN�fgg�������i��u'�pB-������v����E�	gzz�6��FGGk����n�)����=��_FFFj��	��2�|��G��g�u�� ]�\Q�n�!�
��s�M���k;�9�Z4(���5[���(.d��< w����C{��{����INs�������.	�#�z��s�t������P�S�����X��Z ��x�������?�C�A�n,�,(����A��.�S��2�h.����
�����7�H�v���Lu_��h���_	��u�,�����������������R��i'��_x������`��v1�
wm���=Bl��5l��%������p$��^���Z�'��7�F]���u�>�������> ����
�����4��Kw����n���$ga�#C�%>(\��S��r�H�z7�1���!@?������>��dj0��/�~���_��=YP=P��2�q�2(W�AO���h���d{���2����JBl��$�����T8��_8�b����_��0~�������?8�x�
��J���{�p��<6������n����%I��!��=��	��V*��
6$S��{�("��Be����})����C_��$��OzUX���V�Kr{�����o�|����?pU����������u�� X�YP��%dL��i��d�5�����2�
P�C������|i����.�^�.�~����,��_�+\�����~��I���� �x����~���G�p@�dA)��47n����7'��\z�����8fg��-[��9��mK�Z��CF��^j������W�}��d����Ok^��0��'%9�������}}x�{���� /�tZ��?YP��p����b����Tc;v�H�����+��9���<e��S3w��}���C���$����_���/���&9����SW�{�,(Ev����Yd�����A
K�O{4�(s��r���}�����_Ir_V?�����Z�����A��7eh��	���a7b�B���t�B�
�Q����aIC���|Z��<�\7@7����}~}��L%9�����p�%�
��|Z�3��A�dAiv���L��Zl��-������	��.���d*_�����^w\o��A��-4�G��h�����]yI8�o��47����^�7a��g%9����j��MU������@�K/�4�+.h	V��~���;��������\�R���k��.���d
���y[8����������b���g]&��$g�84����vK=W��A^���e�s��7�fggk�g�}vO
��n�!�
��s�M��\��"���u�`��7�LOO��c;���hm:q�M7������������Hm:�\Q��o���q����r�+�+�������W(Uz#L��"�r�A�ho1�!��D#q�V� �\7@Q����p�;^���X�>��a�%�m`ah��	��t1`���(�.�s1��@��3�����;.��2�
���7?���Ua���V����Ins?����U�
������� �1\��@o���������Y�b��_&���d�Xqh����n���$ga�04H���@�,���0\e0�eP�(�a(�rE�55g�X��o�r���+��7��`1|���q/������ �X � XB3w��~�w���|I������b&~����W�/����I�Qm
�rC�4"����]��}�xy���?�In1C����#�z������$wN�e[?���o'9�C������
?z��$�,A��fv����a�[�}�#InA#ca�_����0��/N2��|�,`T���_�ga��>#��&��MnxqX���+���!��Lr����o`��U���/��?~Z8���&��M<������9���ax�	I���C����,�� �de��>�����7W�:�����b&����_�pX������OIr�`i��.�=V���������,Z1�������a�O�~9��$�(C�,A�%����a��>#��������A��y��q��'�~���S�Mr�LM�������Xb�,�C�>�����{N���7���w$����{~Xs�;�����0z�#��9��}_���9<���3��C7�44�S~����2A���_�������<����$���3$�y����/zk�N�����~x�_}%���}!|��;��C3�3�
r��14@�	��������xI���_f��F�[���Y���������������������y��������p����b
P>AP��o~&�w�%a���V����In1#�<$�~���q��*���)�
�_�xwx�;�^�������N��w*y�8C�,A���[�+���_	{�i�:���������p�/�}�����;�9�}����]�_����>|��]��V`q	��/

JW]uU������'��&�[o�5Yj���c:��3�%����kr_��_�,5���_������tJ���-�51��m1A��m_{�����+/	S��L�[������|m8�W?���Z��}��p�U������}����]�j��Z�f�� K@�d��yC����
���%���v$���>1��������X�|�E�������N������7_�~��d��=������=7|�
�74�d���n
{�����/&��'��b�&W�O�����?&7�(|�����{Cx�>���[��?8�,����#������m������Oy���gXl�,�ox���.� �(fv��a������9���>��42V<�a�o}"���K����~x��|!\��������Zw�k�o<}��4��O����*y��"��������Hd5����������p�?>��7��?_�����^��#w��\����>rK�����%Z3::���S�[~���������P�,KM��rh_8�/v���������7�����_�X�q���_{�����������0[Ih����
����������_��Z/,?�,���v��p�gI����p���
���i���af*y�����t����
WN]��'�o��M����/y�uOy����W?<��o>:���+'F�gX�b�����T��X�����d*R��t�������;���;�)n��O_<�'�?\�*|��{����~��p����ZMkW�%�t��7�fggk�g�}vO
��n�!�
��s�M��\CC��0����~�.X.����0==]�>������hm:q�M7��������������rEn���#����:�q��P�(������\y�����=������`1����S��9���?�����,��#O������<&\��SX� A��C��H�����}{s����$����<>|�~�~�K�o����gok����r�d���w�c�������[�<@/2\}���/N�����+���f���/ov�;����$����D������sv���'�wBm8���<1�a�.�pd).h��B(�a(�rE�@�+�������A��u�����w's���kWX�vm27�Y���S������})�)���s���\>;��$�u����+.|���A�[����Y�@��� �h� ��b8eP�(����A������k;p���6�����W^���o�`�����[�4�����v��c�Y.��s������g�!���	��/4��"��yb���a���
{�������r��N����'������{_>3��$����#�������^������/y�~&����,��Y����~vx��������m��/�����p�����7\��s����]�/
�q�����'$���w����_>��
���g����J�`��d�az���7����p�;_mG�[���q����~���'�6&�����g<����_yd-=�:=2<�-�_�J4�j����u��5�}`��]��J���'>1

�Ebe�w��uk8����C���INq������O�zr�	�In1g��*\����Sw��rb$�e�������lm�������I���E�^533S��)=���?Vf����P��WKa�@�@��ZA;P����tp�=a�r0��Cabl6W����W�����
�M�������Z^|��UN�������~)���\^����u�`����k���tm��������� �M7�T;���_FF����rEn���#����:�q��P�(�
7��L�p���&S�����/��.���dj�	�8V���\f����*��&9�M����������$���<�~�����#��	����
Z�)�W�o���w�s3�s���k�1�!/�������
�an�H`���0�� ��t�����������&���	��!��2�N�+��b8eP�(� ��Y�dq,=Y,_���	��}O8���$9����\�{*k����~��Z`����v�X�K/d�!�7>|$MMM3]����k������p��!	T�)��
�����Ry�e��j����fdh*����������pxv2�>NU���t�1/����������?�T�d��n��n�Z�����7��/�>��� ��,(����A��.�S��2�hN��O��G��Y�X����\��LN2��zjc�����TNHr���'���?9<�a��z� X�IT��
�S{Be��0T������s���X5��s���c�������!����z�*��p8�-��*sA1����s�c-H"NWS=��peE5MT��>��ph&�'�����f���/�ri��g�S2�	��!��2�N�+��b8eP�(� ��Y�dq��/�8����+�L�X3�����?��P9�7�,n�����zr����INs'�0Q��"�\q����a�d�,$Q}���S��=\M���0}_-P">����"b^������� �t�B:���w����8�
����j����[�@*C3����yW5����s���wt~�q��_SM���������[Gu�R}N���T��>�����������6��
W�����As�,�v��.�S��2�N�+� ��9A0�Y���}��+*��Ir��v�Gj��|k��$��'�wB-�"�^A�d���$QM��w��C���������"b�avo3������^$F*� �E�H�������U���of���������LWc0�l��
$�1��2��3�1������P�����s�K�&Y���c.o�5���bn�#�9��8=mtj���w��E�4&�h� ��b8eP�(����A���,�dL������`8x�{����&9������G?%|s��$��uk����?�\����Hr�g�,��j9=|��0UM�w�z���3�w�G����"j�����0<[Ma�1�l,FPDVGA�����Pu���=>�=��c���j��#yq���6
����	t�S����O���dd�C�ep1�2(W���p��\QA�	��&������?��*��uk�S�f^����3�$9�=����Swrx�c���0(Y�T*�������������
3�����C�C�?#3w���=a,�
�C���$��1A��fq�$��%���644������Ac�,�v��.�S��2�N�+� ��9A��3�<3����[nI�[:���.�=B�f��+L��t���!T�Z�����s����>?s^��o��������q��O]��2hY�MS��ML��N-hb������0��0<}W��;�Uv�'&��K^�8I�-��]~{�_$s�� ��,(����A��.�S��2�hN�}k��ua������v����]��
.A,��������0��/����f~�~��
3g��N?%|v��IN���XS������?�1XY�����p���jA���f�q$hbh������{����0v��������<H��p��� 	������������Ac�,�v��.�S��2�N�+� ��9A�-AdAf��>L}k���\1{���g�w��i����L?6��otd�X��j�AP'�b��wGm��41{�{�'b�D8��0<sW-h"��&���%����3��}�����k�}�k���T��W}�����y�U��8]�$�R;}����s�+���Y�dA\��ep1�2(W�A�Es�,�[y��Ny�#��X�_~y25��_��d�cTf��1����**��=�wgO�zJ�������9mu-�"��*��'����L�=��D�i"MT�414��02sw��
�Io�Cs�`�0U��D��!bpD-hb.@"N��Z�D���[.���a����y�W������\Eu>�U�Hm��I�������=F��������H�qh,�����������V_[}��_}�=W����w����N{����s���S{}���������y���!� ��,(����A��.�S��2�hN�}KO���k�)A�2��@*����$������������?M?9\=�1���)��_-��1��]hN����&��=L��N�=tg5�
�'�1pb,��M�
H^��}���GT�\����$j=K��%*�1�]bv,	��=I�K�&bPD5�(�cO�a_�����R9|0V-;1��hAF����t^b=�l�P[O&�!�l�P�������/����7<\^���/KA��A���p��\Q�)�rE�55'�������YK��/���8�[�w�O�)���q��SO�������W�+�� kW�%��� ��1}hw-h����k�M�����&f��M�Uv���=����-�*�G{�H$�%�'��I��]b�P�}�=J���8�������8��\�\�z�X,C�+�V�����V���8?7]�;:�_y$olx��0�T|Y
�,�v��.�S��2�N�+����9A�-�&��X�L3?�%���a��/�����<S��*'��7��`1����G�T�x���%9P��Y��t������0;uo5��=���Be���c��[{��3{�pe_uz_�q��?��j�=�C�T�n/a��q�����d�j����]���X�w�~�Z��\`�H(142�����I���%E�A������Y�dA\��ep1�2(W�A[Ss�,`�	�8� YL����T1{�]�3���rR���Y������g��f�<s��'L�+b�'��Lr�u�1���POGR����f�	���@�8=��1<�/���@������j
�����cm~�p�i�c:�]�$q�0�{�C�j��#b0D4�w��'H�����Y�dA\��ep1�2(W�A[Ss�,`�	�8V�YT�
��->~1T�K�-��fO�S\_�8+�5��G�'�wB-�b�#OLr�3E�,�
1������F�����Z�CI D�'�Z�1���86<�0V|8x���s��:?<4WQ����H8TY���{��>�V+�cu�`��`u�����b~|����c|me|.0"E����K,.A�M��� �h� ��b8eP�(����A�*>A|�IDAT����dL����-���O-�"�Q����qg�<[�Je�P�)��]n�93���J��w����)?2�k����"����y����kijj��tLw�qG����U��<�^.6��
{1��C���c��y�Yf.x"N
�vt�Te<?(�qL�C������g'��0Q}]��!�c$L��0]�[
�p�s�
X�qx��8\}vn��8T��>�0���-��

U�S�z��� 	:���Rd�C�ep1�2(W���p��\QmM�	��&��XW]uU25�K_��dj�����0��P+f�<z,���d�~6�qV-�"��J�J���px����Ct4�*�b�5�H���Bm������S{Be���P� �#A�4���$"Q���C1�!�����������������������D�7����!A�	�C����P����UjA�>Tk�
�����a����j���4q\-�>���w��ck�y�$h��/KA��A���p��\Q�)�rE�55'�� ��6����������f��=y�\�*��o���O�*n�==y&��OYy$�����	�V�?I:fb@���a���ZpD��[��������"���	���
{gV�}!B��*+��j�?=^�d��l�	b,����T	S���0�g���2[�W=o��;�|H�>������ �Z��\PD-@"�$H"H�������{ntx,Y,_�� ��,(����A��.�S��2hkjN��!6\w�u��K/Mr�\x��a��Ma��-I��&���L���0��8�G��������r}w�����3�
q�����w+''��w��� �#�T��&��T���$
��w��j�>��1s8F�71��6=T}����$H��A�14�2R���l�b.��p5o���jZ��@���ZOf���8�F�7����a�1f��9S=�������
���V@>�K��T|Y
�,`qm��-���#\}��I���[��
6�z� ��b8eP�(����A�����dA����[����8��d�u�Q#\�'6r,�`A�We�`-�"�P1��8�jy������g��o��Y��������c���R	�8}M8y�D�,��2;�'LU����a���ZP�\p��P����A18"I�G�H�`��A����c�Cd�A�=Fj�sCb��1bD�
�C5�^7�.���!1�a�6�����.����Y���A	W\q�����x��e�]���-YP�)�rE\��e���� ���u�������c���+�]������7/���8�o���-?�,���+E��"S�z�����3��	��@�8�G����j�V$������)jA��g=`U����9����!������4�E����1��84���#F*��H8PM1@b.8b�H��p���z�C���?=4������cl8\}��[D�?U[f�6,�����{>��z:�~�n��t��/KA��/$l��1�+f����:�B�ep1�2(W���p��\QmM�	��ou;�"�a#�p����r��B��������^*jA_3��1y�\��������T+������g�z���G��8��5������
������':��ZO����{C�:_��CLW����c: "�1\���D�j>�*���0R���X�>V���%� ��r��k��������w	XZ*�,AP�����9�r���� ��b8eP�(����A�����dA��v�E�����!y����G�������Z@��m_
S��B������r�]Y��qV-����%��f�h-���1���bO'���������@���Z@���G���=E��2j�f$�q�tPD�!"�Q>��8;4�
jH�����d�`������D5��8��fc��0\-���atx<��L����09:��yqzl$>NT�Kc~u�z�hu����1��X{�����D�[��#�T|Y
�,�\�9����
�X�7|��.�S��2�N�+����9A����]�jy�6l,��f6�b�6n�8�����dj�O}�S�T��5}[���S�B�����r�^9��K�
�g����s�~�3s�����P��x�����?x��6u��0����s�1�{��=N��*��	�s=D����3��1U�5T��C��*����D�_�+c� j��a1�!�����0;��������024F�'�H\�>�<�.��bt2L�M���+�D�J�C|��@��� ���.�E�#�Z,�r+��2�N�+��b8eP�(����Y�����E����C/4n�8V�e�rh_-�b.��a�:�'�����3�7*q���S�����&yfN�HT��*r����N��?���Cw���w��������#ihfO-
��F��0R�/�V��b`���ovE�ove�qe�;;TV�}3��@�fc�x5�� F����f�����LW��t��5S��3���P�9�[��2\-;���j�C�a�����k�@�!F&�xp�>����01:V�M���1VV�WO�����	��bP�e)���do�(r�F���!��2�N�+��b8eP�(����Y���d�n#E;
"�M���������=�����;���C!LO���6�jC~�`�o�Vg�
+��!�p�xP�f����P E�$��88;�U
f������/��f&k=D��*�s�H������^!f+az(�A���1O�WF��B�UOJ���c�R3V}�����R���1bx"�V�Fc ��06<&F'jS������x�����a����b|279VM��>���Cc	��T|Y
�,�<����-����
��.�S��2�N�+����9A��n
���P!i�?0A�_����+�~����04�8�VV��������gB���z�8���p���I��7�O�{�V�����p��[?8[�>�����"hI%;T�K~���>�`���8�+��P���	b$wQ#�11>6&���qX��a��D-b�x������������a�Xuz|�6��o6��o�������>;�����/K!�X,��#]�R�m�d��p1�2(W���p��\QmM�����y50
�N(�mY,�d1}�W������ f�����G��W�{�sp�D$���<2?Y^E�2<~0zR���)������5ajr(�Mc����t�
3CSa����������p�Z�!�EP5f+s�>�@LT�Y�syI�C�������s��|��C�a���1V�b"L�M���X1�"L�V'�����c�a�:_��1�1{��-�s��(Y�@��� ���.�������"�E�ep1�2(W���p��\QmM�	����Eg�~� ������c��fg+aj�f��#a:�US��������s����,f����+V�=+V�}��a��x80>�����
�����������c���!f*1pb.$BP�`������|��1�a�Z&����cu��ZPD����1��$&���h��C�PM�z�������L}�����e�*(��Y�@��� ������Q���,.�S��2�N�+����9A���I���h,�^��������W����*3�k;���h����H�,�"����9nE88�?�V&���S��F����|,���V8��S��}����7��U�.��J��L%���J,s1"L&�E$�q~69LV�I�%b�C2=44F�WT�dY�GW������d��1>RM�1���U����'FG���1�������c`D�Et� `���Y@9:������"��2�N�+��b8eP�(����Y�Y�������M����j�e6L�Tj�ND7�1��������C�9)�;��04zs�����.Q=a�T+6�����;����x�u~E.���j
�~�q�:U].�����"�:V���;Kc��P����G�^��&kidx2�����ib,C��BL��q�����SDk���ad�	���/KA��C����p��\Q�)�rE�55'� �b.������kB���]ajxw5w��eD�wN�;�V�W�]]�@���!VT���j9XF���!bOq:�1V��A#�a"B�N�c+�d5o�������rreX1:��0>6\]��{2Y�@��� ��!�Z�b8eP�(����A�����d�$�"�0��,���������?���v�;wP��zR������u{}="EU&�SqH�z �\O1b|d�\�#�a���Z�C�b����z|UX3�*��N_}\�ru�9 A7���/KA���� �(]�Y0(\��ep1�2(W�A[Ss�,�[W]uU25�K_��dja�d��{���-c�k���jMz�X_�T���{��A����P�g��U-E�ru�9��r$�*�,APA�:�)�rE\��e���� ���u����qX��v����]��5'��_��>�-S��Y=(�2;V-�ca�:?\{#���^#�ya2�
�cC�a��&F��Du����0^}\Q}\9:6��}��3��%� (� Y���5kjI��|�,�[�!�b�������N�����������W's�Y�������du�;����"b0C|
��j�>��R�7����X�q��&��+j�#kE�A���d��$�"0l��1�[�A�-"��4j����������O'�xb�C� �� �b~����)���f����7���~P��or���='ed�tY�������d����AQ��	X���4&����� �t�� ���:��0~{��P2�n����������>�:_���x���o1�*<���={�$�j��� d�?^��W��o�����p��g'�,�l�D?Y�2A��2�,�V��`�h���Uf`��/{����W^N:�����=/<����6mJ�e�u(�I/��v0�<U�6,�F�4
T,gw�uWx���.�����W�:����K������;v$SsX,=Y�.���dj������T1�h��w�t��'K#/���>��'������<����M2wT��/�j�S���$������h�d�S��k;�9�jn.02��(E):f��a�(N�E�x�+^���w%s���P=��'~�'�\�����3@qym����0Q'�C�d�����@/����|�;kA�|�+���R�lp�W\�L5�]F���d���xgH�@���#��
�)��xw�g����b���;p����'��_�u����j�_��\�>b���d{���p�xb��p!����3Jw��+������������;E�h��5k�����ZO{���\�)[c��e�]vd���V�c��y��h�(O^�� h {g�Bb��;{�~$��\r�%��@��z��#�O���\�!��������H���v`�h L���l&.'��~�w��p�UW��y�K_>���'���,�&���`�4�dA_�����������d�}�����6l����;�L���n�����������{ff&���{����k�I����������]g�qF2������Z�D7��O�tx��_��G'9t��0�1�b��M�*X$��o�[�.���;�;��]����k�9���[o����m�����6���'�e4h��=X��; ���#���%��N\~���
oxC2@'[�/Y���
w��L�t���~��kc
�=�g~��=���#����'Z�����.����E|�^�d1�<@�bP��>��Z`@�d@�W0HB_���k���4��'�kh�)��&''���a��i��FGG���q����vg�'=�I��Q��z����[n�%��O�y�k��>��d�3��mX�Y�5EA�@Z�n]��{w2w���?����][K�fP�������>����������a��'y
��v������_����d�=�+�,��,v��5���A������_��_$s�������������=/���&� ��j��+�L��H�E�h'�"V�A@�Y��_��_	����5{����Dym��#}.�q(���}n��>>��
��A��+����	�(��&��
B�;��3���n���d
�v.�����Zx�[����y�s�S$����$�k;dA_X�n]��{w2w�]�v��k�&s�J�E���_�������6��g?�Hp���h-8*���p!b��=��.������?�����P��@O����:��<�_~y25��_��d
��d�?����p�)�$s@3ym�,���U�'�Dym�(@���k��6l���a�N�7o���-��)�Z�G�e�~-oq?��2�
����_�t���}W���?8V�M�rT4��u�}R|lW��r�-kq���`��\�������@��S}���9S�uw�^��2�Uv]ES|�A��<&�o�������R\n�]�6���i��]�R@'����R�}���;+^xa���).�mE��z[z��������\7�!�����]�+�������������iG����
�N�U�����m��Z-[��WY����fS���QO��bT���^��5�����u�q��p��W'9��e��E#u�\7���9S�}C#���*s���
`9���:����n�+S+��(�3��8\e�+��}�h�c�d�(�$o�������O�����t�n��l�����u�����w's���kWX�vm2�+���O�/����i���t6l8f���^w��}��dnn_x�W��X9�)s�@��;��c-�W�]��D����~���u�`�9VP�t��e��uJC�8��7-$�������r�K+����=W����X6����y��"e���`Z�r��s����(������{n��S��.;H�����	�������K������1��f�N���k�)s�@������yV�^�w��nzK����~����������K�rY�<=��i�e_��u�u�\e�K�rE���G,��g�Z�s&�b���r��oV�A�{K���@o�L��fw��@7��I����]�����r�^7��������VZ�u���J/��u�[��k?����]���?�tS��h�����N�TdU����n��([���s�"m	ye���2��G��*J/7����wQO����>���g��x���q27�����������!v��V����.�,���]G]��zO���u���^�w��n��ce)�NI��������J]7�U�c��dj��}�_v�"C(�e�f���#�b���r9[� ������0
��/��l${�u@�!���V�$��tE4*2e��5t��n�������������3�kM����X�ce)�NI���q����$���s[;+Q��U]��Y��8�����s1�2�%��&������<&�y���i����R�-��+UV�����	~'������4���n�w����� ��h��oO�Z���.�E�XAY��S�����2{�n���vV�n������'�����u,s.6��.W�s��	��>���^6�`��$B7[��*s�@����N��zu�e��B+�����<�Y)�\1��#���b�B��X���b�+d}���vQh@�����'������j1�b~��NN��3��u��\��F�^�w�/��
�TF���/�w+@Z;+u�,WQ^��q��c�}[���|Wv_�-���h�B������Y��
	�v��d����-TVz=��IOzR�t_<������cb~��1��5;k���<�u��n�����nW������u�+XL��S���y<��J��r��V�o��W��e����0,�7_h_�\�V)W�8�O�}�Y E�����p�5��&e:����U���x2�Q`p�S�*�?������y����KG����.�,���-W���d�.F�n���v@=�.c^V<�:uJ�_��X}X�z�y�;������O�S�I���Pf�*�N��s��Y���G�.� 7
�p!����4H1��F�����~��?-����x2��8v��XfED%(K����X�c�,�:%5����wgw���X����~/{������������C��9,��+�b���/|�S�j� �!f�<����IU���x2���,Vu�
0x�)�~�Uq�V�����]��/���6��]��:�N�r.V� ���IU<��]�-tV_��������)�:%���>�����"�����R�C��E�	�i�\9+F�0�����a��djN7�MeF��;
(K����X�ce(�N	E)w�!����hY�e��-E��s1A�@���?n�����l(�<�<���N	��M�6%Ss���?�`A��\
��� ��hV�;��3&`y�F�vS��[����X�c����e�=I

s:�{Z��\��n�+�d}�����`9Xh�J�~v��*!e�l���*s���
�m\t�vV�#���/�o��1�����o��B�s��VV��D�Y������iPd�+*;�V��H������S��f�����u��W�]e����XA���SBQ�Y)�}��ot!�U����f����Z� ��o��o��/�87���;Y��m��%���m��d�5"�N�}O��,������Xv����u��d*�K/�4�jM��2�
�^�w�/��
���:%���2��m�7oNr��c]�Me\w.F���s��Y@��F"6r�W$Ss����6 ��M]v��m��S�$/.������]��[����\7����N-v����J7�/d����[����w�v�E�b���r�\�5�l�^�v��y���v���,���'X�l���;�g�������t*�_�i�}Q��UO�g�+�\]\&��f��u{�@������������}Wz�n�����]�����������q����#�n�),�}N+�B;+��S��e�����S+�P���oS7���)W�2E�J��1u�\e��\l�w��,��d���u���6H]��[����Q^�!f�,���dj�B�����.���dj���c�u�.�s�����]2���u��W�]��,���nK����uJ(B;+��-S3����RzO�w��c7��w.6��,W�����mq������y���v�
k��M��_�(;�V�!�w�qg��i�� Pf�\��(�gb#C�2��\<!\�K��_O�������=Yv�E�q�\7������;w������.�E�l9���7������XA��]�dp��H�e#�������r�w!2�>��.3y�E��AY��Iq���10����)�\9;V^�A���I�kr��,��h����F��r���Z��Tt��~�H��)��u�-�h�o���K������;��~�.Xl�t[,e�)�r�N�h�*����r�53lH�����Z��4K��4�\l�,F�r.v��m5\}���������4�=���Qkq�A��o6$�^,�s1���}QT=q+�?����WO�����2q����<e�l���*s���
�-��2��PT+�pP�YiM����13����{��4�\�ns.����E��8�\@�uw�����w���S�>3o_O��]"��Q���}�@��v��I�D����W^����v���K���n��������N����J]��U�LE�Uj4�@;b�(r�s.���\9���c� `����Y�H�@��3�L�����[�) d"A@�Y�(��`8y�	A.��p��'S�]y����0\0���Y���[v����k��]a�����d������'�B^Q��L�k��&����.H��HO� �k;dA_0\�Rf�8A� �k;0\@�,
0\}��k�I������)8*�k�:�@8��B�A��v �H����w�N���k���v��dd������'` ]|����|W^ye2Dz�Q^�� �)A� �k;0\@�,
dA_8��3��u����m��Fn���c�K�'=�I�R�]u�U������/O�����/�}ML�\sM��|q��^S#�m�v�
�N�}!^x����w'��=�,
d�8�I2�����/�]�6�����!��&��������.Y��
o8���\�,
dP�P5U�&��T�ejh(6���u����k;��@�,
dP� �Y ��A�E�^{m:��I/�m��$�7���y��c�)���W�=��C7�W��`�������:qL��#&_����2�
@�d]t�u�%S!\x����|���
6$S����r�������:�=*������Po����^�B,d��>�X_��qc��#+�}l��)���j���z{=���'��(]�n�������X� �2������(i���������(������@h� (I�;4�w}��+��"�:V��%��\=���F�zO$��~o����Fu�t�G/���\������M�tI�����u��d���F�J��o�~L�s��0@����h�����?��9m�A�%��`�
v������P�|l�����SOq�[�\7����}d�7��X>��1uS����	��4�C���>�#m��d]����v��XJ�>Y@Wd��(�
��^�L,_�>���

���7&K���y���������>��m��%�tOl����y�1�S������e���J?S\o'���n��oS����n�B�:��M�=b��g��i�-�1��u�_��S(�g_��}���������=�����oW��Z��[Y_�]GZ����W�V~�����^o}��*[������5���J=0�����1�[��K�+~���6������4S����?��w�������'��j�G�~>���|=���^H}���S��������%��k����������[_w����*���;nv3m��5y�����������K^xa�5�������:�}��g+�9Z�.���g�[W�T�}��%�t^')����}>��B)k�u����}]��PYL��.j��,���Z���:�XN��s��_�:�w|�fj�VD\_+����?�5�R��k#�u�Z��������mc*�>��DE��H���������������V�7�}�d���[��V�I���N��A�^�������I�(�F�7�,������U�lE�).Wt[��W�Z������i�w�U+��ZQN�z�b��V���_+e��~��;;�Q���R���/��Z+��z�����S��Z}���~��6�l��b+�e������_?� �}�h�c�dY���KWj��M�6%s���c�1����8���������^!��~��Y���S�����'ss�zcp@]�"��e��d�u���~�����������������e���v������nKZ�����v��P�����}M��d�/�/�[���B�c�7\h�ie�����L������/�]�[zA�����u���:cZ�^��k�e�F��S7�>�F������|z���G�nO�|$�G��l�^��}E��m���~��6���n�G�}��y\�����*�,�5j�H���t���nC�n�U��o��pL��V �9���F��#�U+)Inw�?K=��m$~��YZyM�]����U65�l�lK���~�(��5{�F����������{E�,�^.��t����o���.[O����������r�>O����r������u������f��X��/��n���{�����J�f��)�n�^:5��V�%���o_�W�m&�lL�.u�|���cjd1�S��b��cj�>y�_�m���B��K/_O�(og���f��H�lx7��mR��4�Q"~��^�<%9s��_*�����������W6�?��������g27���+~��������,��������?KV��%+���wKk���fu���>�l=�^l��b].�.�W]jmO�m���F�^&�7G�:zV���e�]��w�uKY�:��O�E�'���n��V� @�dJ��L���"��l�.��v��\������H��-�
d�x��:�����~�Te�HCQT��d���6���(~����y�^���>���E���.��M��h�6�-yCv�]�n����H��V�Yz�R���,���cdoB*���L�������^'�:��P��5��JG^�y�;������}���d��%6*�m�/[�"�46��e�����e����#����\,Sq����-���([/k��Dy���z�Rie{��X���z�G;�;�����L���,�E�v��~���QO����2A��t�w��K��>��]��([�F�/�V��HE>VH�6�����j������d)�b'wI����}���7/�^^��\�>�l�����z�b|��Z�����m�}!��:z/X���I�R������������������R�Hw���E��d���U����T�b�����}�Jf��N��,����ST��"����<44t$�B����S�G��t�&�l=���0���b}���T���/KY��!$��&��1�"���>��
0�Y@�����k�[A����n|g�T��	����b�2V.c%������������i�pQot���o�S�G�z�(��uC7�����/���?�m����[Y�����m���X^\�L�� V"�)[��>�Ni�
I6�8�tb����S�1!�x!�[����;wj��]�2$��E�zq=i��/���������W��h4�����\�H�0����X1l�1a���A9��G���Q=�"�/XJ�"x� �
�Y����-*�JK���.Z�D�p�A�k!604����S�\�+�15B��X�e�lQop�i���-��n
��+bO�"x3���x�A�����<]j��,��-bY������ hA�RS��j��lJKWZ���q������=�qc�.|��zcB��S�\�����,���D���h�K\���6y���s��V�[��N�>����E��=���x�;����[��)�m������X�B�\�r�P��B�<�B���^��)[iM?�Mi�]vY�21u��c��d���h�^�X���V�c���@�JY���2���[�n�ro����s>[?����k���/���[�A(������c9����������.��P�-V�"[	k'�~9�8�����d#���
��0V��+Ac�T��S��$-STlthtW@/ZmyA���s��~��m��md{���)���^0����\��#�V����&�H��)]�le��`�u�@^�@+��X��V���E�X�,�=y��Iw�y��>�Qi������Y���lGV/�����e�����B��m�����2�Bv[���#�����`�Y?��z�GL�nGV���� �����]����?V��^����+��������������,T�k���X	ne�A�/e1���Jc��'��,������w�:O��f��e�v���m���,�hoq��|�~)�ymfE�p�.�oY@Z�@������=���X!nv�}��8B�b���c�y��F��m�kdhTn����q9Xt��}��IO
i�R�w����P9������
hG�zO�b�}��a��"u��|^��r���=��mK���������[-T7���}W���m��x��v���bv;j�����rZ(CX�27yT�2/H���t����3^4���^�}�8V�U�cE'����R�[�b��������8���4����P�]w��g/��i�^�m��V�l���+����������5Zo����,vR���h[�����=)+����M�1���u���s�G����:X]�:�B����|��&��lK��sv�������i����:z�mQ��[h����l9he[�.�����m���V�p�vDy���o�+����4W��9n��F��c��q~1d���T�8%ki.��nK�1�����y����N���y�>c���5�~}6e�V�/�l6e��V�����}M+������Z����v,wy��~�������5�/e�}������Z���j�����j=�H����)�.��4Rt�F��O����.�Lv�t�t�Q��qL��N�a+�_�e/���^���3\��Hd��r1������c����S�5�5`���Vt���,�}���r�����x��b���s6��������c�6�/e1~����O�Z�S�,���(�����/��lE+mq[��9�2�����i���j����}De�;���X/{����m;� �P���U!M��X�����?���b�W*C�Jk���mjg[�rq���������������e�#6@������vw*nS�2R��S�Pc@Jzzu;Z���>:��/W�m��?����N����"�8<F�{���Q_w��<�h�����!~��6����e���.-�w�Ki1�=�����u����=q.�K^���,
dP� �Y ��A�(@�@C�T��<�R������|p�~h;����#��@O�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
d9�m����������������k������M�C�@�d-��cG2��k��6����d�����q����N��e�D��&�6��&�Z��I�u�]�L���/�^zi�� ��	7nL��u�D�M�H�@�d-���~�d��FC�����M�1m"�i��,�E�v���HA<�k�?i����AP��^�L��=� t�	�`�w�i�Q���M�M`���.���djN;�c�����uk2�!u�	���@s�D� ((}�F��cf����aC2�-������?�s��c���.m"0�6��%�
��iS2�z���n1;�c#�����8�[<yK��S�w�,����B��/�=�����u5S�s�~m�1'�>���:zQv���zCA=���Y�;5b#B�R	��~�@��&�X}ym"�Kv�����L���?��&0�*���Z�?�ob�����t^\������bJ�-����ER�37���f�z��p�y�gS|}#�|�"��]>�w:/����We������m$�Lv����6;��������u������9^:��������bJ�-����ER��4Ov;�%m"�'�m���}m#�e��Tm"�#���'=Y@�'C�T��1�Q��F����3�[|M��(F�f��h&����:���������-���������b��m��%���J���}���8��sglq�O�c�D���m"�I�����1;�3�1!��~��N1?��]q�����z��?�3����L/��^�������bcB�s�m_v��$��������u^v�e���+�/z���c�������6m"�F�ed-�F��{ ����,O��+��3�����Xi/r��n���'������Ve5�����y���lCK3q�q;��m�=��tcxT������_��i�D��M`qh�,�,�E���B]�e�^[9!�V��]E^�=q�~�������:����������j���������
���"�����������q���MD�@/�&@Y���27yT��fq|��w���=|��}Ie9��U�O>-<�Q'%9����t�;Vd����s��^�����I]<YKW�����+��a!��)��t���;�����Vt��d����������(�4X������W��;�Or(��Vl�8�?�'�����;�����(�x����cG�p�����+��,m"Gi��]d���.�M�N����b	���+��
�	�����W������l�7�x5�>!k�[�(�x�O��=�[H<��7\,�l���|7E�JI�'�@�}��a�/���H������SD�"_���u��6���M$�6�[b�\���i�]D�ed��>�/�'S,�x�L����F]�e+�e�����)� ���h$[)��
1
+6,�u,TI�T�;k���6>d'���
����W%S,�x�LQ���e�&�4��h��J���)��k��Y,�x��w��+��.������c7�
b����SlD�)i�H��$�n��-qq]��n�~g��(��4����K�����cW��
�,�D��6m"@9��,����6�M��:��U��)�C��"�c�+��t���_�L�����d���M�B
�����q���9����d�����z��Vc8@m"KC��m"@�iYE�wm"t� �%��M�%S,�<�;�{�.���v�^�zbe>}��H<��]��rO.����5ywq�������Vl|i2�bZ���d��V�]�6�4�Mdi\���]���Z����|��D�&AK���:)������XX�K&~��{��f]�u�[��n ����1���b�@l�h�0�6���n\h���5/��]�}���N�F!Kc�O
�/z�;7I����=���Hr�)z��t�	�I������\�hm"Y�D�n���U��.�X�i�&@�U�	]�R}��D���<s��2�5u�������2���e?C����s�Yw+���{����d?s���]_��������F��F��H��;�
��D=��~�.�D��}z�F��y�-�n���\���H+��V��g�0��f?s���]_��������F��F��H��;�
��D=��:P=9J��v1��n1��a^v�e������T�����f#}�E�]��`!�D�5�D�A���.���-fV��(�'��k�8�h�����F���Vd��JW�q���������0�:vE�:������G����&@���d���������+���=��e�'��T��:9��~o��m�o_���m���N���=vu�1���M�=�D��6�E�t(����N*���FU�c�|����\c�3��]�2�m��~��lJ�n:�'���/�9�]iq"�Z��s#{��-&Ei9J�@o�&@7���u1u���>2V���
�S�hiL���;�6���8�3��8j�$5�>�� 5��i���;��]��?(&��L�[�~YC-EiiL����M�ndjT��4�5��b���b<������;�y]67�[w\G\Wv���5���F'��������z��o�^x����}��oT��"m"Gi��D�� �����nD��U���5��g�u�zgI���Z�oe�V��|�;�"�VZ�M�(m"�A��d]�=)��[��z�?V���8����\�D0�y����&����J�������rEN4�2q��u�u�����Y����e�:��Yh�6�����m"t*�UW��<*��Dy]��C�A�n�ymz�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dP� �Y ��A�(@�@�,
dK��k�
��mCCC����7�������2��l�}��b��|z;����^�-�m��X������eES���,��vvc��>��6��������*�yY@����7&s�m��5l��%���'�����s����������k��a�����Pd�b#��
�����������*/�R�����}?���v�r�M�~�w.Y�6m"�D(J��������Q+'���^*�Z�>v^v�e�T�����X1��i�����������=��8W��iY����
m"�� X$y'q��s��Z�j=�����C�;RXn�'���J*C�L����l��nr�/&n���j�����h�q��9W��h9�q�m"�B�������'Q����2�'8Q<�ht��wR����&{�jvl[,�����8��� 6����el��5��i����_�t��=����~�w��v�r�\	�~�M�(��b���
m"�K^���,`���I�BQ���lTg�9�rr�u�%Ss:�/[�X����y�a�H7<��.X:���G�D�8����Mdq	��E�=�*2Z62q���,�cS�D/�tt��
���9����n��1~��J�#m"s�ay�&�xY�"�'5q�V?�Z��V��tD��M�������a1�������s`yq�@?�&2�q�m"�G�����8�N=�"v��~m���'q��?jw|��>Q���u\����y�D������2���r�O�"]m�e��)"�|�=�<����8k������{�~�����lw;��U���>C����l,����k[���6���[�~m�����IyI�I��gdeg����:�E�ym"�D�<�MD�H�����M�7���1	U�����8_T�����o����3PL���x\J?����V>K��I+������^_�2��6�^>��������s�u��k���9�\\_|��L:?/��V�������-��w������z�����m�[��.��B�_^~6-�;�����>��{����v,�q�����o ���Z������V>K��I+������^_�v���1-�],�=����+�\����������5�K�S�moUz�}���2�{7Ky����g��,-��f��y���/����g�B�s+�K1��3��g� K�-�r@J����{��)�#},��q-�d=��*"�};=66��Y�I/������q[��3�3,�],�=���~?�����g�K�/�Z�]��{������~������r�lg�u�������&/��'��l*������,,�}�S?����v���8��M�nIK�y\�y�e"m"�h�O���>��_(�����y)�}[��15+_��Z���������7�{M^�oO�s�Td���/�YX��L���0��;�"��8������v������k����||���W^*�����/��>.S����o��z���?[3�e������Z�����)�#����i���]wL��J�����{�����2�o�7�.S��/�>��#O�����L����~����*�8�L|MLy��v�QY��t]%�G��+/��u[��_�c}\>�2�������?[3�e������Z�����)�#����i���]wL��J�����{�����2�o�7�.S��/�>��#O�����L��L���0��;�8����1�"o�\O�-����Zv�VR7O��q2��"���<�/�q>��f��;�l=5R�{N?�����G��O/Wd��g�f9K�����>O+�?�<�����\�^>�<e}�(�\������'�O��Ic�����A�n����L#�{��+I���'�������m!,v^8�hv�H������o������'��+�o���<��H�����Bf���F���}�U0�/232+3�^��#��^UFddTf|#������4�`�Vq��fNZ����/���{����|.����bNCz�9�>���=��?o��}]�5��\no��������
���"o�e���z���f������O�������<l��������������g����������>����3�^��K��}�sx��������x���������;|������}��g�/�����cs�8�������������l�������W��/�����-�Z.��m?����'�|r|�px�������z���qQ��W�mqt��h�C���{�����������S����\�P9s����i���������s�-�S���{X�[\�[�7�^�K�����>�4����M����s"�1'��"��1/�E\k�!c[s"��y���x�%s[]�[�7�^��D.K�>��9���dB\�&������;�}Jt��� ��������@j-����!�E�o�8��w&v��8��kOk1����z���
F��dy������K��[��O?���h����/�������{�������%���5��hg�f=�fl�snn}M�]�ZO�[��|�1'�/s"��s�"��1/���S0'2�9�y��|k�k���,�8�?��?=>bO�)�)�j��@_��v��_��������s�b�
�b��g����v\+7%^������:����n��������Mw������	��k��I������.�LB�L��}�n]�l���>��8��2'r�D6�y��:����S�D����.�D���^���Eg�"`�������`��0c@5W���@ �D]V�������'5�cF���"VE������&�w���\���=b�9��T;���������M��=&;z�R�O��H��	^�����E�{��9�����y���y���l����9�>���S�`N�4�D3'r�,�8�_��_8>bO�;�=rg74��;�5;�(����\��-L�7�����:���f��m�W���,����������6��_�����y�Y������`�z�eN�<��l���yt��9����|{<�D����9������"�3����w|���������j���1�Zjm[�n�����]@6�����s	q-���P�����m�U������y���=����k`)s"�����Df�q��:����S�D�eN��1'�%����>���+�z?E�i�S2?���>�{�����u@������������������D;��q8����O����vi��&[����<��o0���V����5
<g�D�U�D������v	qf���&�����~���z��m�E�7'r;\�\�o�<������kk��������+0�%��z?���\V�����&[��\nn��}���9m���|-s����Py�(/�[�=s�=u��/9/{��?c���v[�7���^�����9��}�Tn������a�8s���J��K�k�~N�k��%�r�n���|.7�����k��6��E��9��n��Z���������������������jy�-����}/C}K�����y*�eI��F��z��UC����>��M�/��Zq-���zU�s�F�\b�������-�������P��<�m��W��uM�U��]CpN�D�&�D�2'�-s"�1'r��l�"��;���Cu�4�UC��t�s;�z�:��k���q~����4�:���S��7B���%��|�Y�1us��
��~�j����5
C��pM��<eN��9�����d�L������]an�o��l��A��qm�M|�.�Sk��K�������o���3����Z�� �S����m�P��}��q��Y�s2�0�����g�+p{��k�D�]J�7'r>{�1�q��Y�s2'�>�,`B�z����
u']V�M���;>z�����#Xf��7=��������������\����'0���\�K��y.��fB�}��vy�br�g�n�kz������`���9�e��fN����9���5�g}�dNd{Y@�: g=������vj���N9��X�q-;g\��7SK��&�!��}��}��Q�1��r��kR���W�N����M��s^�zM�=������n�1'�51'��9��q���SN��}N���������>���T�Z���W��{������=n��Mk�H�<��k��ka�����s�\���z�#���=���"
����>����AQi������������������8����?�X���9Z��^[�7o3������/-9��������kp��kk]+s�L+v�~��1�}?�h����Z�#��r�C-�3�����m�h����HC��s����C�����6j�n��Z]�Z�����K���y[���u�K������U}�6s�����������/m�=Sz�$�T��������w*��LY#���.�'�������Nm�K���c������m��gly�:e������=X�ms�soL[�AO�[�S��b������_�[�1[��,�gI��Z���F������`mk�������Ik��|,�x�S���|����I��{�4��G��NY~��>�~�j�\��{��{�����������y������i�V��-��Y���N�+���_���wf��vjLl���C��k���s�Z�I��k�w;������6��8�:��_s�*�q��������DXz^�qM����k����G��F����_��3[�n�q���c��[�dNd_�DN��|1'��'+/��|��]qV������q�w�v�y��m���U\+eo��~O9���K
�G��G{dy����u��k�3�vQf��Hy_����"��|k�s.s��"�i��"�yJ=�����.�W�g�|����h�iI��Z�����S��c��}n���r��*�����ky��K]����#���=���HS�:��5��j�(��O����|�{�s���9���y�����y��<���Z��y��+�3u���vk�������<l���7o�^���������s�6��������_�������x��]�������P��Fk��������t���qC�����_|������
s"�����7�����pk�'�����x������~,��*�'7�k1C|r�=�5��Ol���`Nd_�"O~l�������;�p{�od��]W��G�������=�D����Nk��7Yp5b�v����!���?������#��`Nn�9�����'KY�n�9+��}rn�Olm�7Y��`Nn�9���&nBY����+���twww|t0��4s"p[���G,��M� �d�"�d��E,��`�@�,:Xd��"�Yt����E,��`�@�,:Xd��"�Yt����E,��`�@�,:Xd��"�Yt����E,��`�@�,:Xd��"�Yt����E,��3z������7�/^<J�}�����%�U�?��\�H��8/�������]KS�����6&�D�J��S��[��R�c~N�*V��P�[y�Qbx�%c��z������=��@h���i�{���6&�D�J��S���i�`�|���X�*�O�|���[�|��)`e������_�~��������_�����W�)z���|����O>����sm��s�n��z����i�������~�k=K���Hx�����>���������
�W��k�;����KgNs"�seN��k�s�7'���7Y���*������.�5�U��_�>>:M6��k
8��so���87��������Y��;�J��'^�
.�9��l��b�#�R�,���
�NV�dB�����q���W_=�n�U�d��ij���P_��7��������ki�|.�p{"v|�����������"�����vY{},�5����?�Om�
�U��k������R��m�D�k`N���s V��Zs!��G	��}0~t��t�����y��A�����)?oiH}�[��������1u�����\~O��<s����7�s��)���F��u�>��kp�����6'r{��m�3�%r�i���>6��y��5'r��*XW�J�s!��X�W��������y����#>AP�]���[^��Z�P�_W�o�����$���5�s��{��6�#O�v�f�
.�9��q&0�9�&V��,����_!��U]1P)�����O?��x-y����/�����Q�bR=��m5�=7��D}���ku�(3Onp>b\s"\�L��9�E��mYd;������ %V���k��W=��b`�u����m�5�Y�$���N�$I���I9?�
.�9��q&����$V��,��$V��x����9b�����E~.�K'8��c�h1�,��������a�~����qL�[������*s�>=u*���6%O��S�,�_�6������G\����#�����s�_��Wb��q�>���N�\/7���H��[)�z�^EZ��C��RfQ�^���v8��b�����s������9�qk��r��>D���T�S���S��������5=G�9o�'�2'2����{iI�^�{J�E){�r������U��o���~��Z��{�y�����~0��>�w��'R�~�����q�����RO���c�������v�ICujm[�����{N��P=��g����.���������w����~��~z����������=�����t*^�m���v,V�4�}��o�%���2��^�k=.X����w��x{i�����/r�H���V�'�m5�z�]o?6������'
���m���f��9�vC��r�������sx��F��^k\��w�2���S�����>�'���:���:����x���2c��XQ���������Ok����x���p���0' �|���@��~=�[�����5�i��_o��6���i��ZyZ��W�/��zRk����NCm��.S��V�V{�B�v��X&_�S�s��-��s���������}o���c�%�t�����r{��%�]r>��x�oF��z\�T��;���bR���2^��gL���W��Jcm��������z�=^mmW���YZ��1n���Jc�Y��ny���kt�z�r���v.#�T������������cL�$�����[n�����K��S�O��cz�$��:���u`�e��;��"�--?��4��M
~Bk@��y��#��_o)�������u�V]B<_����-u���Zu��Z��4V~�m���i�����z�}�T?�R���V����[����=������~��6R���v-�6%����Z����o��V{��y���������,U���s���G��u��45�Xbi�9_�!���g������6R�+�����<���x��{y�[����=��R�?��i��z�HSm�R�.K�7�����~��.#�����O]�H{i�{��m��c�l�m�V���Z�mJj�����[�V�������!0,_G)=}nY���a��G����#�������������ER���>�:�6RoiH�]O[��+RO�y��m��m����]C��z��%��2��l�z�e���K�1�g?u�=���gF��o���~����g��zIc�����m��i���Sm�����kp��Km1v��p+���5cw����G-��4�n��}�u*m4��>��z�����W��6����6y�HS����4����mZ��/-��q�G]FO_�Dz�S�����}f����yZ��y��z����4V�9���vN����m8�6@[��Jzq|����������>���?�����3l�W���~�7�s�������a/^����>���}{������^�|y��p����?�����\^-������|��������7�W�^�z0U�^u��������1�W��G]nO��vj��e���-��<�mz����g������_�����[�cCn����?:�����O��<>�V~�����o���?���<>3._�k��)k��kI\��lY�l���O��E��e��w��bI}��� V����6\��t���\����9�|���_5'2w�X��1�W��G]�����eo9^
s�f������'?�����y�=�����qm�e�Z��<[�/�2�,����s��m�����AL&���~j2a'������S"@9(�|����Gbr�W��u����,��tmS�{k�1�`��N������*��[��}?����b�<h
�_�>>W��j�Z���)�����_~�������D;G{�N���Zqwww|�`IK���ma��4�~������D����g���Y���;���U�gNd_�D���H�g����ESe�rK�k�W�0'�s<��c^d/s�E��k�9���b��XjN8�Eg��`=�^���J�!��Z�@fjub�6�hK|��W�G����k��%���1��G��������s��{�������.���?���G�)>!�c��VD_�o���_s]Z\��E�YKc� ��i�GvJ�����
���9s"�D�l1^=��{��c�S���������g^d��V��E�1'2M��mXdqF>�q�����`�b�'-��""��z��:��y�V�������A=9U���b`^��T��5����c7�����F���'5�cN�o���/����R���2'��K{������=c!V\s"�aN���Z��W���-s"<�E������</sb�9�ibl�"�3���d������|�z�U�i�1u�{�]����F�i���!e@)~W-�?R�WO��g����~MC7*\��-L�7����k�����C�G�������K��b���9s"���Hs"<�E������|���y���a����o|���=����=>1�E��� ?6IP�"=�f�wu"����^���44_�������������o������������K���%Lr�cr�k��
����y���v7'�9�g��9��mws"�2'��,�����s���/���N~�;?�����?w|fZ����z���*�s���U��� /���`9�:�R������������?����D;G{���~��L���Z=�\b�Z��1I�����,�x`Nd_s"�|��������m2^�e�����~����i3'��7u���kc�����������+�b��7�{�\V�k��7���m��7o7�>=r��m������XZ���>��MoyCz�O�>��e����l��^9��2����G���F��������m"��#���<S}z>�5c��>��u�j�-b����5�o�5�*�\�q�"^]c���qWk|0dj4W.k�8$��4�n�����m1.����]���F�ciO����6��
��?��k�l������z��K��+�oN�c�}W./R����������L���X���c�R�u�
���C�~�x����k�$�d#��F,���J�Gm�o�������)������z�fO�����}8"���]�o�M���Z�\���ZK�,)cOul>��([}a/��1�K��#��U0�ZcGs"��������k-�D���2'��"���^l�Ai�f������;��vi��*{��u��n������������q��^�K�Z�#�)7�u���P�*���#s��t��yFr�
����s"��3'����s"�),��	9H��J�{K<���g_�I=�W���m����O��9�y��M��Zq��'2��������y"���nO_�U��KK���`n�9�%�~�
����9�a�D�-���9s"�i,��	��3��E ��������g�)u�������V�������a��P�5�\��s&lq��Y�F��Zq�V��{�����-"����E���/_�zm������V,��t�T���b,�����H�9�Ek3'b�	��Zq�V���D��9G��`Y@�<X����w�_<#�7~YkQ��D]�X���dj%o=x��������5x��0�>����9m����n���ze�����o��6��K�tj\�������O��3����1���=�"���9	�c��/V�r�D�1'2��p	��|������h��c�X���N�S�k�~`p|���w��e�������v��Zr�s�!��4�>�r�c���<�r���_�]���M]^K������sIC���?�6��-y�����vC���V9����[R�#K�}��}A���ch����8�H�MI�wk�H���V����>Z}zIu���S��
���w~}h��('�k�[��1��v����z�x,_G%]�k=.X[�Z�X>G+�����&���S����s9_�!�q�c;���Ey��1P������sIC���?�6��-y�����vC���V9����[R�#K�}��}A���ch����8�H�MI�wk�H���V����>Z}zIu���S��
���w~}h��('�k�[��1��v����z�x,_G)=}x�7�N
�ci�~��c���r�"����iH��{�X���zK�u�m���N-K��HS�M�v������>������^�u_��
[o�;�>��)���~�v�JQn�1.-�%o�����8�&K������u��uT�5������b���Z����v��W�r�"����i�����9�����D�������p���j�������[�6�D�|���B�S����|�T��d�_���UU�k���b�)�=���og}�Zl�E�����:�m����V�������Z+��l����5�Z{��{����f�C��i��c����m��/V�������k�qQ����s�f�9�U��g��eN��8vs"��#��U��'+/��|���"5���OV,��k�?&��w_�������j�x~Ji�:oi��2B�;�MK�z�!�����oi��|���:���.�F���u
G�\���h-�_\�?�2�����S�s����Uv<������}�|%oO��<=���oN
�w�Mz��z�x*_K%]�k=.�B�N���%������-�}��+�1������gLS���[��w\���m�R��Cu���vc�[�6%_+o�Nc��K�Qp>k]��7�5�/ZK�������������*?�n������)e9_��S�:O�(7���FC�j�^�������RI/�y��fo��9�z�����@�a�$��������/�m�	#�^�-b�����>x������q����8�$�-b��5w��B�F}����G_#��_5��_�g%��"V-����8�$�-bl�"�aX�����#�SVG��k�E\�&VC�9�q&pi�5jbl�"�a��5�>,�
W�_AWX\
q�L��;��q&p��52�
�? ���F��(����\~��^�������kb������pm]�q}�Y�8�T��X�j��&�q��1�p�{mL��`��{w|p�5�X�0vd.�L���k�
��.�,a��	nOY�e�+c���q�v�U����6|�p-��a�	<����`���d|PV4�
����������p����%Vs;��8x.���%V�~b��o��&��&��,��`�@�,:Xd��"�Yt����E,��`�@�,:Xd��"�Yt����E,��`�@�,:Xd��"�Yt����E,��`�@�,:Xd��"�Yt����E^��o�����?���EL���M,��`�@�,&�G�$|�
IEND�B`�
figure-2.pngimage/png; name=figure-2.pngDownload
�PNG


IHDRM
����
sRGB���gAMA���a	pHYs���+��IDATx^���T�}'|>��� !���$lg��L
�,x�q^�2�$�$z��j�AS����a���w��5�����d��3��x�7$�xEy�y�z��l$��HH����@������}�����}���|����9}���o�s��yFF�J%3#_���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)��^��00~���o��Jy�����z���O�����W�^���YS)I����~���!�o��P��0h�&�(��n��k�W�xzu�`dd$����~
�tU��Q������k�V�7������N������7
4�c���e��\l�0�D��x�~50��_��j��B-�=0}�P`��S4���v���A����/:��
*�g�&4��,�_|O��m�������m���j��j��j���}�������5�it� N%HP�b�D�!�[7���mO@��B���%40���-�Z���yN5P0�]����~��Q�������&JB������T�_�>�C�U#����D��m�Y(�xN���@��	�	`�L��I�#��A�'����x�z?c_'��'zOh�d"8��������v���FGG��={�L1�6�������������>�T���L,;Q���b��-�	�>�5�wk��M��|*g?��x�u���}6��vkz#��l��%�W���p���E��[�fM���X&���&�0�M�c05���P�n"���{X��7�&
,��;mt�h����������x������Q(�VA��^S��K��40�)>��}���?=�����
W�M�������s	�n�"���n�4�M��h�~[�n].
�!t��W|?��������;�*8����qz��
�	�!�ipB�0�V�^�K���3��{Y|?�����Hp<�����o_.�-BC*������m�������D?Dp"�����,�m�����:
��	M�;v�Rs�g��A
L��������'�O�z����.�	�!�e��\�����v�#0:����E��[�n].������@7M�vg)ip��v�Lt��W/ur�z�g���Y�zu.�BC��YJ�*�c����L��FFF��j��4h�s�����}���AQ�������V�S�~�sDg��F�_�S��v�u���_������cm?����4{<b*�c2Q���a�A�����.h�W�����R{�����������kv��1_��M����K�O�}Z�~}^�����M��O��v����+��
���������n=������^k�z���}��o?ZM��x���>��>�J��3����n�6M��mu?���kd������������j�������xn��1��m����yo_��A����������M������n�6u[��d>�jS<��~Z=�:}������X��Z�o��th���1�����l�40����k�g��]�6m��5����n:�g�������}�����8���������R��_{��>��MT�W�G�W<��Z��k��u��T_����|�L���~����gf�>;����tG���~�������/z�~,���d2�k5�����fj��������22���N���/��W�������%40����8�0��v�����i�n'�x\���^����"����n�Ob�<`����I�^#�����&^?���n�Wb[��J7��H/��l�1)�Vz� ���cG.���Ct���[�k5��x��+`�3�����^���k���0��t2l@	M0m��K�V�Sk����/�ll���A�FE��n)��/�sD{�<���_�����t�s�[�������b�E{����������5���u6�{������c�����~��=8��=��{m���}�@I��Bk���
�0�(��:�W���E�u��K�R�u���������i����s����W; ^%��T�����(n�!�^v�+�c�-�z����`-����>�������~��z���F��m�B%��h.����v��0qBCn�N��h��e�n���L�)�v��Ur��������Q�����>���w���wz0����������7:�
W�����!��k������w���D��d����z���}�������R3�!�N?;B�yk|<j�_7�8�=�7���x��O�f���{O�i8�n�O:���mS��Q����m�ri|������z��>4G�0a�����%�.��c�h|�j6���?/�?��G������M�O��x�����_?��*��u��f��LD��u�����t�c��a���<wE����}��:1����b�"k�8v��M�x�Y�&��N����}��u�l�N�/���d�����8��w������['�uLy<:}��4Y�m�6Md_���c�����h����u=������Wlo���M���{<��<���O&��t���S�W�b���Q�u��D�N�z�:}�J�����E�v��A�Lz�b�8�m���9�~���g��I�������V:=�p����_��Nt�����6u��e������v�����L���/�6��F�u��x��y��r�|+������q������x���o����vgn���c"��D>?�{G��{���������G� �'��:}���������m�}�)��>�������t{��V�����������M<w���sBC���'��n�b�Aw�:i�����������d�{5�JQ��[���;��7��I'�KqM���. �(����%�o������N��Mf?kb�N���^k7L�T>;B����j����&�~&��w'������a*:���}/�.T��������D�*E���)�K��3���&�T����n��nk7Vv7����-L�!����NVQ��[���[������4���D��A��xm����D�v��������'zy�s"������8�9���[!�|�}��l��'����>��q���^�u���D�0iz���!BS��Xm��q��k���&�L4�v�=v4�M���D�j�����V�{?hw�m��m����E��n�����g�����>|��Z�d���������o�~�����N��fPBa��u3H���v�e������k������V&�D|Nu�s�[�gZi��Y�^&&*���zOh`�DX"�X�� N�{bh���������n5���������2h����n�
`0��Z��5��3�[}.wf�g�E��c�IP��������z��nE�tO�2;Z}?�����*�A�nj��=��v��h�*�ks<�x�w�n}W�5�	�����hx�)�DW��������v��FGG+���S�G������.~���[����A������~=��3����k�n��:n��B/��c����^iw���x��1�F�z?����� ����^��t2�D+�UE����s4�;*k�=�&4�G�D�����jc��4��q�c:�x���8�S�GL�(�n6��k��f{Q��[���k��������8��Zn���Q�3�{���i"�#D����+�A��
�zr��x����������D5�2Z�f����������w�g�'4P2�|������[���A���q@�W���2=����* �n�����h�>C�u������n�(���T{m�����v�w�
�����`����o��{8(�a&4P"q�X�(S3t|�~<��<�8�2���[��OW@����^j�����z�>i��A�=�U��
{�c�����@oM�@��!0�p�4�c��n���oE=U��g���e�I&�A��M���VA��*�gq����q/��a�V�	E��,%��j�S��c���w�N�G
N�������h���gO�3\��x822R��6�~&�(�~w����vzy@��������7R's���Z�2�+ 0���T��s��v��';�E�u{�
�q�x�c��H�V�)~�u�xNt��������P�������P��G4$���V�c:�8����������������������E|_��x��nN����A7��h\��m[.��j��v��\��X�^�(�h$�M��Y�(R��������`�ptF ���D�D�#��V(*�k��=~�x�]/�N���o�%�Jh�����~,��L�n\k!��%B-$Q��z���}�3~	��:(�kt���9tH;���~&�:�/��\<��z�����QL���&�#,!$A/�C��v��|���n��*�R��w?����}9���"�`Zl�����D���+�^=���0����Q�\u�?�����^��!���{_��;�&��Lt��/���Dm��h�5>2�m�W�����=�a�*��.$�*�#���q���}5��D}8���)�$��_�����r��ynw��ti����g���x��~M�t��B�BB����v�r�g@�M�7�Eo�����#b�G80��iw�r/���MQL��dP�:����;����8DG�a��������dj�}�n��kh���a��Bh����N���%��D8h���a�?^�!:��SY�J��pq��UP�����9(�	����Y���gR�^��<��E!H71�����k��=����T����v��2�5T� �������ah�Dh��hw�c��K�r�}0����a�~��k�����������n��aB��v����������a��;z�chh��j�k��
�	M�s�4F�"0�,[����v�&����a��:�V���v�z�o��}����� e����@��#<�O�{"`���m�ri8�{��w�A4�!:Z�]���@1	M0����9��h����k�6M���	����)n+����.p���x���~�s<����D����V��V=O��va�"
rO_�C���
&�x"�2��F7^���=��6{���6�:y�xL�Nh����+��h�5xo��=�����i0h������@��R?�A��jg�O���}�������/S��� P<�q;�W;��={��R��5�B7C�.���z����}n����;(�{J������O��M/������#���`(�;���I0Y�F�k��u���~i����vA�^����v!���n��1.����"�L�9�H�� |'�������t��T��K��'����.6hZ=����V���������^C�(�!4���v�E:k��;���Y������E��Q�~�A�v�{�cR����E���g����si����.|�A�&�<DO�xvp��j�|��3>�7�^&B��`�^o��?�.����I��J/B+���ql�9Y��;������>A���`�u�;�h`��Y��O���<����G(�NBS}��k����t����s��A�N��~<�m��m��\�.��d���YOt�W�N�y�����{���c7��g<��v����V�[������#B��q�$0����&��NpuC�
�0�zy@�{�a��@Q�O��>U�^k�?j:{���W�����2�2Q��N�w|O�G���x�'���xb��[���#�7��_'���h�>	�u�����.�z�[��N����N|>t�]m���	��]��T�t�@
E���[L���'rP*�c�#�8����J������������B'A��<������&��
���d�q[��'&j�������Z��}��}7���Z���������&��)rH����S|��.^;����S}/p��}��K������X��j(�h��C��[x�����ezy
�C�C"�{`��G3��+��g�{�N�$������������"�����H�!�������x�X���>��>�<���������>���D��N�}b�N��a8����_o�������1��%�N�5��s@w	M��4�uz0'�kq��q�
��6��t������Y;����[�?:�>�NP�{ ��?#������jm;��2���{�����l���Q�������^<�����4N����g{3���:=`[�O��Z��j�+�m<�z���v&�>i��F���zq��p�{��B8��=�������o:=�Pk���v��6��h�ow'GGG[6�<x0�`0M$81Q�9@�3��K����T;��At��Q��c:L&8�(����b;�}j��DM�>�^k�S�kw�z�j"�_�������}���������t9�������o&��ZS;PP�:g$v���o��\��5�;����0����(B@���L�/�ov���n��S�jpb2��&����~�O���n���������`r�&��^7PG{}���P�4��-�7�:x���aTN��h����.��1]�;I?^����:	kDpb��N��u���V���-q���n'�O?_��*^��~0�	@o	M�w�h������
��c,j(�xmO���8p�ktgX�{�W@���t�x���}l3�]��c��^��O��k�M�a�Z�S�>T�o�����V��-����j�y�*�Lk����'^�>kzOh�i
�����v ����V�����hj��hH����w������6P���K���N�6j���v�~��G��������O���
�����M��:y��n��FFF&�����o������_������4>�h����4�"v�C�Vj���j��1�c7���A2r�K�h.���5��B�
���q�Z��VC"LMt�g7��"�"�����Z��	�f���L�����S����-���I��N'r@���}�8p;��<n�v����B��A=�^B���h����f�B00QB%�� ����%3��D3t36lh����4@Q�J bGO��zL�N4L�	�Dh�����f�P��b���M"���s��n:U�g<�CP4Bbdd���u��)����Wj�������-�1S�'b(��L�����?�R0"4�L/~���=c��4��LDO&P$z�(�n�8��[� 0���w�LPDBp���\���[�����E4�jh�K�Hh`@���D��[�Ml��!���,Q`<�����{sm,�K�Jh`@�^�:���}��\��L4;�����D�F�h�S��m�r	�ed��\`�����Rs���Ok���������N�U`"Lf�@��w�p6~��;D�.����kP,B$��X�vm���y�z�/�9B'a�����D��G��"�0�����	(���&|����&P?�v���w���|	��1�F��m8�L��m�r	�Kh`@m���2Fx���m���x0n`��7�;��5kr
�Kh`�����	"Lt���ZXB��d��KL0,F�~��e
����������}����{+�F�3F��[�G	�������[�w
�'VB@)�(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�Jid�������;������6�~�g6��?�5��F�NB@[+W�L�����j��t���\h��@)	M����1.��w�E����/����V��9�y���<�
M������AQC�<�z�m��JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh(������
6��������e�T��Q���8p�2�	M�"$M-$�u���w���4����]��2�@sB@iD#A�F�ASKL�=����0>�	�4���"0���?"<��M��P
���40�~����vb{�G�zB��������c��itt�2�����)�����\���\Ohj@Q8p�i;F�&��-[��9�[�fM��O4�}"��T	MC)~�LE�}��\+��D�"<�7N�]�6��	`�D`"~�LE��-c"��z��m����m��&���K8[(��~8����cG.M\�8�l������0�aadd�����j��}�4��-[rirV�^�Kc5�=(�	���+��]b���y��j�~
N�j�A����M����P%4WE�S3��A���q����b(�V?����?�q?���.bjF�PFB��Z�A�h��;P���8��kH�>q&���h��)�*m�/�;vT�h���0�n���%�;B�F�h����Z���!!�������� ����d�E���%�=m�O�n���>�l�R��M#�8$�����?`M@�x�SgaLG#B���8#�.�i�(��&4M�k<�u��I*?b{t_}X��g_����Q.�������UB��x�qp>�F���Ub����������$,���<"��l��)P%4�3+������"�ivF�@u�0H�y�f��Nz���:�Fa������CQ�������qf�D4�^�����t5TD#� ��a�ugWL����X�jU:|�p������
����;��c<?�pe���D����@h�j��0���~��.��^B01BP|�<����<W0�����B�%1E<�(m�K`&FhZ�v����Lm��U`"l��-���	��H�m��]`b���z
�qM@�a��c�L��	M��L��@	L��	M@����%����c8l��A`�@h���?�FFF�>��u�^��Q��w��\���tNh� &���&��*�^4L�_�^`&Ah`�E`b����v�L���G`&Ah`@u�&g��4��+W�C�EJ'�6l�c���e��\��f�K��V���T����~6�x���hn��U�����V�����7��6����6��~8m��5��7������@h���'4#4������&���o8�����s��QC`�Ch`@D`b����v=�	�.�	�!0�%406l��K������f?�p��wo��%0�#40��n��Kc�_��ry����O�����^&���'��]��	��V����%���&�Q�&L�	�ir���\���PJBp����u��-[����g��q����9�y0����c��Y�f��� 4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4���PJB@)	M�$4�������$'�<�&�R�JIh(%�	���&�R�JIh(%�	���&�R�JIh�@~��422��)�������
6t�>@'�y0]�&W4�]�6m��5��f������6D
m���:qV�x
���by�A���F#W��(�\�2:t(���:�������}��U���Y�~}��gO���g[���Z�fM�U��hl`��cG��eK�1���&gt���\�j��t���\+N�AQ����y�O�mB%�x��A��ej�khp�ypML��1\#4�����d���c�5v#
�4����� ���m��mt����3#�����$�5���'[-�������1A�B��"41���J�����|�hl���u�r���@/i����&�����s��U������T�z��\j�U�@�i���F�N�QX�re:t�P)��3.�;�={�����l���&GG+/��������������&f"�}$������N���.��-�W)�r���t���J��w,K��c_��jV�Z�>�k�i;(�~��6����@OC����m���Rs�����������y��t����k��<�n��\~+0f���K��/��K�*��A���n�2����f��\kn����4����0�>��O�e����7V�����������t�5�q���� ��@/�c�4��zv��0��&7l�0��`��78�|.�����s�����Gy$��w���t����i��<�x��;�KP�C�'���(�RZx��4���������Hg^��k)}���_����RL�y����M��`�h�����l��"��z�[�l����������������\���Q"z�������x��gr	�����7��N=_)�]8+-Z��G�����3�*���|w��O�R����+S�M0(�yh���t�y�c�l��=��V�^�K��n��\�^�p����Z�bE.�W��&���~��r)U��V������D�_�6m�"41$����3���N���=e4 ������2���e�r���e������R����'b��W������0�`�h����K��?�p��uk�u>�g�T��l�����
������,Y�����k�3��\��N���z(���3����;��'��?���VCu��33�0gFz���t���1=L�V�z�p�/�0��s|�`x��6m����6��!Q�&�#���{66 ,]���o�A��z��N��^�~?L����s�Pz�o�(l��Z�>�����XBm�<������C ����c��\��:v�X.�*�������b9�	�6zMhb<x0��V�^�K��3-��#G��0�>��������������y��<�*�1�s���+�� ��@�E���~uUY\S��2L����ts�-�����ct���)�/_�k�2]�@�]�r9}�GG����"��13�h��L7m�<z��C�[�T�[�.�&���'���������-[�6o��k�t���{���	
E�� 41d&�Me��eK.U=����41��}�c���'y���k��\����@/M\�����o��\jm����T�f��\b�u���qc���O�y����@?�������YL���&����uk������
�M���ks��Mf���b|��_�t����p�:v�X.U9r$��n��M�4ySy����3������X�<��yt_}���D��7 �_�>���'�&��A�Y#Bc�Ah���K��eh"B�&���n�o�	�Fh�����J�@�������G|�u�����������~�����76�z��x@E@�q�n��@�M0Ft5go���+�l������;��OW�����x�&�����sij�����/�����=&@+�<���;x�`.u_�E1::Zi h<#�����@�i��_F�N�QX�re:t�P�p��?��?�k)-]�4��7/��'���hXT����|���V�Z�>�k�i;(�~�x�yt_}���&�R�JIh(���Se�{Rd��kd$���q���\<��/����|$W�����vP����h������	����z�vBp��L?m�W�v`x���&�R���S�*@7	M�[��Vz������;��������L��0PFGG�W����{����3��3g��kR����X&��,�	``\�|9=��c��'��s��eb�X`2�&�i�y�������^z��\�Z�hQZ�dIe�r�X6���	��"�k���t���JW���#�<R�����rx�����C�r-�9s��;��3-^�8-X��2E9��u5�N�0QB@����P���+��e�����b;���7���\����[����s�����z��tBh��K�%��~��\���<������������ss�z��D,SS�.@��&�)�pC��j��n�0F�V}�(�7�x#�<y2���I�S�L����	`J"�a���������Kc�m�m
N��8u�T.UEO�����R��/��K��&���.�0<n���\��t�R.5C������r	�3B��Lw`��S��T.E�F,^�8�R�p�B.5W?G������&�I�G`b����\�q���\�l��%����3g���s�z��ej�����0a�R��P|����\�:y����$jb�+���kU��tBh���c�!�c���Rg&r;z���������U�r-Uz�x��+���g�V�(���^(b�X`��&�	����s����w��z(���T�����k:3����&`8�[�.�q��Vu���t�����z�l�0B��l��1����C�%6m��v����N\�,"l�ixb��]����3��>�����<��X&��u&Ch�X��N"�0��D���D���+�R���+!�{��'-\�0_�*����2�,�d	M�dh�G}4�������+r	?��?�x��J`����de�r����Jh�X�P�������D�#G��P67�|se�&�	�c��-���=�����6m�����?�zB@����	�n�JIh(���7���	M]�k��\�����?��Nh���c�ri|+V������qc.�������f��e=B��^,�9�K�	M�$���#�t=8�k��Dx��'r	�=�	�c�6m���"8�I��DX������ub�������0!������g�<Dx"�@t�Em�X'�1�G'v���K��&��G���"����"��������T[���8���(�	`Bb�N{����Cs%4L����si0L����	`Rz��\�^�z�&Ch��*Lwp���TM�6�����w�0!@s�W.����7�K�A!4LI'FFF*�>�K56m��k��:��3��/mK�?����O�?=��_�\F=��~���$����"z}��z���G@��0����X:����<�^?~$���z�?����,0]�&����z����]�����k���>�Z��by�� 4t]-<�BD�a�!��:��m���.�=G�z�`�U�2of���Y�����\F�^,��	`:M=���Em�@D�T]mA	(���?=���Y3G��o����yNZ<VZ0kF�2�1�
#y�j�g_x&��Ch������{a������N����U�&��y��z�����B@W����r�:$G��D��93��q��o�@D�W�QX�re:t�P)��~8��V�^]�\�fM�r�z��n:�|:~�x���t��4o��\+��b�m���ry���W.�x����X1:Z�H�Z�jU:|�p������Ms�<�y�0z�r��'��k)�u���P��<w)����\K�g���42�u�`*���&
���i���i���y����_��m�6����n�h@����v������\#4�t��Q��(����7����B���������wx}������r-��>��t�{~*�������gC�]�������r�gO4��n,��{��\_��	�M�@1-z�r�������Z�r���%�������[��ZU��c����F�|�����z����~�!8Z�
�@qi�(�R���+r-��o\�������
����9
h����������m5�@���`�e��\�f"����kX���n���ci����69���k�A�i���6�h��������ZJ��uNZ4�y�������\�������t����@o���b��z�~����3%�uY�8�Y�A�����;x�`.��X�lY.PD�<�o����KU/��F%1�����7r���u��K�!4Q0�#�~��N�thl��o_.��8�Y�AM��6v�	��k��\`�h���'��������<Z�I��S��s���KW*�Q��o�y���X�����@ML���w�]�AM44�Yq�m��<w�����v':]�*7n�5n�W.����7�K(m�����[V����W�_N/�v)=����e�����@�	ML���u���R{��gQ�e����G�V�{����x�tc�`��}������?����O�?=��_�\F=��~���$'m�������8�J,�L��i�]W�UE}CE���Q<�>�h.������C��)�������G�����������r0��y��9b�o�Ez����KW��UQ���~k�&�i%41d���68p ��L�Y�
����4408v���v���k�w���\������Z�������b�X�H�@1����t���������3_�v��3_�\F=�/����$���(���=k����
���H��u�[���k+��5��Y���m4k���q}�={��E�i��\�-z�x���\��e��t�M��{o�[��z�X^�m�02cf��=?U�B
�C���q}4$tr��m��4"�7B�����_o����D=��C�TlG������������#����Iw�<'-�?+-�5�r�w�:'��z}M�w��gr
��6�!���Fa������CQd@��������X��n����?���Y�k�EC�x�7����-��?��?�k)-Y�$��3'��g�������G��e���Z�����/���;w�\.Ao}�O?�N����Z�&�m~�������_��kW?���G�{~�7s
(�y���R�Z�*>|8���vP��.3m�i��`:�<�&
$�~O����at���7ggl��������Q���>6{����{&>�����#�<�k��e���3�&��g��O����L�N|�R�!8�G�v�?u1�z�r�<k�{����n������+S�M�/�<�����t�y��������2����fj�P6���c����F�|�G�E�`����V,��D7�7�W�
L��;�w\�\e����a���~�(�V�51fg�f]P6��
qFE���-[������_�0�iD(�M�6�RqD���r@?]���\��;�����k�m���9
������IBh���{���2�����K�������~1��X�������a:�W�p�����<��;�K�;�W.�����{���]7�J������N���^x�R�����idFg�`8L�������6���ye0�mB����F�Y�h��0��������S756 ,]���o�A2��M#8�L�@�C=�K�7�Cmt�p�p��M�?R)�2of����������^=�R�q����7�R)�������9m�'4QPS9����������6��������h��������	���/mK?<�x����[��Es��q�����W.�ZJo_�@����s
�7�&�m�i��������0�Q/+hm����KU/�~#��P�E�Q&��zw���\zE����(�u���Ro��v�l�]���x�����o����z�2��s���KW*�Q�&���X������6���D��^�:��<�K�5.���z����%���c�ZN@��^�`�e����W�_N/�v)=����e�����mL��i�s�����^c�@�qA����D�M�-_���L�����q��X.��a����"4Q0�K3��oX�~}.]3�1:�m����������kH7.]��VE=����]z�`�i�`:MLc�k������o��KU��m��k��n'�6l��*c��]�v��z��v�=Z�FGG+S���n\zo��������J��/~;���/W.���uO^��6���D�D7��g6���4��?����+���x�E�F����e��\cXm�����K+V�x�v�-[V�j��Pt#3f����S�K(mL����hV�\�:E
���{M� ��l�>~����'���n�a������������ZJK�.M�������F�^�^Z��mG���w�N�6m���iw��LV�Z�>�k�i;(�~S��c,m�W�v 4Q`�~��g"?�'��0��A�X���n���ci����69Bp���E��5�<������?�����k������v��`��hG�����!Q��������z��q������mOg]�5�=Ml��9=��#�6�����&�=M0�yh������	�����54q����l��\��t��L?m�'4����0V���]�v�R�6n��K���{w.uW��������������&:��Hh��0BQM@�M�����}�m3*�R��iS:v�X��������>�K�'L������;�&lp���\hOhH��/��b{��'r	�=�	�b����TL���k@Q�:u�2�Gh���iS�����t�������?^9	�_�Be�r������������R�<��C�0�FGG�W���J���<�L:s�L�&U�1/��ebY�?�&��D��H�M�����r�������KO>�d��\,��:@��\�*q��+W�C�E
�������������K��y�r�|"H0G����-[�,�������#Gr�zO<�D��WA		n�f��U�����V�����7�G��p����v���E���s�V�.\H�O���k�;��>��\����@h���a���&Zi*��m���\#4�O�@�������ZJs��I��v[�={v�S��o��_~9]�x1�I�c�X�������s�H}�3�z���&B�[�xq�U5�t��@��8q"�RZ�p�[Cr�'z��ej��zCh�b���'O�Z�7�v���uc@�M��C���:u�T.UEO�����R��/��K@/M�s���U��~{.U]�t)������n�-��^����5b�������r����8b�N��&Oh�G�,Y�K)�9s&]�x1����X��~]�7�&�i�y���0�V�\�KU'O���DM�{��Wr��q]���&���@��]�*���G+���O�<�Hej&�8u�Te(��K��U�V�Z��4���/V�g���LQ�y��P�:�.�[B@����P���+��e�����b;���[��Vz������;����������u��w��kU�O�N'N��LQ���:@�	M]a����T�������'`��{��_�j��{wz��g�������2�,���9sfz���}����4�����{B��E�!z�h5�F�D#n�>P���/��{,=���yNs�L,����������y�=����kR����X&��Ch��/DXb2Co�s�����Xq[q��0|����^z��\�Z�hQZ�dIe�r�X6�(�����L<�@�}����de�r������&���.�0\���t���\Ki��9��;�L�/N,�LQ�yq]M����7�\���#4L�t&j>��O�Pt���7r���[oM�g���kb^�'�5��u�������\p��0)�L�X�"����:�=�k@��8q"���x��;7��=M���Y�.P����M��s�7����c�vm��?���e�c�s'��%�Kh��A)Dp(�7�x#�<y2���I�S�L�����������������L��46���_�xe9�2�&$���lH������:������S�r�*z�hgdd$��^~��\�r���=����~!�Z��by���&�������v���z�������/_����DBz��b����s����K�����h.U�v�m��=G<���s�j��Yi�ms�-w��\F�^,��	���&�	��qc.5a�Kl��)���3���YD������]�r	(�x�/^�8�R�p�B.5W?G����0��;qlL3f�H7�m^Z�d^�w��4k�
������W���������PB@�bh�v"�a���%��&:�P<K�,�����9�.^��k��PE,SS�.��y��\�Z�xN��`l�51����������@yM�dh�G}4�������+r	(��+W�R���'��&Q�^y��\�j\���/=�K�!9�&j����:��(�	�c�B	�w��j��9�K�0Z�tiZ�jU��JO/��b%<q������W�E��B�]�r�2<G��93s�����{�Xe;e"4tl��e�4�'�x"�zc��M�4�v��u���;��#��N�>�N�8Q��\/��u���������t�5�q���;�	�kz��P3g�L>�`�������b�X6���{�������+��Z�r��vB�@I�������=���.\��I�r���b�XHi�����w\�q�������_.������P�7o�%�~�'2=�����'?������c���{s)�g.��g/�����X��~}����f��]����.es��7W&�����|.U�9y�ip"��~��z���>�K�!4t���c�4�+z;zt��J�����r9�����\t�����~��?�k)]�t%����������ko�K��\F=�_�z}M���%����?N�:U���#4t��e�z6�F'�X9r$��x���Lz�K����~4}��OO~�*�Q���:/	��GV�R���V�ZU�q�����U.����|�@|�[�J�?�x��sg���P����:���&��uJx��G������e"<�������������~x����������������r�|����D+�\,@������~��i�����g�Ig�����J9��u�L,�����M�6�Rk���g�v",q�����:il(�o�����>��\k-������#�����~������;��������z�����/��{,=���yNs�L,��7ru��W�\�:E(���������ZJK�.M��������voD�!�����c������D�Dm�k�T����.Q	�NC�Hp�O�sDc`��y3���3��Y3��K���7��W�����|:����\h������?:����i���y.@{�V�J�����u�a<�<���������,Z�(��;�R�p�B:}�t�\�i>����M�mz�&��G���"\���",����+�T[n"��`h����O�	L��9��}��t��s������Y3*�Q��s������zg_x&�����{���	�i�����	L��3'�y��i���i���)�1/���ub]���&�	�!0��A�ch���}�1���Z4;-�;���Esf�;o��kU����� ��7��KU��zk�={l[O�y����.�}B��-_�<��Dz��Aq��o�RuH�f���N�r5��0�N�8�K)-\���!9�=M�25���}}W�7�g�=����T�z�����5k*�S��mw��=�����l����p���Cu���"zm�����O�?�R���Y��8�9y�Rz��K����|��iDW�@�����vP���9m�<���7�H����!Z�'�E����N�>�N�<�k)}����g
`����&
���i���i���y����_��m�6����v�i@���0�������^M��}�o�����\K����Mf������+���/�ZJ�}������T�t���I�G�6�b��"��� �Rz�;�Q�M��3g���_~9�R��_���z@�������L��k������X����Vz�m�G��==L���w�0!��E��@.U]�t9�Zk\�q;0,�y0,n���\��t�Z/��4��u�m���B?��n��kUq��;����q&E;��6�'���E���/���iS�A���7.]�k)�}�J.�V�\�oh��6�I����5.\�E����&�54���9
(~�������?nw��-�j��^n��tU9V���h�k���b���l��<���w�^�%�A�<��m������}���hN�������_��k)�}����o�5��0<����z�<��������#Gr������k����s?�s�t��9
.�����~���gI��V���f�E�!���pC7{����]�a���_���_{��O��������s	��6�Q8��<yrLo51��W^����u���(���^?�f��5[�l���}����X��6�ROD�x�h���N%b�������I?���s��������$�?u1�<w)��t�r�����z7.�7�`8h�`XE/Aq6k�����/�X	O�={�2E9��u5�N����9
f��
o���17���S)��J<x0�^��R�a����5]U�5]�sL����s�jPB������;��^=r0���e��������[�����y�O��p�|�r��?����K/�9��q��W�W�����s&���@h�`��G7��g=LE/��k�*BhbP	M0���X������������?�k�'4A?i��6��m��{��'����w�}���v^�����sH��������m�Z!V��_���y ��tE�[�����v	L0��yP����?�6n�������p��|M��c^\�L@��]=MD����uk��F|}C@t�i����v?8�b,_�'OO��+����}*-z���]0�GO��6���y�S�NU.o����%�z�(�}���Ru����a_��+j���k+���7��m01���=?%0@ih���",!0�Kh����}����wo�s��>~�8p ��L/�M�����2=z��T[n���yM�J��`x����?�������u�r����������b����]U��:�)�+V�H��-�s&o�����'�H;w��sz��p��9�m�i�������iv <~����'��jl�������a��%i��9�V>�����������s���;�}����'��;�K0x�}�Th�~����6����Ch�@�����~M��;v�-[��ZU/���
����Qd�����������y1��#�<�k������s�\���w������J����+S�M�/�<��g��t��6���V��a��m�T5^7������,X���8�MJG��[`"��
D�(�	�l�y�BgEt"����!�8p ����m���LL�A�j�%8!0��6�%��3<GA4v�8�n!~��1gD���������������;��N����������9�1���z=�P��sg�
����;����yz�����Pf�9�g0<������j����l��(���4'�C=��X�vm�]�@��m�Cc���K���z����G>����s��9�/zx����kp���|g�������| �����_�y47�m�������?:����i���y.@{�m���)��q`;p�� &��>��\p���l����K���O��k�o���W.���;q,/	���Y�n].u_/�]e
N���+�G�_�	�l�y���:�'�3��c����/�s/�
GD=����>^Y�SB�z��\�:x�`.���l��z���(Kpb����41��K�w�~k�!5����Xn2������H��|��J�_��k��r�<@'F�N�Q0�g1���Sv��h��k�q�������{m���|����G>����s��9�#z��Hh"G�I�6m�s:=G����D�"|�s��\+�	M����(mE�����1�Aj��.�sDc`b��Yi�����93����K/�g.�k�~��?�>���r
����=M��;r������\,S�����4V/�]&�����+r��0,_�|R����X?������?��G`(;m�����&f���nz���h��4���i��*�Q��q}M������5��	MLc7�k������o��KU��m���z���)�P�t����N�����:���6��_�|<��.���,��kc������'�,��'4Q0k�������b����H������r�e4���N�����'&2��������x�����T��Y`�&���j����D������q�D�����+S���z��X��^n�������c���������;�����X�<���������M����~^��5��������{��c��43ru����+��C��HA4����D��r��p���t���\Ki���i��y�6��z�����|$=��syN1��V>.��P��M�r��6o��y��\_4n��E�j��t���\+N�AQ��k�y\3�m��}��t�����<�����#ynJ7.]������u�|�|o�0��?x*����z��t������7�Zs�.��^}�\�����������\S�v�����������v��1����v����^&B�z��7�	���y���>��������	L���������r�y��V�R����Rk��5n���&�Dm��V.�����2�f/��-E9���=N=z4-[�,����^Z�v��,_�<���E��	�6=M���������k�����bu�����\��7����!6������%���>q>]8s�R~�������*e�����	�B�����kW��qc�]o�C�d�&(�	�~B@E��}���Vu���i��i���������W���/�k�~��O���?�k�p������7�\���{?�����2�R��m����r�z�^J���|���3����i�gr
�������gE���'����m��9�z#B�L��z���c�f��w�2'�}���x���`���e�c�������}��\^�������?^��_�Be�r����������Tu���J0b<1?�������0>�	�E
N��V����\��+��9Cs*�	���}�1���Z4;-�;3����w^����'�C.
��E��_�j���g�y&�9s&_�*����2�z��������_��O�ZJW.]��$Cp���t����������&�{������	�&E
N<��C�t�VCwLU�b�lY�/6�>�h.
�	����>�K�!9�&j��YY���s����r�����c��'�|2�i.��ec��#�~)}�]+s����K�����/��\F�^,��#4������m�U�G�������>�K���i��t��LF�\Ng_x:�RZ0��C���~�He;�f��}���^���E��%K�T�(��ec�����#cz�h%���:!4�����V�:Do�ND��f�LD`b����68&(��^&��Y�{��i\�q;E�����C��ZJs��Iw�ygZ�xqZ�`Ae�r���jb�X�&z��7�R����O�;�{� �1�s����0L����'�W�V�t��x��]�v�Gy�p�rLPF��5�=����z�h\�q;E��o|#��n���4{��\�&�Ex�^��@J�\�,�O�?����������~��~�r������KtFh�0'"�0::Z	?t��e#p�m��1�_���
�!0@Y����n\�"�R:���\j�~�X?�3LN�8�K)-\�0��;7��=M�25����y������@�`��&`��38���N��D�j��"Q?�o3�m��DM-�a��4�&(���|.����������M���\M�����7�H'O���jo��/��6����S�'&#B��dD�q�N����%��^8�F��������g��Z���4N�:�KU��D;###�T���/��B0���&P���{�O<��\K������W/��O]L'�]Jg/]�\F���\Lo�9��L��n\zo�
��o�=��.]��K�E���n���\zAh��D1L��W.����7�K���{�����s�*��x��K���/T.����|�7l������ZJ.\�������u;��<�	`��M`�n:��3��/mK�?����O�?=��_�\F=��~���$�������1=N�����j��%����3g���s�z��ej��zCh���`������X:����<�^?~$���z�?����,P�s�������5����s���W�����a����+s�����cz���y���J�U5�t�����`�����;������s��X.�(����������7��~���N�}�������w���^K�.M�V���T�i��_��'��=[����{��ub]���&� '���=G�z�`�U�2of���Y�����\F�^,��	��Ff�L7���*�e�n��t�w�Z������'*S������{BP ��K`�nz���cz��5s$���9�������g��fT.���[���W�������L�0�f���|��t�}��9��2�l�������������L����+�e�����KUw-�������93��7�����O��\�FFF��?���q��t�=����kR����X&��#���Fa������CQ��9�|:~�x�U���7o^�
���z*}�#I�=�\�31�7o����s��\�.�������Qz�kG���_I����<��c������oM?����>x��n
����~4�~�H�CpD��<�bz���J���+*c�R��}���\+N�AQ��S�6�29u�T����o�\�Q�v 4�P(k�T��'0����������O���<��_����'ae�u��v�4z�r��'��k)�u���P��<w)����\K�g���R�
���L?�	���o;0<��:zK`�;����_���&B,�k���������}*�����,���\�v��	(8������������>�J�U��S�������+�Q�w��������w�}]]�Tr�������M���.������M������
�������.|[�5������e��rw���X/�F1���KW�ZJg���K��/��`j�&`HNt��Dw��o<�KUs�-��5���3g��\_�q�a�������^=9�������>���_����!"815�����8�R����4��9�6��>���_�,��Wr�����h���q}����r.������k���k�!4CFpbr&��?>�N�r.�R����{�hT�\��F��'��������<������������K���+�������&��q���6����o�����S������{��{��G�����/W��*�	B�#0�4�*1�_N���?�nY�:��b�^���}�B��~H���z����_O_��W�_��_��~��������T)?�����b�X`���!%8����Y8vZr��\K���|�����s)U���sm8}�_���'Z��b�a!�/~���;��N��\,�
N�%4CLp�5���{�{n���.�{5]y�b��/�����6v�a=G����Ho_�@�q��<�*�1�o��&�����t���\��Ee��t��7�;���2E9������dM�����D������p�G���z��w�������f7.�7����i�o~%��������+�Q����'/9����o��ab������J��rK�?~e�r���jb�X`��&�'�����������7�Rz���t�����3?J����:��\F��+�W����b�2�13�����\��3�<�K�&/^�f���1�yq]}�O?�t.tNhJBp�J`��>���AZ����V�������/]�~X��z���������� �RZ�h������.�������R�	�	(��'&���������O�/~��yNk������z��k�����;�k)��3'���_�����������	(��'&���f��K?�?��������;�~S��*�1����RY.��\F�����7z��t���V�&�Q)(��'&����m�������������>������r����{��KR61���r-��/�Rs����7�46�3l�����s���1�����|lVzz�?�\F=��;�7yI�SBPRe	NL�ysoH�{���Ko{��r)U���|�r�]/�����o{.
�����w���J/?�X:w��ynU�c��?�`z��r@q�:u�2�GhJl��P,��{o.�t���t���q�1/��ej����\>���������Zk/\].������������;w�/|��)�1/��KhJnX�P<�������\K������^H���j:w�\e�r���jb�XwE��_��Z���)�}{J��}�rI�^/���)��2::��������w�g�y&�9s&_�*����2�,�B��'&��>����s��Z��������^�LQ��a"��u����S�9�f�
)��;Rz��S����\}�n��Z��s�F���)����s
�n�s�c�=��|��<��X&�m5t)�=B@��'&��n�����O|bL���2�l�3�~�������KR���]��w]�����v�0����W9!���E���%K*S������{B�[�����!��~�������������kR��?���b�a
L�s/��r�:G��DM\_?TG����9~�x:t�P��4g��t��w���W�:b�r���jb�X�-�	`��'&`�|��L����������S��T���_T�������u�l������o�Zu(�N�/��v��u���\����[����s�����z���'4\�h�	�	~7�t�u�T�s/#������6�k��u�������\�_'N����.\���m~vD�4�������0��'&�N�>�^{��\~�o�{�Tu�|.�q�B.d���\������~.��<}���M��'�^��z�����$���o��'O�Z�7�v���uc@�MM
zpB`��7�������J�����~��~/=����������u�ld�
i��kC��mC4s�.\��v(��:�'�3��c����/�s/�
GD=����>^Y��9u�T.UEO�����R��/��K@/M-
jpB`���������������N���w���g�5�R~��g+��2������!�R:y&�Sm�j\����_&?z�le*����C�O��r��X.��7n���\��t�R.57::�KU��v[.� 4�5h�	�	^����������9��2���'�|`s.U���������?�0������4����si��L����J��;��2E9��ue=G<���s�j��Yi�ms�-w��\F�^,��	����G�S���\B��x��\K�����I���v2�0yB@G%8!0��k_�����3f��o�9�q��)�1�&��u���[�N�s������K)}�)��S�����?~�Z���7��^u����/��\^WFG���d�����t�;/�����O���=�����������01c��t����EK��y7�N���P��z��y���X��?�k�D={�������W����������U��W��_T�{���$e�d��\J���3�����v�h��ej��zCh��t'&`�}��������]w��n���4��������:��0z�O}:-���ZU������~������b�;��7���|%m�<������9������,�������T�p��4g��^%jb��W�����?�%`"��_+��g��v�h:���<���s�T���o�Ee9�i����Tu���1�I���W^�i\�>�	`B�+8!0���g���j�����3��kb^\W����O?�K�g�?�Z���ND��|���V��_���9��o��2E�^,�GW�f���� ��h��������_������I���c��k&���)��K��U�V�Z��4���/V�g���LQ�y��P�:�.�[B���;8!0���� �RZ�h������.�����~�K�)z�X�s�L���`����ynU�c�������LQO=�r���Gs-�f�K����������n�LQ�yq]�W�����W.W����5����^�r��t���3�s������
��-Js���zw�2��by=N���u�*C��;}�t:q�De�r�X6�z����T��~��i��
oM�y���������1S�1��}���a~li�_�	�	(��^{-�;w.�R�3g����_&��hl\6��Tz�����?����������*�Q���n�;y��������T5����	G������=�������\��aNg_��k��+~o�~��&m�<��p��W��01c��4��;��EoO����f��W��z��y���X���c{�c��	 >�`�������b�X��	%@��\�F��b:t(�/~�<x0m��5�������Jgn���rYd�@�w��\k.���=�����6������Y�k����������V��l�����}O=�T��G>��{��<�{&(��+����}*-z����r�8��������Z�l�������"d��K/�ZJ�����M7��k�_dOz��3���oI��Rn������W$��ma���6T��$z����]�k)-�mn�w��\k��ko�3/_�������f��s�Lt	}���BEi;(�~m�h�������w���_�o��w�Ysn����y��t��������Y����8��/L�o}�[�aJ���3g��o.\����w�{��'��O�de�;�m��i�����k�N�� ��X7��!�,�x<����� �r��T��vz��C�z���l���Lz�K����~4}��OO~�*�Q���:/9�b���Zul�v���u&���o��3��������uc�&���cY��t�bg�l�/�LLmci������
�Cp�
L��^_?T���?�K�Q�"x���y����O~�2E9�	L@�
|h��p0�F�fj�	��:�d����TgU�����oM;v����7���E<����	�	������C��)�������c���z�?����,7������R������������x��������+gs���Y���i��{���9�����7�R�p�R�x�R����s�*����@h��6`�]�|�2<G��:���~�������J'����7�\���3���v
�9���\��@���_[��uM���"4$��R�C����������3����X/&����u+8!0A�|�w~-}����Zk�\,?������+W���'����D���b�����rx�c{��f�^IF���JXz��������~>������48���<���G��\��y�O�PG����0s��\j�q�#��q.0����J�u���l���F���WOy<���#t��}c~��D��t�Y9�����x��d�i��r��2���x����G>����s��9���l������-�f��g���f��.��o\I����>��^�`�
��|�+�;��N��4c����s�T�bH��a�>0����/}���5������s��������'�.��j�|�m��?T��m'���I�����\���pV�5gf�a�����+�!9�{�����LY�K�pM���a��Z������6��i���=D����Z|��#������7����]��\K���~5��9����������i"�zhl<��YgP�����������?3!�>���Q��������������;
���|@3��qB`��y���c�f��w�2'�}���x���`���e��}��4��%�;��3�6\>����s��Z���S�N��^z�2E�>0��:����v�1���W��������x���D�_wE���Z�kU�8�������*����X^`�?�y���(�:,��\�������I��/����
L��
MDW��g@��n6�RkH��q�2h���!_�q��D�wQ���g�N�������I2I&��[�t�)�F��*k�j�\�+��WPQ�h�{A�UPPA-�vE��-�^��U[,��.��m�6�>I&�$����L����$-i�y��~�����y���&y���sd�Q$�[8��	�EU��-C�3	�d��9�6g$��P����e-iii���k��C�12V�Pl9�Su���t}=������T�#C�\+���~�v�'�����c�ch�yQ4�9�_4����>w��B��2�g��\�""""��6�v�����&�Ms"!�6��EV?���y�Y��bc4��Ptn�&(V�~O��#9�L�H����1��)���6N;�4��#:|��p8�=����4wB.V.��#�8���p5�����^��>s1�
��d����|g~�|L(��{
K�=_~�;L��<Bc������������^��0�R0���o���
�X�����N�1U4!��%�XxH�%|[<���T���(�[T�Z�J���g��	LP�����r��G������|y+������:�}�]ttt��8�OH��r�{2��O��������,�m��h�R/i�����sb���i��e���+���������Ub��g�]7����<"c�������,�x��h<��]-��n;���xz��U�No������6>[GDDDD4��T��l�x����|L�@�e��l��{�6��w�����*������C��	LP,k;�S����w��	�>V��x�f�l��]���Z0#ce��[<~���p��)�'�s����2'V��m�:n����`���1�ADc�e��C��q:2����p5U���H!�e���9�B7n�-c�����$�a��BB���'��7�`>�T�[���>���	LP�sN����;�_��~+��u���:28�N�������d���#��x��87\���B�.F�3E��jK�����j,�]�yQ�z�;�v��D��x""""[X4�$y`^�$�I�H�}���r�J	��	LP����!�t��WO�nEf'��}����[�n�`��Q\\���8�����=�#s)v�5V]�O�r��9�%m��{DDD}�� �h";G<u�
\p�tL�x���	9���{Vr�	"""�1J�����.\��
{d�D8����E������+1dE��7��r�j���s�uuu���RGP����P��c��]�={��(�y<�{�3&��f���<s'���VG��l;���Emn6�u��:����|�:�)�z���u!99YG��n7�9�#`���8���tD����S]2S����jRR����
eee��m���'w�w,c�#<�<��O��<����N�h�<,]4!�w�u�j�*yx^�dI�n�.��>�a_D/+3|+.�%�!�0001�BD������Q�CG]8\��{�	�L,J����a|�C�Z[_�!���&I�8g$�,�������x��l%���!1���A������vzz:rssU;�������]]H����f��l���N�v�����DL�M���,�������@v]��������9�<�������d4sQ{<��(���p�V�r�a��@,W�y�r�
�zx�{�"���T	&�K��JDZ�ADt�^�R��>�y�6�`B�q���=���&���/�� Bv��hq�����~u�X��2�j}}}�
 DRR�n�g#s�=(���[����U�_��
&���O����j,Q�`�����
QY4!�I�����uo��������Y� �Ld��9I�#������ ")�>������"�q2>��]���:24wyP����
��*����yV�r�t�0����W������O�~��~�����������""���9?�<�������T��6D����}d[B�Pc�:T_4�,����������"8A`N>��{���d�9A3���S��{�]������/��!)����������������_x_w�|\�l���������O�(�������RG�"�����~yyyHKK�QhR$����#��n�d
��y^����vg��s�z{��n�����M��?;[GDD�i4��<���b���9?�<�����(�f�#��&��<���;~�+81r<{p��y~���L�hH �����XD4���&\��u��������_���;�n�C�����������,Y[G�Tm|
�����w�^ �t�'�A�Y��Q2C�Z��?|�"==����NCC��#=������N���vn����_	I)H�)RW���.t6UW��_��N��w����X4qj1���G(�y�<s� �����`3yp
N��e������6n��E��m��E�5X�jI�Dz��Bb�}I0����K��0"��/f��O��/�t&f^y��g>�����J,�V/��b�G�!�n�����>V0!�s��^��~�2�f*���"�HG���DDDcs'�9""""""�0��h�|�gpb�'�����X�j�n!`%��=t�8�C
#"��
���`����".������k,��f��DOO�������IG���dmkZt��ey�v��h�{��c�KDD4�0�ADDDDDD4:��h��: Tb`�B%���%Kt��aV�DZ�AD4<�~��������mk���F��D�>��C����|d����jU<�r��K��g��B��\�
]=}�j��2b�'�Vx�12W����h�a��9""""""QU41�$�yKJ+N��0�$R�A��<�W��5�~�&h�hd�6�2!l�v��,x�����E�@��:2������N��m&c��dm5M.�2$$]��#oe}��#""�h��Gh�y���}�%������[�9f���>�W�����'e;�p�3�z��j���I��~��?��0f9��|��XHtuu���RG�j�����j'��Cv�Xt�c:��������z:[��^�#`��W�f�F�&����[�������,X��/�#k�a���:���;"m��hBg�Q��J�$%����"���m������h����`�#�<����Dd��Iy�i�Q����v�����.x5�$�"�3I��l��>!GG@_�nEf7}B6�1H��g�}6.����1����T[����a�D����%���}����x���e�e�E#�<Bc����������o7����I-"��?_Q�������W�?���������*�0o�(�����M���:���N���98A`&�'�Ja��
�M3'��&z������Qh���2�g��\��X4o�<\t�E����p�������O�Q��T��Y����p���	wG����%�y0�AD���Ww�K���Ko���7�^����T�e�}Q�#"""��'��&�+���.�2?DG:���@������f�������$$6o))���L/��[w{�*�E
*z��������E�.>>��J�1�b�n:����gp���u6���<���h�`����E��>�?*'�����hl��>����|Oy(6?���U�Va��-�V�:�R�K��D��k�?�1R�p��G�}?��I4v�*���AR��	)�%���u�#9�;L�/]����n����{��AMM
\.��s8(**RGt��?_�Ql����b���ud�;s��d|������-p���E����O�
��|O
��7s~�yb��h�b�����(z�sQ���<�����D�<�'Bm?)�Se�p����s!���3���|"�M�(�;NGIt����T������A�������k��������
&��������K��������,�H���J��&d���9DDDcs~�yQ4�W�P0oKDjf1��EHJ��-1E]%�~������_�?F�����FWTM�u��E|h�{2&X��p���$��p����?O�?����%L�hz�;��*���q2�b�A<��#��k��	O��XN��[<~���p��)�'�s����2���h,c���9"�~�F�s�==	�4�~�o����t�b]KK�z������m�JDDt�D��frve������VHP nUId�$�l����t/0}BfN�����`ji��q�-�{��`">>N�v�]�n�mmm���W��={6V�\�#�%��x��rl��� ��sKq�������{��h�x<��`����� �.���*�!�H�Qb(�mG���)��'�y����c����G�A{{��KOO��q��������N��G���]�@W���n��{���H�9
�sn@Z���EDD4s� j�&���I)���z������	����`��z����M�Q�+//W�n�8���x�~�t�������1���s�=�����)���������{"s$'���X��	"����������1�A4�x<�Xt��t$���8������)����U����!I��o��{B[�`�/_�������To�jw��{����M�|����O�=DDD�9w3{�V�L���;5��:FVX���
&���=��g���X!;L�&��mH��CZ~�zI[�|d���U�~����DDd=�y�Z�6����h��8��q���b�,Y�f��B��X�cU������U0!�v����<""��������hL�-)}�H�P>rO����������<p$G�#��"%3_m�+/iK����927VT���o����>q%���b���������n�IDDDDD�Cv�4���u+��q��C��q�F���w�����W/s�C�X�cU�w=:7�z������f2��;���h��h����F]kk+:;;u��C��1���:���sa������-c����B��t����=����^�nY�+[�����o��e�
,��X�����j�5s���}]��y��	��}(6TTT&9���b���O�;�T����#s��q�h�������<s<0���r2���3J��>��y2���h$�h����F�H��)��Rlx��Y������>rO���?��[�u���g��E&�d<����E=]m�sw�(4�/�|fN��-�o���n�������#?���	���V�r�E�2�/�:��b�7�ODD�aEm������z�j�K��"""��%[P��
�{��M�cdnFF���dIu��huu����dUL�cZ�s���aU�s��C�tdHNODzn2��S�Ub3�'����<��������-����a
'�`��{������[�����������YVv��1>��V�Y��u#?H��^��8�|""��uE�8X�|9/^��n�M�Y�@���������P�����x������8���"�"���~�;\�/`����xd�������$$&'����o����y��uDDD��9"��1�47^�PG������jt�EOg<�]�*q����{�G�M����������:2v��y�����
w�A��
������p���|y�XS��R/""yQW4!����������\U{���U�v�J�u�l��bu�X�;*����5s�L����UR T����=�3c��"���HE�i�N����D8�12W�������n�s��;w����4�}�����[DD4Z�� "�.��;es��� Gpt����T���#9���y;ZZ����$��[TCC�nE�{�nl6����,?�������{W��5�����=""��W�p�Bl�:��4��)C�����{u���z���E�������:JKK��2�U"�QV�~
<����r�-��Y���k�k�.�����_BA���&��g����+uD���'6��}5�oC�����%�8X���G���e��"���g���|��+�nB���%��V���vc����i����U�������������������9
�9����Ww��g��Ev�`�D���w��# //iii:
Mr��)n���a�P-v<]rl���l�8W5#���t�IRj1�}��,������������=���p
�~�����lDD���;���	�����b��M8�k&���0�@D����W�y�
-k�"����:����<��#��z�!g�^{��C&�Z��� ~���:�H�*T8!��5�q���+>�e��#���{p�O�H�MVGq����
�oO|{l�\�CD~,�8u���p�� ��*���7va����I��'�`��\�l�:��b��?|�"==����dg���v�����u�]��Vq`��h9�VG���@�S�A:����+t��9q%�,}AG�������l�G�uOd������K�`������F�9wU_A�< ""+�&�&�Rl(�H���du��L��<���)���#�"cX0���a��RAE�n]-uj�[yI[��2���`�n�2$���c~����!"�S�9"��6m|6���3���V`��Wa����O������-??_���!d'�pd!��`B��ZE���e��\a��H��7�o5���cP�������������9DDt������""�f�{��H��aR��3��IM�#1^]%��mG�����sU����H��q���b���p8�T{��)���a�D�����p$�wR��7�Z��QW�^���!d����I��t����aI����!"����Qt�y���N�WW"!�^���===:����&�N%"x��L�y���g����
�p-P�����W��_���z��|��y�k���# !)���pd��6�%m��{>2G����^=DDD��j�S�e(q&��zK|�����m�+�|R���f��W||�:�SH;!!A�Sl�JK����,`��pd���9V%GjL(��#���/��<N��h"""""��'G+��>��Duu�*�p�\�%m�3�B!sd����@jN����V��8Tk\%6���������-Cjva@q����f��<����/��&�-[�[DDD����{�e��`�G
'�Gu��[�H����x����w�^�%e\��:G��'�d����bS����TsA�y����z��$��V7�`�n���p�zuZOg��c�ODD�sDDD��d���������:������9V��+���y��=��8/���`M�n��,$�Su4X�=E��1�%"��U�]�? �^�Z�������Q�[G�#ix����d�����x<X�f
�o��{��12V�P������_�����WQ��h�m]C���j����e�M��������x���N�H���h40�ADDdm�k�5�\����d����N�q�I(��O1��� k���1C�1H,�3���'�����U
�:�����c�y���� "��UE��z�����v��E��C;u���8���q��c7nDmm��N�����%m3+s(�<��|p�YG�3i�%�%m3��w������K?}��Oo?Zk��V�����v�����/�}d��|��DDt�1�ADDd}����g��.�3f�@zz����>�'c|�n����Oa�Y�a����/�c����*���.�#�����[�����r��5_e}��%DD4<QU4!V�Z�[�����y�fE����e���N	����'�UVVb���:�v;���������^��>��#sd.��������ud���,�GV!��%m�3����;G�Z�ye_���ud�#8���\����#9���yDD4��� ""�
����E]��n�	�_�zI[��^,�OL�#o������e����w�,�\pDDD�#��j����b,�������BY��a��������~aVZZ�����0��F��;W;�#+�����"�p*Z�h�2
'�Jga�kU�*^x����?��������!t��8r����Y�f��/�Y�=���v���;'�=�������t,�[��/�������O����BG��,� �H����m�6EO� Z?n��c�����ZZZ�533S])�}�g���aO��#�H��q5������$7���r�&"���s�(�����_S4b������W����uL���iLG����M����3.��+���5<���hllTm��277W��ihh@{��);P\w�u�M��@H��#g�j��	��u���;��`����{�	�0�`&�-�7u�Q$,�8����`�yY��;�w�^� ����\��q�����D,�Pd2������;�VsPG��P��h��sQw<�H'���FC�Y������GF�"�r��d�e�e
===�
&DRR�n�g#s�=bEo\���5�t��+�6{��H��cd��G,��?
���v�}��x��oy��QW���DDcsDDdu��~����")f|�����K/��	_������=����t�'��;�����KG~���T�#C�\""��*�`������Q2S.�EG@O���IB��h�����_]%�~��#��Jg��|[Q���CW'f���V�~���������g����c��e�*��w�l�#�����[����WY���[Y��[�VQ��Y�������3������/;Q����Y��j��qS�*}��9�~���*��wT��#����`��5��}��	O��X�C�e��\�\4]GFqD���j�A��U��-}�b��92���NLT��z�j�v�m:2�{��X�h��N�g��[��U%E���^���[t4��Y�0�[���:d���w��# //iii:
���-`w�[n�eX;TD���To�jw��{����M�|��������G��{�t8r�awD>����	��Gu���+����#kze��x�/��Qx�~�z�W�%
��9N�<(�<�(�U�_������d���e����6l�0������dcw���n��0��i�.]�#�}�~���F|p�8jt(������K�`������F�9wUE����-��u�tD��	"�fL ~��cEr�gnn��x�Y���eNN���:�����~�Y�Qt6��������������d����l��#:���pd�v8��#pw����t���U�����7�����C�;q!n������(�&N�<(�<�(Zq��_EE�|�I;lJ�#x��=*����{��/������������w����'�s���������Y��hh��A�f�W�Z�[DDD�M
!��"���i��t�Ab�/���,��"??_��s<�	�`���|��y��T����
&r3�g����Ub3_��ge�
�;K�����|��O���X��0��"�a"�`"9=����*NUW��d��#"���9""�v�@$�`"+����DL�>��Ub3/�������-Cvvv��2�O����� E7\���B�.F��_0)m��{7���DD# jw��m��|�����d����C;��8q��	+���O<����o��W\���[I��'p��+u8���o���kg��.��p��L&��8r�]�#k�y����u$$� 5�H]��lO9��|����|��g|����>��;���;���"	�v����
Oo������7UGDD�4q�0�A�0�AD���r���BG@�-%�$8��8��=�n�AO��{�,q����5pwM	5�.u-�r�+}8��i������P"c�i1U0!$�+?��HQDuu�J*�\.�����&d��
&D��u�0>L��HK1����)zX�h���������\�W�zI[��+�s�Z0!����n�L�O��7[���EDDDDDt��6>�[�pB�����*��ca���W0!B�0�<F��{I�&��N��*��3=}6o��[DDDdK�,AAA�o����PWW�^�6��2��:���a�3��0>r_����[�U����EY:2����P�^�6��W{�X���=�e��`�G����0�'"�S�9""�
�=�G��W0������h?��nYCKK�ndg���w����<QU4a>������-"""�
���k��,�=��+s���~=��:2v��8�/�cU	�x��kg���StOx��MQce�Uy�=�x�D���]���������FsDDdr����_��H�s�y\G�n�>V����[���^�
/������ ""�'�2�g�q��������"""��g�}6.����1C���#m��{2&x��U$�wP�`]�������������hv1r���iK�����j�����[���~��>DDt�0�ADDV`�eB$'��6x\��D3�]������������q����ADDD'.��&��u��%/^����c���*&""�YM�����ZUq"�����.�7�t���z�����=+�%e")�XG@{�n��K7�d�=}���������Ex�������Q/iK����
g������[��~"":��� "�h��8W�����m�~�h����[�g��v��n
&E2��<����NYr��yZ�p!�n�g_���v�n2�:\����������J���HI�^�DDc��j/*�|R��)�P����B��9(Yr��=S���������l�-����5HM�A����
xeN\�)K_����>q�:bC$�'��?��Bmu]�n7���P0
w_��j����a��m:��������9
�9"�6��\y,���b��L�jGR��Fs�Q8!���w����@��?��:�v�:r#x	�a���!����+�P_����hd�sQU4q���>��D�*�������QxS.���]�#���c����#G�[a!�s�7N�a��&�=�Co]�#�aJ�����\�@e��jZ�2����3�
Q,���{���^��Q��#QG��t����{�3?r>�c��:""2�h��a���a�������W����uL���iLG����M���3.��+���ul��a��#N����
	�a���M�}�g��K������F�9wu�sYI�O�V���q2��$��������^RE�-)�-}rO�X��R��7�:{+���@}��j\%�SX0��������l�������
���E"���7���x}���t����������u�n�[{TaD(�_���#���iEK�,AAA��R$QWW�^�2V���U;M�Y�'���~\uAD�(��m�#)��6u��������>V�q������Cmm���LW]ul��+V���@?���(:�u��Rs�c��".�������O����BG9�#�nC�=}�~��=�����������/�����;M�:�yP8�yQ4b�#4Y�!���o��{B[�`����d�DEDDD����*� 
�	"�6�{���:mq(q&��<���j�Ekz<�]�~�'8Jf��:�U�`�=������_��W0��(.�1���D��r�������(���q���LGDD�X4A4��� �h%�e6�����e�Z���zTG��G����� ����O��!\���s?�3	^����e�6�2�n����999p8�%m��{>2��D�)�(��O1��� k���X,#���8��j&H
 d����q,� """"��A
 d����q�R0!�(���.�M7�����^��-},� "":�X4ADD4
���[����
&|�pB����[�y5����FR�����	���V�^�)L>�9������v�\�U]%�����z$�Qw_�8�����P0M�$��{���� """"��J��Xx��(:�"�������_v����C���T/"""=<��,�[UQ4����k��(�HDNj���k��EUk���3yq���-���?���F��-)sssU;����c[XJ�u�]��D4���u�`�nL*����n����9�FsDd%�i;���s-�� ""��3f���������u4��cY�|�����F�$����$�>�����X����D0��+�AD�I���qsY0AD4��� ""�~?�>�dL>�DDD4����9/^��G;� �|,����=DDD#CVS�u�zt+��q���ZZZt�`��u+<�����<ADDD4�1�ADDDDDD4�����e����<�������Oy"A�K_���1�YMa>�����[����|+������-Co���p�	c�u��hb��������hlSE��������#���n;%�y������E>&""���>a�n�]��#�6!�e��y������������8�;�#=����FsDDDDDDDc��;���[o��M�V;�	��?R�s�@���8���|����g]�[�������/��J�\�[�����[@{{;�n����
�c�KDDD4V1�ADDDDDD4��a�jo��b����3$Q ��H|�4-Z���l��E]7n����dA0y?&�OWW*++u���"%%EGDDcS��58��}:2d���H�Gr�
��u$�y�	1��[0~�5:��:��O��������$d��������+��B}�'���{�P�x�9�__�3
_YY�m�����;%>n�<h��� """""y����.����2�db� �1�@D����W�y�����Y�0�[���z6l�0�������d��&���T�G~�Y�t����gOe9�(�#*���hS�����c|�4,;�"�(9M�������9
sDDDDDD#��;s�s�"�j���$[RJ�@���""
R!;G���r��X�d	


td�"���:�
.���2����<�x��>�u�c��Bb������q2���FsDDDDDDD�+*�&|�8��c�y�7�:\����u��8 "�Q'Gm,��E�q�Jg�^���_��?Y�H�`6�
�\s
,X�{��12V��a`�w<������'2'�e�.�<�������FGT�19T���&gJ����[U���{�vh'��".>vv����{����#hooW}���7nf���y���>"�{t�j�Y���)�D���c?�{�t��.q����z��v�!����9Fs����3�,Q4A�Y	�&kiiQ���Lu%��6��:~��:��H�MAb���#�]}ho�F�����u�|����#""�&�FsDDDDDD#��;���9�����U�{_�mw��[����{.VW����r���X�D������eH�]0!S�}���DDDDDDDDDD��;MX����u� [t��M��z���UD�*�������QxS.���]�����&��v���Gs{�j�f&!-'Y�#�h�FgK�jg��������DD>�i��
�<�� """""I���&���mz�]wa����g�e��a��U���?����{/n��V�&�(Z���j4�}>u(Y�a��������{����#G��������1n�8��1���S}D������HG@FA
�i�:
������.|�y�e���&ht1�a`�������h��s<�#��j�����/�e\���p�/_>����v�ODD�Ov�.��J��$#Ss��Ub3/��l``���:^z�%U4�+���>�'cd,�YM�K�b�#��[��x��&""�^�y����&��$���LVW,Y�DG��}�M�6E\}!�����p�W��[�NG�W]Q�����mw���h�C�3	���"	������=�@��G�Y����c�=���Z�YAA���*�l�?oV�������H�����4���,?�������&4��&�8S0�$��U���OP}Vp<GV��y<}x�i�Fs��� """""y���&��l#)�"��%B�
�*7TR"��oe9�I&�(��y|�n~^G��,{��	)�8���Pt�E�y�]:��
6�y��t"9���oww7���T�G~�Y�t������_Q���j|��{u/��1)9�!�
H+��ou���i~��;xm���9�������8k��p�����}��U�@�=���^����*����i|s�=:""2�h�N5�<c�������h��s<�#
���Y���/gp����r��!���I&Hb�����~�=�2���T0!�v[�Q��V!�`�/3�v;���������^��>��#s�Id���A�����_�4�?�}@���X����)T��[j|,�����_�9d��xm�5V�X���}J���]����Q��n��o<���h40�ADD���������*,��2��o^��!���[o�-���u�/8� I���W��l�������h��~:*w�p$
���y������jP������$�I�O������~�y����q����n��j����G����6��`w� -�D��m&c�����s��Y�W��s���H�*��l�A��%W���]���Y�W��DDD��9""�����/��W�����}M"�����*��w6��G�,��2���r8�B�%) �U�V��������C%'��(P����eHN����O�����vuuu����;�#�iB����ZI��]���rr3������*��������������u$$��Y4��B$92�K��'�|�z��\+�������i:2t��������\%6��W}������h�0�ADD�H�!mmm�Q��y�>���ih��fPDb����|�z��,,��2�K�,������$�*��"�S�9q�n�{��cD�����f===hll�����P�cd����4�{
����3��/���L '��J<����S��'�������-Cjva@q����f��<�j���q��~��?v���L��x�GDD4��� "�XQ^^�g�}<�|�A<��C���s��{�b�kKQ�ux��U�q2����d`v4�o'9���!+1dE�ou�<q�6�������Q
�q2_��*ZZZt� ;I%..N�

��A�����e_8�l���b�7�o%k�_�iYH���h�{��c�kU	�D\�����K~���<E���������R�d<�hc����bAGG��]�W_}����w�����W�d���2�9����:2�����E#����f2^v� ""i,��I2�^�7o��C������ "��+}����<hsG�mB��8�|+���>�������nrssu�:�����	��?�D��2��<�J�z�P���#�f��e#s�=b����������ykn~w^�F]%��%���DDD��9""�fR��#�`��]�'<#c�Z8�����9�')�2�X�e�FdwM���n����)����#""�����(|���$�/_�V��v�m�^�/V}r�d�����l�ID�J��\���=a'�_���,�L��A�_������������q����-����Y�#c'��0����>VS��_�#������[Y��[�#��y�R8K]����"�<����6l������Gff&


�K���#ce�����nJ���4�����6���s�""",��r�/�������$�g�p�w�b��hx%30��[t�xp����7;{���WW��_�������:���|����������$q c|�s� ��?NB��s��6� C����O|xNi�S����h,c��������<`�	�����dee!55U��-}r�G��\��l��nGp�+�����7�a�ODD4�0p�[��b�����Y��l��#��aV��6��L��vYy�W=H� ���9R����G��BWW*++ud��l8�������Z��]GC���	���W:����j<��s:��:.Gn� !;L444U\|��(..��5���T�vQ��l�x��T�u-F;1e�}a�X�M����Fc�R{z6�E�������]����_{�jE���S�cLYY�m�����D��s�y0�AD���]���� e7	)���Y!��xPUU��~c���q������a���3��3��J�����^^���
q�	:"""+��GTM�������s���<d�[�N�FY9J�?S�C�>�D�Jf���58� �p�/"���}�h���t���_Fz�u�C���������t"99Y�e�������M�������:���}
�m8g�R�OCH����
x%�.E���td-���a���!^���H����������1%�'d�����#"��#GR���:�X41z��00�������K��1��D$���hi1�{%'r�����x������:f��>�n`�)��z�s�9��""�e������9$y �W�K�#�'k���0�����1f���PI�U�V�/)����gH�����u�Ab�/���-_0!>�������I����Wp����9V���Y�2T�]:"��o<�J��v���(�zr����FG���DDDcs�<���
\.W�n���-�<F�tvv�(�������3L�#�,1~""�#*�&�X��#���x��U�Y��p�I�P�������b��0���M]I�#&!k������������Ub�O���GZ�lQ���~S�L�=��+s�(�`��]�#���V�k������J,+-��O�w����)��9+OGFqD���j�A��U��-}�b�O{��\""���9?�<���
Fbw���Ii�����6K�B��p��+�|����n���F�V���f��@��\�hQ�=��t~pj��X����|���y�����I>�n����������Y�SK31cb.\2SJw���k�.���555���P}iii(,,���S1{�l�ge�8��"t7�K�-9�#�t���
�,&�������������&d��/�H�Y��BD�7��{
�qr1�����sDD����>�[����������N�����������j���~^�tBk������~E�������*F3�uE��������/����B3��~�����[o�Qd6�0�W['JKKO�?,"���Ww��g���pn�t!.;���Q _� ���X0������A���uOx�����#.>I�X[����_��^�v@��vN�|�����
�E�s~�y�1�AD��}�Y8`<����%%%��B����xPUU�~�=�,����U�*����]<MG�=(�]8��T�=}��k���H���'"":��AT-�>��D`e���V���ha����2���[�l����s>��D4����u�*�2N��I�D,L)�(��O1��� k���1C�1H,�3���+B� n�`���2,�]������>�w��X0ADDcs�y�u��9S����~466���`�'�|b���<��d�e��S����#����Q�rdG}�K��`��;�DD4���h���++N�V�����q�nQ0������$1d�
�D4\����������R�HN/�#{��Jl&�e��}
��zs����3WlUW��?��g�����O��K��[����:G��-}r���(0�qj1�ADD������u�\j7	9�C������O����kE���g�gudhl*��=�k�����<""��UE���g�A0�����	�2s��d�%�$yM�����W���D�-���Hv!)5��u�8��/�}d���fQ�kkkCkk��(>1
����J�
��EDDm��`�����m���HNN�����GZ[[�^�6�0!ce��M;g��9b8dg
ODDt2DU��Y,V���v�"xl��D4R~��.�2����`�K�Do����s���-�E������x>� z�!�~����=""""�b��9""����4\{��;N�#cd���:�9b�����q
Rsw��X�g�]�3���E�(8ib^�2��-9�%`�< ����`�nGrHaD$RPa>�c��F��X�����k���W_����������=#c�hh��]8P�[]����"�<��(8���&��126V����	��-f]�\�������J,�)��#���N��,����h%���
��"c���%T�@�{L���x����&y��$��Vd�q�n�����(H�#�<�]�w)	E��XN�YM�K��SY����}��5�����O^��K���w�H""��<�� "�����`��5��}��1�}�+sbM\|��PW""�S%��&���W���P��6�{�����~������-��U�t+P�8����B%":�M�L[�]�"��`�nQ,��a���u��A%>���(((P/iK����9��,?�������{W��5������X����S��s�����{����
}� �������8ODD��9�<����dg���Z�N��w�������
�M����N��*�X�d�n�6�f���<����@��Nz����]}�|��A+.BmS)	��q\mAD��Iy�e���u+��q��C�U^^���lCYRR���,���������E����{�������?n��]�hl�=!m��{��]������������=��8/���ht1���YWee%�n��#�n����999*�!/iK����92����N��*�^ ��#�V�����<�'��%�I�K>77)\�*�d���������rt��
�}������Pl��w�n;LH��f��?��{�'����[+�<���Wo���tOx�m;����{�������tdHq&"-/���\%6�����':""������9""��m����!;;III:��>�y��%""��UE�|���!W�#����	N�{�Z�
B%��(��9q "%��<��h$������jC��CG��}�3sR�nQ,8r��n�S�*���{�m+kjjt�b�co��G�ud�;s��[�^�6��2��6��:�,QG@�=Y���� ����K�g�KU�}�,_����b����YM]]�n���HNN��`�����1�%""��#��&d�E���<0�����<����1�����+%!�i�&�'��p�a>���h$]�l�n��ua'�_��}�s�u+�x��h;�C]cEkk+:;�^�V�����\.���n��v���-��HHJ��h2Y�Hrd����O����kUo���n�8"19t�QbJ��o<���N=�<��� ""����Acc����$�b#s�=������'nu�������$9p�+"�@�V�rB���
�pV����L"�Fb���+�l���R���"��F�S�����n#���D|B
l�vxz��H���K��s����Z�������W�AW�A��NBZ�L���9}���)xx��u 55UG�I�Emm���n�:"+����c�N��}���I�=���>w�j>��xn)n��:���<�������$���_�������#���������j����l	-��h��}����sDD�Lv���o~�#`��qC.iooGCC������J�#""��c�Dm����'�WSEV D�j�	�����������R6w�������zq����r��tOx�K��)~q	���Zx��c����DVV�j��������v8����U����?[���v���e��9q�j<w�q�GIn:~�
�}}�o=��}�������f����^��v�������#/��8"�c���a��|�� "�n����u���!--MG�������r��v� ""��3���x3I�����[X��}��Q�2y@Dt�I��12.f
&����_V���q2^�YQaa�n���������8���"�"����;V0!l��wS0����V�Hv��!.>pw""�>�yY������#���[��3�!sY0ADDtrEu���$d�IyI� �%���e#�<r��Sw��gN��	���0}B�����1u$��O�����:2��&�8#	%�$u��L��<+�9s�n���jEE��	��{2�g�������������&c7����WY���[Yx������>=�����������e��h�a����(������q������`RT!c|�s������D�E�i�������'�[�
k��<u}�G�W�SK#�`%5{G������D��$�$#	��	�q$���Sr�������V3�|��=[GPGuTUU�c8:;;�K���;�C��ku]M����~�_8��$b��>��Kg��z��M,��-��/|��g�������`S
��~:�{
"B���-��K��DDDDDD4r��03Yb�M�G����td�KDDD#�EDD4*l�8���������7u�P��GR�o�iI6�:ud�oK�.Er��(�M�������%m�2V�X]���a�OC��5�l,������Q�guq�/%��:��]������T��$��b��)�2��w��+����u�,x>����Run���4Q]]��'da���-}�](d��%""���EDDD�����t�u$"%1�����x5��<�J���p����8����2������[�w>y�w����I������"N��;��h�Q�\�Y<�l�5�������#��8���]��\%n�v��>g�_�����d���������:������9DDDt����i�O[�n�fTY�z�n�89���[WW*++udT���}�9�h�n���n����	�I�Lz�{KW�����=Frn������{�����G���p8PXX��3g����s�B���{?�@���\g7���G�*�e�Qx�-:����p�o�WG@BR
Rs��������G��g�W>��rud=����W�p�>�3�	����C\���h0Y��m�6EO� Z?�`�y�`����:�n�:l��]���`�,_�����;��
��ATMl��w�u��_�{>�A��DM�:����-'g��n��(�X+�0kmmU����{����]u����(Q�d��X�������f�]�dDG�������-���`w� QN��t�����>+M�W�>MG�������^�Y�����|�kH�DD����S�9
�������C-9r��������7nf���y���>""":y����[N&���/��������=��c�i��H\��������	���S���u��`BH���Y�����5]��y�\��#�It4T�Wp�����;'H�e��	��������P�=^�1H,�w\�+5�DDcsDDD�A�".��"�t�M�����K����	""�S/��&d�DDd���;��z���p���
�q>��dM�
��-�H�p>r_����[Q�-?��Y8g���9eS�X�Kf���M���}��Xs�����5�*���(���DD��9""������^DC�iv��������f��-��������0/""�S-���t�P�������z�Q��/�������E�b�@:�u8�uc�q2_������p���/�a��b�8��TK[���
�/PccYrb
��RW""��� """�`o��]�l��������zI[�����'�h8�S���������u�T���{Q4�����_���!'5�����~w�~����Gcg�/��}����?ud]�|��������O��9~x��n`O��f~�op�BG�����0���DD�������;��q3�A�0�A}<��>X�Y��`��v7�������w����'4��������E��N�9wUEq�/�\-AfL Q4���?�:*w����������G��D�3 ������o�=�-X�@�r����U���$���e� ����N^��E\|��b�&��F�&N�<(�<�����f<��=�9���7�^`��l����/~n6�����DD��y�q�#���f����,<p���;�����2�����h�H�����{�.����L��x+Lx<�Y�f��	!cd���)tH���#c��pu�������	nUIDDDDD���Ww�K���Ko�P0!�?���/���j�P}c���	�3i�%�%m3��w�8M����DL��wp�-O"�cg#�p��cH���/�e�����7���VG�����|������9V���Q�����!�e��y�U�V���?��Vl�U��6����O�=��v5������h����ux���F��8OD���
xq��:�R�,�GV!��%m��{>k�sd.���*����{u��y�nE���1�������O=������X����UWW��#��f��v#''�C��-}r�G��\y+��{�n���NH��X�7�F��&����_�9�����m�X��<��?�S]��hlb���(���[w��!)����8�����f2�;NQ8/�}�nR��#|�/5�HG���DD4|QU4�h�"����.�"""��=����5l��	���������Cvv6���t�'}R<a&s�=���"%�#(.[�#��8:
��Gv�7���x���x�����#k�m'�Ued��������O\�+~�?x���*�����G�X��Qt�W���D�-���Hv!)5��u�X�m��>2oe������6�����b�����iYH��L���1>��DDt|��h��3�8��b���\yADDdQ���HNN��`��������n����:2�u��
��x��B�x�Y��C�j�I�����;7���i���+���^����������_������Qt���t�`O�G�=MG��_��=��=��XT^^�g�}<�|�A<��C���s��{��z�P��O����sc>�12W�����_TM�[o���-S���c���L$YL�&�
g�L;g��9b8dg
ou/�#�H����2h����o����BG��8ODDcsDD�c�
�e���`�G
'�Gu�9��[K:::�v�Z����8p�\.�������'cd,���&����88�,.��|��m�EDD�#��&��u�t�X}!�����~���������ud��$��=����S�eE�s����;����������U;S�nU������`������dd����f2�;N��y�}O�:��'>a�_l
���7z�g@G���G��]����"cd,'b����2x�����u�P��/�""�����	YiADDD�1g��2����Vx���Y�f��5
�K����Uq������a">1�)p�� %#	��	�*�����}d^E�~�hb���h��}�����%]�/���>��a��
�������->���(((P/iK����9;$�S��?�����[�y���S27%)AGDDt<��hB��������C
rrrtd$�����[Ps�|\G���a�OC��5�l<�Tb����|�z�Y��������!=��#pW	�O��7[���EDD��9"��0kR�n<�C����������<`�	�����dee!55U��-}r�G��\��
3up�7E,��sw�������DDt|��hb���!�r���y������-���nw���U��\����RTo�UG�Uy��x+�V�����[���
&|�����|"":��� "�6[�O����
�\��M�����b���{u��aB�l6��'}r�����=|V�%�b�n:����g����l���!x.
��T��r^�p!�n�*�1+x������[o�/�����PYY�#���))�;K�����:��O�����������0���PTq�W���V#;GH!�YN:�HR���:������_?���F�i���z���u�����g��]�����[y![U���������?Y�# =7Y�1����7�wvy���`���#��UVV�m���(:r"?n�<(�<����v3^z�}y��2��`O��`}�t�T�������W��Y�������N��c8dW�H������������7���~���X����E��9HL2����t�����>+M�W�>MGDD4��AT�4���uI�W~H������j466��r�����\0!s�X0���n@��I9e0���2
'�2�X���#+eg�����z�U�`kv��!�>������:�yE�����-��������n�}�/~n�n�����+��Hd(�1�ik��1��U����E��5R$��P�^�2�j�"":qQU4a����,Y�:2Hb���N���2V�XQ�{���4��hI�K�N(���s��nU9���Y�e�sIN����!"������7�47^�PG������jt�EOg<�]�*��0����#�����Ql�����w�5V��}�cX���#l������9�����)����"":q�*JDDDc���y��W�c9�"cdl�3@������e��`�G��8�|��;!+M��Q�v���k��jU/iK���B����V$GjL(��#��=��y����DDDDD�w���P6w��=]m�n����B]%6��2/����b�������x��9�~���*��wT��#���t�#6|��g�c#s322tD�">.7\���B�.F��4���O��p�5���>��-���y�n�Ux<<���hhh�=��+s�f�����:2���8�/�cU��r�I3u�n����_�JOg��c�ODD��9"����w��8������b�l��������Qx������j������f�\��3��YTT�[���?�.]�'o9�~������=""QU4!gz�um�����:2�����������9V����nR��	"
�>V��*[���t����[8!��
���>w�E�EDD��9"��%;G<u�
\p�tL�x���	9���{V���?���OG��8oU3g����������pB������1c�n��"{����.\��[�JsL3���s��������:JKK��2��0��PQQ�'�|RG��nWGp$%%�COO��e��U���_�����(����$���n�R}���X����".>AG����Y��������=�Ia���?=&��|e��x�/���!9=�v���s��#9�;L�K?}=�+����������m�6EO� Z?n�<(�<����3���1kRl���6_v�.��J�����D�{=p����+�p`�E�`��ktd-k����]�t�����!�����&��g����+uD��������j��jBc�q��1�$��U��&��>s� ���m��I�����s�J"""�x���]���L��]'���F;)tH���#���Cp�����r�D��7���!&�k���2����a������@�������\0!�Y0AD460�ADd
R(1wj~�LtT�	(�H�~&e�1>����D8��Ub�OJ��d��j���e���HN���)�---jMyI�\0!ce�&Y(������?n��]��
&���O�=��v5���>�i$�v��4X�x���m,-Z�����3��-�V\uAD�~�a��HOOW�LD"�M�����Q\w�u�m���4����:e����Z:�Gt��;�L��oud=�y�]�����vg�����=]p��|V.���8�4Y[�'B�D4�4q�1�A��� �h���U8��yAF8�m:�����&�n�Eg\��W��#k�����
v�Ev��������,Y�q�#���f����,<p��KJD4�����*�0oS9���e�c��(������_�"E�Ee$�e���B�r�-!w��V]M���?��oOJ�CNH�De�����;�f]X��k�<T�[���# !)��g-RW���.t6UW�������tdm�����k4�������LJ�#'��v�W��Kt/Qh,�8u���p�� �h���V�U�G��H�Qb(-�cGu�����;���U���c���8z�(\.��s8(,,���31��I�=�~�]���"DD'�9w��3"""u����<�H�� ;OXIJ�GP\�ZG����I��Q�����#;���X��%�yV-�/�#�H����ABSd��<��d�����
G�*L�/�$�~�/�������F�@��X��p$
�W�q����X�E\|������q�
7��oT�K.��1n�����	�m8�&��U�$G�zI[�����S���a������������^�������F���g�gudhl*��=�k�����<+;X�/���e!������)j��y�U���o�9�c�
f'�������FB����eHN,�Y���������!w�������������%"�����9�\����{F?nUIDV"�)$9��8q��K.X��?|������!� dg��v�b@������T��j��U[o�Qx�3�ZXYWO��C�6�r,GrZ��B��hR�t����+����#k��#�&���h�!��>w?z�t�&]���q^��tDD���9N�<(�<�(�HN��k��(�HDNj���k��EU��9��G������W�U
F�K�8r��v8��#pw4�vIn:~����MDDC3���h�(&�(�������'�~�=�
�����>aJ�\�m������/`�n��}��qa�����V�~���5^x������]��� :��m��^)�����"�M�H�;X������g�d��D$����u<p�Y�^��"����>~�����x���awNP�]��ht����{�{����M���ED��9"�6��\y,���b�������hq���8�Cr!����
.!":����ADD4�*������<�n~>�`BH,���\��Y��2�Lv��������555��<��RsN��O��.|������M]%��X(�2t���s�Vx���<kn{����u��`BH�������-"""""�'�?|���mC�#�}�<�(��4�t���8t�d\���*��t�����&���FQ�O������Qd2N�[�����N�����jU<�r��K��'�|d���%q�	p�B]cM\����O��K�����u��k�k���q$G��	�/�|���������y<���N]cQ�Y������'l���W�.���6cI[[Z[[uD��ENLwo��VW"���	""�Q";G4���#�l[)�}N�MVW��d��w�X�d	


td��A]]�zI�L���-�
3up�7E,��sw;�S��Z�������I���_����������_���v3.�������p������E���"p�D+s�����n�����&�:������~u�X�{���%2�������r<���x��������R���{N�����"����?���k.�5��;��F]%�~�ODt<�_��~"�{n��Y�g�q�n������E��ID���r�:r�'��g�����V]���$�O����V�������7hhh�=�����?��?��X��pn����HHJQ�|�����K��)W��_��N���u�?�?x��:��S��<t�������:��K���qsuDDx������7st��� ��zu|��}'�/]�����#���2��D�5k��QYSGG6l��]�v���f����K�"--M�P�����c�N��}g�d$�C���"m5u,�[��/��������g��+�����'��?v	.����`��'�.s�`LM�I���@��e��GJ���Dm�<�
G7?�#`R�=d���N����3.��+���ux<<��c����=��NW]ul����'�����b���ud�;s��'z{��nkTm�����g��#k�"���b���H�H�Qx]�=ho��Jy���`���'"�c���a��NsDc��?^��;��hhes����,����n��9�Tv���Y�L<��#���?�E����k����1�E������J��9\
��G�?��Y����h0s��_%��hT��������-�H�H�i��a�o%7nT0�t:����^�6��2�b�U����EY:2H�DGC�zL����s�J
$�����`�q2�DDDDD�';LL$�8��^G�xu��L��<�;9e�c��0a.���3333��yI������9[��a���:2�#��~Ws
z\��%m�3L�B+L������
&R��H�KF��%W��d�c������c��2<��0��AG�n���};6����>V"���+@�v;����K��'�O�����;�
	�x��kg���StOx��MQce��M*��������WG��}�c�ODDDDD�I�|$G�-���Hv!)5��u�X�m���e��H�N����D���e��������#9JJJ������T�����=�#s)�p��`��{o���# ���b��R��LB��%W������>o��U���"�"N�3
���h��*��>��������~��:��&��8�/s]���o�F���!c�u�x���{������HmE�����#��<�f���^�#����@W�n�d�B|"��|��0���{����f���q�`FI6>9�g�������~?n��:���F<�s��;��)�nx�_W|���qL���VAD$x<��a��NsDc�,�x�-���)��H��>�sw���ZG�gN���b�������4eAHr�
���z���� ���t<��s���j�nR��Q9����
������)Sp�%��6�������w����'4Y(���NG�I:�m������������������~�L�Y����#�c�>�o��GGDDs�`LM�(&��6��Hv�x��9:J2��:����;{Q��_~�#�!�B[�?���hl4*������yK���������@q�u���U��+�w�]����u���3f %�4���i��uo��q�^]�M�S�X������_~�#Crz�J$�*�>w�J�w��~�z�W�%��h�h�1�A46]��������p�v$�mG������'���}^����r���BG@�-%���G��u{P����>��e?�%3td
��?:;;U[���]%"innFKK�j��7�|�jS��B�
��F�'53	i9�Z�u4v���G������uRm""s���{������I�CZ�,A���8�o������c"))I��3����V4��������i4����	!���}�S������X#	�������{��yK��-}r/�H����ud����n4Ww�kp���g���y<�G��'���<������	>���6>�[�pB�����*�|R������X��09�y���B[�QlC����������B��}��^,�o=r�`BD�a��<N����3��&d+J���������)�X�}����>����lE����2��7oYi�o��>�I �m;(;OX��@?���q���_�DV��~5^���������'��-�������>���K���Xq���L�12N������u�>��iK�V��}��K@+i;�S��#9�L�8�65����{�e
���1C���}p��]]cYsG7�y�����o��}5hq���������{2F�Z�#9p�]\���"!��1��&/^|��e��KDD���o��-�==	a���s?�����[o��d���.�-Cu[O��	���f%K.�-k��<J��7p%|(�	�����F�w=:�ud���{��L,0����J�<���7��M��5���B������C��w_�8�����P0M�$��{��w� ":��� "��Y��a=�n��,x\��X�I����#��4�_A��uT�V�cN�S���v���<F�fd=�[LW��p��_��N��5����O�����������?[�M;��s�#cd��'R�i�x
��������>2?/c������DDt��1���#8�0")�0���������9�\t���?��
�`�-n4v������K��lO��V:SG� �.rrrttw��g>�C��H�h���	4�~XG��3v`�x��7����3gW�g��}���d��=��|p4p7�3i�%�%m3��wN,��?
���v�}��x��oy��QW��|�T=��������������}�]�"1��>![����w�����F?x\��D���B���������/
�{��8���t��j����x���pDb����|�z����_-������#zS2���_�^�6�+ce��M)��[���=�������<��(MX�l��z�j�'�k����u��~Fz_�'"�D��|�0���d��k�5k��rGUk/�7t���H!�e����wioo���B�*d��y�U�zQ��G���y���}���V��p^�������8�&��U�$G�zI[����Z��+*���7���w��W����cu�X����#����.�<�h��9�_4����>w��B��2�g�$���(������wx;F�~�h7s��K?%�5�s#}rO����1C��g�kKQ��VEV�'��n�?�0!9��qS������K��'�|d�����M��n����������}���DD�X4��a^������t������K����!	�H�+��L`"�������3�[��8����V�p�B�2H�������55�rD�\+����[@~&��`�G��8�|�y���u���]P�#}���+r��Z�+[�����o��e�
,��X�����j�X���e/��[w{]���>w;������9k�z���i��t�z�wL�y��7�������c�l��s�����*477���S��-}r�G��\+��#����#CN:0>��iS�$��L���V�eW�n��R�
oK�=~�'���V���*����s��Y�W�H��������#:�z��}�Ub���>g�_��E���(%+"�^��C�����dC��A(2�x�Dx������xEg\�XK��d�&|JKKQVV�#�����jU<!	yI[���P��k%������?$�N�a'��}��`M�n��,$�Su4X�=E��1���{��
<��_�(2'������<�(L-������=�^t�T�����y���.u�������}d����:���	���A�m�m���y�M�|+Y�t)���+"d7����������;L�X�cE�����#|��)���E@��^v��#I%�~{��%;St5�KG��~��(�����>rO���?b���U��6&L�������h���*�������OGDD��h"J�u�]�eX�l6m�����c�{��W��
��/����D��5��\}AD�x���I+���W���w�������?��K����[1�-Y���L�9�uuu�e>�S�X�c5	v�/���Zl>���>T5��f�������<F��{X�����6��������Tu��L�s�	""K�� �hq���P6w��rGw{-\M�j>�C�x�ge�g]�[�������_���d���t�Z���p����8����2����{P���@f�?��������s�����N���ut�9���z�{XU\\<���oq��.�=��8/������QH����$X�n�8��c���[U�9� �$I�lOi&��P�+�f�]�AD���{��V/c�i���r8l6���j����^��X�c5��L$��h��n��6I���'��:j���������������>0ii����0����8�S������u�X�m��>2��>6�.!"���9"�6~gy����8ou���|iO�6�Q��Fcg/\���*���z�H�hRYXbU�#`��pd��������qG��	��}�Q��VK�>�[".��M��%���g�������Ab���2N�
�&��<���}��HV�Z�[��>�+'B%|�?8���D�����P���z�'���x�������A��'cd�����|�Q%5M@��AHr����y��L,��-��o��x�ni�S��������!=��#tr@������o��n��<�(Z�������2`O
,��'%�~�o�&�����Y�td�#8�Z{���[]�Gro��I%�f�l��]�d��`2F�Z1�1�����rGq�y��������Td����� /��+�f���M���}��Xs�����5�*���}"�����9k����IDAT(�e��2�<���[y!�p�����<�1����"
v|�{V��>V?��B��q�:����t"''G��m&ce�eN\�[�
���������(����[�W^��_^����sA���a�t�v�nGr�+������:�����FsD��zun��FTm��G~��[/���T���q�d��
�q"'��,\�C�������Gg���eHzI%x\��X��q��t�7���8D���i��������L)���DD'J~zSe�.���[�9j�$�Ez�i��b�0�������e5�y�������0'F�c:]]]����PZZ��~�$+n��:l�yDGC��=ca�J
TQQ�'�|RG��nWGp$%%�COO��e����0p���c�������b��P��ar3�T;������S\������oj�����3~�#�����c�N��}g�d$�C��sw�������sKq�����ux�=��'����s��QC�j�A{�����oo�-�"������m�6����p����9��1�sDcsC������O���{��n����B��9(=�ru���1��';Dl_�/���
�w��-���i����".�z�"��� ~��� $�����B����R0��\��?)�����Z��V"����;���1Y4q*���j,
~������
N�{�����`�h���>�O\�#;L��v����/`�n����(�9��<8r����5k.��BY��@?���h���CI����+����l�
�vn����_	I)�?w�����ty?wG��g�W>��rud�����������T$&�<���Csu�������1u�\�hb$1�s�1�A4v��u$�8��[��^7��������b9�1��A���pN���*�f�#��N?���I&�fD����F[r�.|�,(x���[IN���n���5e�	���c�E"DD'�9w��9�H�6�#�<�D���[���'����^��������>!G�?}�JL���:�r������!+2d��y��H����@��o���d���r����������(�h;�\z����-}����9V,��
g������[��~""�S�9"�6�*�
&�m�H�,F��I���%�����o����y�+�u[����O���	��G������Q����0Z��}�<���;w>��]H�8���u��%ms����9DDt�X4a�"CVN�
�k����o8>�����O"�H������r���
ly�*�������}^�O-��s���~���QG�=e(�12W������P���b�yA��/"9#p�R����U�d|,��s�0�(�k���
U�%m3{�w�U��
���u��'����|�ADDcsD4���]�e���#����@�/���[��n���xh���
�<��{�n*��NH�De���97��5e�%���X�v�����2���N��=���{&	������|~��[7h��p��B2�s/�=\��K��������(�������7:���VVD�������������X�������H������I�X������/�����=��S6_?�t��~������o��e�)�;�����t����'�3?r>�c��:""2�x����Gd�y�����t�w_T�M9�Cv�Jw��cGu�����$���
���?h�������H-�����(���#s�B�k=�y�V��}����[��px���&���UWw���l5
N�EG�����eW5�U7�E>!^��dL���oN	�}t��#"��g��S��,��s"�����U���<���`���F�|�����u���!--r1@[[[�J�[n�eX�5�z|�����(�;���o��VY��(��X{�N������wW��%�#-��p���EG��^��w_�8&��w� ",�9�yD��Gx�y�=O?]�;���(���t����VGP�o�l��QV3���/�����pJ�~S.�6��~G+�<����R�U�YGCs������S�j�������DD���h"J�??����h���s9e���n�MG�p��%������%"���w�����U:rvgnn�j�#+.d���������e���!;M�f�N�����	�s�O�,�k��&������-���!9=�v���s��#9��{�]�E����.��e���,�9�yD��Gx�y�=�>h��������=����G{z��j������b������j`�;V�;:���������[���u�z��3�^�{������(�����9��Y�h�<���EQ"\a��M��P[Y��a�����@��!R���hL����>���y�@[/Q����7TUU�(�v����8z������|�����O�~��~T7
����T|�����[���'�v>���������O����D�.''G�NM��<"c�#<�<����~���6���w�x|����c��_�U���9�	HN�G��K����Xwo?;��]�c���\�5Ys�y:����I�w��m����-m��� ��
�;f�^""����y��ob,*R�@�=I���u�]�EDDcE_�!4�{u�����^���oQW�������UM�x,�lC����#?�����	�K������
&���������d���R���_�Qd2�DD4V1�ADc�:L(������[���M(tX�`����)�6L�IFIFrS��HPW�����}�'���9��li3�<�GH���H��.����q�����	"":�X4�"%|d�����1o+IDD��}����M����u�^���_��7�8+��)�������j�Hp�\�%m��{>2g�m-�Z�v����:�R�,�GV!��%m��{>2G����o�9�`���t�!���{
K��O��GDD4V1�ADc��"�n=]m�sG~���2�gb����j���C���p$��UDZ�
��D��[s�!��s���*������[/��(x;���f�J��IDt�v��kh��w
-c�'0��_��z<������_�G����������X�����e���IH���(P��m5�b�E����+����<����~�
����-�����/�>�$���D4v������s�8�<"c�#<�<����[q���#x�KaO��>�.��s��������}���lL)�H�h������Z����DE�R������I����Z��9""��F3�!O�,����CmSi�@��Z-�PZZz��a���kp���td�J�����6t�z���Gs�G�5L���_v���G�����������,X��'������u�jhWm{Z9�T;W��;�U�$7���r�����~|���p�v�����i����y�-q:"�E#�9�����9����Ww��g��oDR��	)�%���u�#9�;L�/]�����#k�n���n������I�L	��.���>n�W��{����YsDDDc�9w��h�X�h�n�f�����N���=��8L��c|�9��p$�������H����y���:������[G��K�����X�����u+<��+�aU����L�8����t�K�������E�<�h�H�C���"n)��n����B]�&d�U&DB�S��x���L� �>�l��?�#�(B��'�d&���NM� ��a�D��Tm|J�%�$8�Co����P���#C��O���ttt��G��]�tOx2F���X3��W���5��4�$$��2.�G������Ulz�u�Y�����{<��H�KA��kL��%W������>o��U������9"i~g��9b8d���2)��g���������\���|d�w���X�v-����B�F��>�'cb1�ADD4Z�T���P�{q����,Y�[�-[����^�����y�f�2����������q$G��	)��q>��V�a���]&���������������X�������
v�p:��I��?}R]%����w�Hk�X��[O�[��@`��4/p��U�����e����0_[S�}���DD4�����9"�f�s�Sw��gN��	���0}B�������a�,}�Gt�s���'r�DWo?��|����E����.�4E��*7n���
�l~o9�D�%&�.tT���H��c�8�/�c%����8�-)KJJ������T�����=�#s����������a�t6�Y%���/�G�w�����%��:��]����/���)IC��������f&!��K��8�|""�S�9"�v��g�{_9O�h�<~���y����>����f����w�Y�e�l��+L���W��&D�|��B""���EQ����l)y�m�����3�����������t�0���vh�n�#�b�'x\��D��{����8�����6�s#}r��H��g�nY�����z��V�Vy��x��T��[���)b�D����f�����#hn���Q1�q2_����h�0�ADVa��a��|u�E��\�q��DG�����Q�����>4���Ub���>2O�[
��m,��2�[A._>�x�+(���^�
d���D�6����9��}��(�s�\�2t����~�hw�����N�3d�����1>555�e-�sD[��ud�I��3�{��Fl&�eg
+;�Su���t}=�'���)��F�\�p$9��Y""�n�yY��/����Y:24v����Um=�*����yV��"DDDc�&�L����i[Iy�^q�>���/�D�~�o�m*���'.��8�Ee0�8�/�c�������`��u+<������6YCg��j�9Qb�8`b��%�$�^3�X���'dg�����z�N���E�udG������Z�K��g.�X��#s�(�����<=����pz��b,������DDD4J�� "����x|���G��/���d���yV��"����W�6u�@5�.�""�����(�i�&�2H�@V_�	��/q�C~��`�+'d�p�W�q�J"��K�0G���.�L��E��8�|+������t���{P���@f��H���Y�����5]��y�\x&���
U�%m3{�w��M)��[@gsO@AD(r���GG�����FsDD�����_��]�=���Tl&���}|�*�Pdh�G������~&��]:��X��KGM���V�f�a���\~�+��������O�����P��0,\�[�n�&E�P��"���<�K2���P���u���httuu���RG@ii)RRRtDD4����b�}^G��@��Ip��4�����������"�Jg��x������,�H��������r���7���V������h�[*Gp��C9thl7��9�1��w���������k�����)����w:�G�8g,����x���H��#='�)�mH��n������`s�9���9g����PVV�m���(zr��q��9�@�yQ�����~��
��=��NS.��%'�����#


�����������VG�
7����Y�@��~�;��=����&J>�c��'�kS9�W��k[��y,����o����`�p��(%�I���p7y $0��:e�h'�����d�\t���
"6�Q��Fcg/\���*���&d��
&Daa�n	�'�*y�g^eQT4���(2��w�`B�Q�a'��}�L7\�g/��g
���	iK�����1�<�������TAD��N��w���]��\%n�vL�5&��h�`���(�
�����8��	!�d���9jC{���n�
�<F�Z�`���z��V����y�o��#��<���WoY0!d!���9DDt�X4�$!�{��DBp2Ab�'��7y�#g}�<�{�~IJ�;+����6~�5��x6��Q����
��j>�C�x�gE3g�A�������pB������1c�nYCg���eH�B��q��c5��������m�����w�_\$m��{��]��W}���P0MG���^t�w������f2����?�
�yE������ud�IM@qFJ�I�*����yV��"�U���FDn0>�X`\%6����yV��;���f���������d�c�9DDt�x<Y��$�h���E������r>�i|��GtdMk����]�t$�����<e��$��g�������V ;Dl_���t|�����������kz?�h+�7?�qP!��EYx��%H�Y�f����g��+�����'��?v	.�����k�LD�� }�yQ��������wt�$�P�L�#i��XG�G�����/"�u��Q��:����r����:2v������xL�o����Rq���b���:���}O��[W�����{����i����\�
:&��8r�]�#k�y����u$$� 5�H]��z���xT]}V�3�;!WGDD4�ADD4F����aLH�����V�t�R$'����8���E��)/i�&d���)tH��'D\��1��9Y��j���\u�@\�����K~���<E���������R�X0ADDDDD#���7u�P��`B�%�P�|&	�oR� �>|�(���
������T/iK��`B�X�`B�zQ���L����Y�|+y���u���]8�`B�b���]H�����h���h�T�_���[td�J��$#��vu�X���2�����p���$��12V�XQj�GuhlZ:t���q>��V��P=�ny_GF��Y4��B$92�K��gN,��+f���M���}��Xs�����5�*���(9M�$"""""9���[@�#)��
�������[	��u��w��R�L��}�c�o5kZt��e!������)j��y.M����=8��}:mq��e��L;rR��bSW�'e�����#�\U{uDV�?�&�2T��/�h��W���ysn�-�y�t�p\�.������L)���DDDDDD'KwC�-�:��|F$�q2_��j�P��n?���j;I�y��������>T5�W���CT�x���\y"":~,� ""U��-C�3	���s,}�v�3�td�|�I�����<��#��k��1�����12V�XQJ�GP\�ZG��8p8t�o1���o6���~�3q�w^j�uw��"""""��)!��[[���&b����m"#c�(��F�����T��?�E$c�C\���*��t�����&���FA���t�8�#\���N�����V�a�tww����J|<233QXX�����>+s����[�,���rGE���{�<�C���<���"""""��K�&��::���&"q��K����Y�����5k�}�v�c�PD��X�c5��L$��h���!tt������O��uL,��-����[�
 ��Xi^`�
��y���.���c������-G�����[����PYY�#���HI���D46
�{���st�d$��8�������^g>����S�_^^�W_}UG��������H����.�����s�����ud=������C��";SH����i���xCG��p��M"wG\���?��,L/��ERVV�m���(:r"?n�<(�<�(�����hx���azN2R���������
���/���?�#��E�?�8��c;O������]�g��K���:l�-����5H��&����cw��2'����/��Z���u������:v4������]���_c�j������*���;�[y�{��L&D!&�(��~�.��s������p$������MI��?����9���������U[v�())T0�#�UUU��7���L��K.�D�����]��� :��m��^ 5g>Rs?���7�#=�N��{�t8r�awd�(���&������?~%R�t�{�P�x�9�<��=��|X4q�0�A�0�AD���o/b������hC�3����~T������������O���5TTT��'�G���v���"))�8���444����0p���c���:���}O��[W���lo�~+�^CN�������i���g>��iW��Z�y������}?�b�>w'�j�X<��_�	�P������n�����y�f�CDD4���������$x\��D�#G�����"\���{2����F��+5�4L��o1��w���^�������g�N��(������c���23��,��
�ANEQdANvu��� ������.�������D05AH �\�����>�_O�[����&����y�~�~����9��t=���K}:>&��D+'T���>g
���lDsgK����m��������s��G'��_\.?S�������cl���c��\���9'j(!bw�(Z��	D����������	�g��	��k������l\��cTuS�8�(��r�U��$D�6�M�@��;�<S���&*�yfM� g~p�h)}�����>�X�/1f��c,{y�4q���|����T6l��N8A�c���E[j6,=eUE6��h����D �mbI+.2����:�%,���b������l4�F�,�K�8A�.�2�D?������O��'�
����t5L%����y�_�q4�1����6����T8��c,�\pSB����h�u(,?S_���<3���-�������P�����s�������g�������Sy����+�3��8{��S�#��������P~P����)����\�c�&��&��_/?�����M�066&?�q�P��=
.��h.�7h�c�16��-�?�P(}�	:O�T��f0���o:�x�Ejccq�����kO�#��8O�c�M/�y0�3�����/�p�%�Hz4���<��-7z{{EO�&��~��k�
��^v��Q}���#��8o���KO9
jKEO��HO������L��c����_�;��C^�����s����Hr4��j����h.����>���8�1��dj8����h
�L���t^�~���e�����=�FP�wg*�14���X����l���R����s�v����94��aV��_h��%z
����J��=S_��?��D�1��t��c�13)����������U��O�|qF���t���8oF������qqIOO�h�K�����K>�
J|�������t|��������f��'�z�X�PI�����4�1���������U�Va��-���h�M}	I
d
$��AA#
,��Z�A�6��A���������-z�1635o�{��S��n+�\v+F#1yK}�	���k1���E�<�x�	���Gn[,����jM�I,Ckk+�qeu��E�p�y��mf^���]<���#_�f!q����=B��a�;�����+z��iAQ�v��G�`������
W��-�p����c���Wc���������7�<X*�`��I,��}��auz�Qs����?,z@ee%
E/9��T_���.��y�D����v�]�{���l���&�uG�k�C���0������2|hyNZ�(c�16q��A^��m��Y���C	}��d�(��['Z���E�1��\��P�|��)(A�u0��=���1a���1a�,[�L���x\Pr��sj�Y�4q%��Q���U~����M�(���<���?����2y���m�y=�|�c��1�c�%J��5k&(	R�0AFGGE+5�v�=��|���%���8aB����_�_\�	<��3���'L0���9��.V]�WK���z-*MI%*�2��`3��`�������o�"z��}"���^��Z�0<<,���������o�����T���;����%}>���9s���c���G+@HQQ���L���?�����}�����J�Jw�1�:�K(��V����J����&�t���?����8PX�������"0��J�*���~+�cL��&r�c,�y0��d,������[�K�$o3������u�e�����*���EO��8�*c�1��>v@w�y�4q�7��������X�������C[U����*M�����\ikk�C=$z��%�\���:����xm[�����[I�j�����7��;[���FM����o�!z�W>�Oh�.�D��E����?;W���j7�����D0����$?��)T�=����\��K�c��|�o}-/��M�`�e�8
6,GQ���_w����O�I��	J�������x�N&zzz�E#F�4�c�M.}� ���`�1���y���&J�V��QW����O�P6��yf������|76���| ��	������4��(�!��� A�d��a6^Wb����}0�c�1�f>�al��Sh����	B}:��;g�6�AI��G�U;��Z�B����~�Am:�O��9�|N�`�1��N^%MPYI�w�!Z�����Vr��3�c�k�e'�<u��vk��:1���r�n��L��eN8��*�G�4�,���aUd�_�F�w��)*���U��j���z4�E�7����	�>��<�qD��o��f��P�^C��Vz�PL���9��c�M�y0�3����}�C�h�Q�Jz|���Du�t3�C[�vuu�j��X���g�1�����J7n�����[E����E+9}��1��,�-E���+�^�>�u������t)--�KH��������l(++K8�r��sz>�����yj��5��tmBKn"�:�S��G�����N`�\��T����3��6(�U��<�ov��1������_o��w�0�O,_���n��O��<���h�������d�|` ,z��c�M/�y0��w��k.�p%T�T�����j���_���;NI���X��c����WI���-`��
8���D/{4�����Y#Z}�+��d�1�&���� A��	%N��	���F�'��n���|�h4���9�����v�������I+)(a��%h���S��K��ZtM�6��ES��yL�>��#Z������J�*��3�7�h,�/��Kxn�q$�������3;~��EK1�D$���Dd4&��3�g�1�[�`�1f��kz����t�a�=�5i���O?]� 1w�\qTA1��K���h�l����������v��1�U^%M\w�u	� � B6e+���1x@��]+Z��d�c�p�I/���xz��9����t�|C��V5#����Xl\J����c�<�Gs�t-�����,�I�k�hU%�'E�����8�~�=������_�N_9
+����h,�1��<'�<[��h(��99"0FPz�3�����y�I+���3��>�`�1f��)����Mgu�\9����%/~�������+�c������m�
�|�b\��p�cW�����M]��H��N�m�q��7����_��u��:F�h%�1x�i�&�R�5��`�>��c�M����EK��gWj�8�x�|@Ujjj�&N����C��:T�AcT��A�>V>��
�C8�&zJ%�l���|��m���g6�/z�����v������X~P���9���fv���������E0�=�a�A������~���c����1�cf��W�[u��26��dl��~�c������[���319��t��_,�c��l�]����o���`��a����@��B��_/Z�1�����[!Z��Hv#������'"��\��T�ch�Y&��Y*Z�<�2���O���SV����c��Z�S��MA�����8����#��8O�c�M?�y0��wT���S�fWu����m^x��S��x��'��c�%w�����_�[���q4�1�����Q����K}��l�~���\}��W[�
R0�c���bEa�r��a�D~:�q4�����%N�e������	bu����=`8 �ECB��E�E�\�u����RX
��#z���ny�J?��lV;.�������>��$��%��K}:~���+����1�f�y0��g���f6FFF��3������
�$b����9Ccc��}�V�S���(�p���#?S_��s�	�X6�zI���`��������z�t]c��1��LE�G���a(�~��q*��|�.q"
�Vj�����&T����t��Q�I��w�����$���gX���#g�m\T�14��1�r����F�1�f �y0��G���f:�q�����w�GR�14�'S4u�J�0a�[P\����
w�v�M~�>��*����[�c,��N�PQP@
&�C
�}:Gc���8`����F���n�y6j8����h
�L���t^�~�����R%N���'M�P����A�S�)a���;K�����$����y=�|�������f�-�Q��%oK��h�S4���	������;_B{_��1!�KH�����y�_�q4�1����1�c���k�F�q��j+",JJJP]]-?�M�T4V��c��_J�E�N8��+g�q:����_�c�%g��	#0��������]�g��5>��Gp�M��������3��kR�{�D4<�:]�C8:�}}!4����������t^E�b������8���q���'M��ct���h��&H���P��*�����Z�&��I��;�<Sg�r^U!���f4��X��h�cH�5T�D�|������/����G��q4��1���`�16�qu���m��Pa����������x<���t���h�e�t����l��s�h)[r�J�P�y�V���1��)�&c��,�h?z������������=�B�������<�����g�����'?�6>*�j(H�:���Q�Y4P�<�O�2Jv���=E$�8�&L�9=�k��	U�?�
O�J�S�H�|�����N���z4~�4��

���"����7tb!m%�u;l�g>��_h��%z
����J��=S_��?��D�1�c�1��D�k������{��'Z�������q:F��Kv��7{g���5���7��w����9�Am:F�f�X<&o���;����Q����c�p�c��)�e7��?��8�����g�ylvq��(*��'�������a�Amc����fTP`��������]I���x�gf�kJD
��M������^�\�����xi�oD�9-(�����
�����g������U/m{F��c�1�c��[���V�2W�����+lX&z�q���|>_��t���:::D��&������������wH�}P�������u��Q�u�-�>���q��:�1�G[��MW�Z�-[�P3o�����������CE{{�����"z@CC�i�@c,����f<�RbF��S��%���k��(���������_�F����C���]�����u���;�j��:����C�0g�8���N��;YbD*v�uuu(���,
%5.���cM���?�����}���
W�R���A��_Ba�	���mo��u?���I7�7<����^4D��]~V����bEc����������J��iW_D�n�V����'����M�cL�z�jl��U��'v�����<��c��|�����G��J�����= z�188������TWW��q����mc{�5�$$R��X<�@�k�T��y+E�����{7bo��$�������`��s
U���Z����w�C�R�1��U}���`�dW��16;�cy�4A��[o�6lG�A�����{u7n������.8��`�����B4����������D�<��)Z���o����EOC����;
���Qn/8F~�j�L�P���fL���	��s����	�xd��p�,��^(��.�=�&�����)��r�E�D�~���m�Yk����s��f:|��O��[n{J(,����FzGPJ��U��W�Vn3����&r�c��c��|��]W��--�;���O����+z�244�����J�����%�����B��� ��z�������B���Hm���J���5K�E�<�����#u�����Zh�+��a%��X����a��c*}� ���(xp�	'LZ��1���y���}�\���	�*����z��fA��������R���v,�p������4/W	�6�J�������K�f���Y��S��WR�Q�`��U~�4�2i�Yu8�0AeO�O�a�16}8��c�L��D6	�h���x3�
^�W��E����\3'Lt�y'���1�y����	B}:����������w'$LPUM_�xKk���j�1}�M�Cs�j~��m%B���c�t^M� ���1�L�%MP��1�X~xw��B��-����:��q*�|�i��=Oi7{vk��:1���r�^�E~���2��"�W�<��=�����!VV�g������V]4������x�~;v��C�3��x�W�`2P�����I8c�Bq$�3V/����L%����X&��cS�c�1���y�����P���8��K!h<�3��m�U���b�7��1���Z�2�]����-�m%�*���f�������SV3n;R"oUZ���a�k&���R���R&N����)��+Z�1�\^E���<�n��vl�����1�&���at�DOz!o�\2����|���n|T��>|��+�}N+�������2������@AA�7}��h�53^u�F6�����<����FE+�HH���oz�y�a�1�k�`�1f��T���B�������u���e�*�x<������t������KE�\(�1����)�������k��\���h<�H�l_��h��R����r�9���~����\��O�Z��+�8;����0"�Q����A����5V-=�K.��&6oN�L������k��=�c���W�5T�(0�
�C1���h)+-R%L�(qB�U�~�Y�&�~�
���CK1n�a�k&��"�h,�/��KxnK�U�m�#��9���g�'&D$A���.� ��1�f2�y0�3�	-���I����Pd���8��#D���hmmE?����6�s*�Cs���eRC����j��R��(=�(}:������P5N3
��h���Y���e����t
�����b�*�S��=��o���-9��y�1�I^��u�����<������<T�i����v��N4-���TR�M`,�H���n��9����t3I�-%KP)J*e�O�������W]�������~�S8}�(�������4g6�"L,������1�f�y0�3����E����|��p��7������:;;���&h,�1��w�-ECPR(:t��Rt���?-s��K�����+���k�6��"r�g~�Pq"G�c,y�����16��@��	��&�%gD��~�MP�|3����>x�2�3^'�QP �!%IP���j��'h�>�`^u!�W�y�����l~_��}<}��-�~>�����tL��'���ft���q�?���OG�h(��99"0FPz�3�����y���?,���0��~�`�1��h���u���B���B\y��	'R�14���Q����,I�0�*����������b�R�2�R��a
�>�2/�����G�>����nP���v�#\a�16!y�4�cl�;��F�R�u N��9	�������7o�h)F#������g����@}��*]�D�k����������u�y���[rx�j�#Tt�SV+z
�\3Y4g����0E0�=�a�A�����_������1�c�1v8x�Hz^�7��D*4����X<�@�6�S�dC?���u�������C���"c�Q�R��i��,�X�_<�|������7�A���t|n�"1�1���WI��z�h1��g�]�sNZ&z���(����@$8 =��g������*�G����bEa�r����*$���|���b1������f��K�P��4F��A�2�������a���1 Z���6����������s������\�s�~�����h��y�1���<c��/I�b��?^�uqD166&ZCc��Pz^-�'�2�8�u�b~M�h������P��j�~�lb�X�� ��c�*o��w�q�h1������CX�X.z�p`��N��.?S_oIc�<����-�?�P(�M0��q*��|F7������VT$K�P�9���]�^u�Z0Ek���I?�����K�03���?�e|�s��,;	��jqFA}:~���+����1�f�y0��W�P$��7���S�>���������z4������x�R2S���V2�u���&."��#��r/�%V�5�e�1���J����EKy��cl����[����g�&?�{��O����������m(�2q���y��u�V�J�0AFFF�n�1::*�12K���H���/Z
�=�7������{H��mY�J|������y����[Z�<�1����1�cf�E�kii��-[Dp:����Cyy��
=�M������\3)���)_)z�����_�7@��:f���g�Y"zJr�P�^��;��j�1}2�Y���c���U����k����a��`��<a�Y��>�{n<��|4��3
���{��qy�7;o�R,<�Z���1���y ��@�H\~�>��*�W��m{���%L�����{{{�&T�1�v��fH��U���N����N�Q�!qeSCe���YA�"������cl���c�1���B��A�-1��u�h)����p8DOC�(yB�8�<�$Z@�000~]L�A�<�S���������V�$4����V�Am={�4�1�����w���_0�p������N�@c��%z����3wE�t��z9J��=��h�`w����_iAh<��g�&((P[[�P��*M�����~�_~P����P���6�{���H����W����9�$���4��0�j�c��c�1��`�E"1s-I���K�����q[��Q�	���5��_-Ekw��	:�l�Ty�h���j�O��$��j�8����cic��C��c�U�V%������
$�&��,����2e


p���e��r�hw?��?��8�m�q�y�fE�	�[w]���^����>G_s���%)<��C�TTT$]
��x<.'>D"qD�A�(,�������p>�K�OAAA�kO%^������K�2�����/����EX8()�$(�����H*�^���\����'��M�������9�������'z�	+p�y��a������+D����a����c��^�:auc>�H>~��`�p��1�����2���,z��B��_{@����w�y��A�S��$344$�8T�^{�!�bf��7�D���DOQ^x�?q�Ru���+L���w��-)��^�����h�{�}�R����XZ_�-��I+�c�1�&N;���	zCd*�4,�q���+.������������Y�X��o�$,S�{&�6�������1ib��u�D��-���cD��
U�'(a��E���s��S��7��M=���=��pK_k���G{{z������u{|r�c�pp�D�p����1�X>��Er�*E�w�}���3G�&����p�B�K/�T�g6��;Cm/�^f���a�/��������kJ��3c�����p��cS����:.a��)��W-=j�g�����+�3��
��K�(u[Q_lG]�S~�>QC�4���]6oL�3���h���V���J%���@������_��q}���0k������,=%9b�}/�������>a�,i�lK�`�1�c���BE�I���{G�����R��T�4&=R�M3��ad�b$�9a�1���R������s*p�����.���{u7n������.8��`�����B42*�7_�a��v����H�Nl��Y�����9�s)IzC������\}�o�=Bs�+M(r]iB��.����������_IoAm)~t��Y��'W�`��4�;�`�p��1��h�����-)-�:,+��`,8��`L�U,<�Z�=�r�3�{����vEEE� ��U� ���UW]%���������� �������3�����%����o��cSG;���	�R�c3��?z	/��$z@a�<Xl����!���=�#�7����$z�������S��/u&M�PQ���>���v��Xv���7��Itc?U�h������U�E��S-[
��}=����'�����T�&�9�/�9]I���;�j��3ZuQ3����J?_��E4w
�#���*�O��l���l�%M���N�`�M'M06�8���7Z(2C�p���,����~;v���[t������]�������s�=s������|������V�;�?�}X�&&5�������7���=�Hrg�Z�;��Y��1c�M6}����`�16e���-Z��[�6a��y���7���wDKYi�.a�����[u��\��	����%)�&�^/�������#?�M�������d�<��T%�L"���[��	H�����\i���&���4g6i����6���}����@�>o��%�c�1�cl2�n|T��&��������2J�����0�c}}�������X;���&H������<���������	���{���-��c���&c�M������`�e�J?���u�f,�H����JSfC?���u�$�~^�����Z�U���1*��/Z�� d�^`6������p���)������Jsf��oy7<t^~�Yt�i+S	���7�X�c�1�c�ih�v����"S���jVU�����<A1
zP���9���f������&z��b�	��V���G�[�yf�*�QXQ/?f{��1��'M0��E^CU��������>x�2�3^'�Q�Im��,aBE�h�*��3�Br��z�����`s���]oi
\�r�Am:F�T�Hsh��������W���h�g�1�c���@<��;E����[������mA���%?��Gci���z=;�=���X6h��=�%@y��L��
�yU�4���U����[,?fs��1��
-Z��=������W_����Eob��.���zw�q�h)��Y#?�]�V~�	xO�f�3����jNO)�EUr;���.��J&uU����sr�L����+�=����r�]�R�
D�:=�#���Kv	��V9<��C��~��C�F~�S`���H�*Stvv�0w����E+;8 z�%�\���:��z��������g�8�3Z����>�0�_$z�r���������f>l��?/�PC{E8aEn8���g>T9��0�*����J�#����#�atX�]B�?�j|b���c���$_b��y3�<q��1�O����o;O��E.x��'��8v�h�)���I/8F��#�����COO�7�+**��/~1�����y��F���sO�x�* �h�hI�������E�\8��c��������N;M����-���d1~�|@��
6�����e����zk��q�����o�� 063]�����kM���������x4�����|��F����D�\�~���[tP	��%���B�*+-
�c�M���T�E�D,Cs�v�{(I���f�������	 �v�����G���YX
o������B#J2V}E~���yM9���v�[n�,v���pz�'e����m�����m�<����D�1��41y8��=�y$��c,���B�\����>��H�b#�^z��.�)�~��@���*���e^g��.�k@i;<u8��V�c2�`�����I��uot��'K67�3��|/2}�<8��D/�M�6M{���L������g�lv��b�
V]�8U,Dh��������>�3���
�����W�=`~�3��C���iej���e�*zS+I��&(�@JJJPZZ*�S������r�L�41��;i���Q|��Z2�����2�Knt���v�~����vL^��Lq��������P\�N�0��������9�L|��D�1��41y8��=�y$��c,���B�\z������G�v��-�����^��|���g��}�����J?'E��F�0�W��������h����<c,����`�%H<����~�����y�1fD	���L� 'D���1:��Hp@z�����5'$L�<�&L���>/Z���0�F���I	m�a�S���P����p������@����"�c:QAM� �Y����h>]�l:������3s
/y[��Pf��S�7���H�0A�<�S��3�cf�1����F��%BP\#:�&L�|���7}��1haFyy��8Aj�1}��������L\$c�B��c�16}(u_N��R���x�?����9(`�l?�d���\}��.���cc�������^q$�%�ex��O�2E��f��
�c�Sw���V`x��V�Fb���	����k1���Eo�������p���^�W�PR�Doo/�~�F���,*�bY�Lg��Xx�����_��Jd�a�-��M�=����No��%7V]��1\���YQ��b-)��`��}���&XMV�1vx������Gv8�1�<c�������)�����9��kP�D�t_�i��V��6h�p�������J�
R[[�+���x����q�9���9��k��ur3��P]�c�1�[����J�`����;p����^����2^7U�@e"d?�8������q��[�����#�������U��fG1�m?��;6�^f���`�����U����������E^m�~<
P��x<.�Iaa!*++Eo����98��(a��2x�j�v*�a�������=�wE�vW� Id4������\�3,��B�c��&X�q�c<�y0��Q�,��{��G^Bh�-�H����BJ���*�m{^8��m(��<�sHd�Q`G��HJ����'?-z��1��}�����C7n-���d�_�d
WX���1�G	_�����3���Gcm�8��>��[�����	B	���u�D������*J�@gg����>a���3�T�-*�����j���7��5�*��p�����
�~����Y.Z�hH�w��q��:�1�X�q��1���h��%H�F��gT~6&L�x3&L����	D�=i*�14��a&%��-Es'�O���t^�8�L8��c���&�A6l����`�����j���|����x�����/����O�W.�#g
,��?���G��.�*�O��_�=S�l6�\��nO�>$Cch,�1�d�����-�p���"���hx�7���:DOa�k��Fc�b�"�V���|���1��Yq��1�r�~�}���G���P���eS�����~;S3(_|*�k�3�!`g3��	tK���A���t���*�y4��8��c���&���F��u��c3���~���_�����8��_���^��?�����j�#g��h��}�|�?0����XD9.���,������)���fF����D$����14v�J6�4���p��h���k���(j���A�Am:�,�%���f5�Z��wt8��?���p@��"�S��3�c��c�1f�%i�[��^j�@�h���&�a�ud��#5������=E� ����T���G��J���c���mK��1�'M��SSo���g�C��B����`������7<����>�o�J������~�7���bl,����Y����8�I\O��q4���U__����>�O�������4��8��������[���z1��*?��Gc/������}F�#����t|�G�i#9��sE�1���`�1�h�p?�wl=E����b;�J��3���@�i��P��k�FG3��)��Fhn6[z������wT���Hz4���<���Gf���q�����G.�E?87?v��L}:���K�d����{2�k�U�Va��-��������/�&*�>�3�w�����>����Qp��g:�k�~��i�7��@X�~���V0DKK��


p�����n�|=�l? z��^1w}�4�3���6����)�=6��H��������8zQqV1���a�����������;����U)����A{{��)�*)Q�������7|kkk�r�Dor���~n/��y;�\������|�����(�P���(��/��L4���~�]����U������b�����<��_IL�q�awZasZ
��-9�&��'^�O��@�cL�z�jl��U��'v�����<8����c,�������h[F����9�s��ph4���0�Q�w���o�R�3���~;v����c|d���8��sD�������w�F��M��'�J���K�.?UG~	�5'���C\z�?������G�;c�B��'�������%�}$���X&��A^&MP���[o=����SYo�M;���x����z��vZ��6m��4A�
N8!�E�t~9����E�#�z\X����
���X$�x4�ppH�U\s�*\�q�fHw��7���7Dp������u�Sw$���(���������i��������.��~�K�aLh�z������1S�&T���B�[w"��)T� awW�f���<�jq����8�z�F�m�G��U?�rlV�'N����`���������
7|����c�8i"�8��1#�y0�����7�����}s�8�/u&M�P
�b���-�(?f������9���GyD�&�P���.������;�.Y��P�}��������dH�X��v��I���cl2�cy5Vo\'+x�o6n�^l�� ���}�2�����$]��R�B}P��<�@c��j�KH��X���������S��-?S�-��*���%�7C�Q�/����8y�)tX����7�8���'����V����Q������o�c�s�m<��2�����	B}:���/a�K�����f����0����SZ#?��Gc�������q<`����c�`��|������m��g������H�0A|N���:%\�������%E�"���^y	=�M��	4g6%LJ��V���	�P����R��	B�(h,�1;�0aL����E.��y�g���x��c��]�	���l���"��O7����=%h�L�UF(Hu
=���U�R����VO�*h�������a����#���7<��m�|�����"���L^���v��
�]�2�I�P=~�����
�}77�t*	�������T����hjj=e�N��'z�


�Ucc#,����������/W������S~�+�Cy����<�?�������o�B+Uns��)������ �������_�#�D�����$~��^�������C%;w�8�����X>���r�4�[�H��s��c���Xl�5�bic9�����>S�>���G���v�{2�<z�j���v%���J��!���������-��WYY����~��'�0�y��w��������!v����
i11r����cG�������-O\!z�\cAQ�N���/!��!�#Z"������D�16SMg��^��w��@HV��0�>��c"{`N7c!�
|��W67�4o"�@�
JL%c��BSlb�M��?{M�v�%U��dt���V�5�����r�Lb�����EO�:K(q�D/��`MZ���+����Z��%&�W�ME�U����l>����\�R5���1i��������"����I��G�2-���4 z�I��k�h�����Cn����|5�aszD/Q4�P�hX�X�K>�(z�������]���^��(`A������=��LG�r���p�D�p��c�p����������a�M#�(�6��W[�SV���j�8jN�����_�/��cQ�^{��H�{FEO��=�T��j�-(i��/� /I���|����8���=�#����"��,����XXm�
�{����g��k�I&T�81��}o�m�0>��2�c��T����/y�4a\	0n\s��v���F:�����'���iEG���T�c3O<>��o���Q��jy+�L���w����k�D�1��������9Q���j��I�{{�������(,L���J��4�����
�no�]KF�{��.��U���5i�{����!��u��[>Gn���=�����G���o~j��6���z/��K�SPiJ��;��� �#�I�g�\�cG|����c��d�4��|��9��1�T8��������x�E��6��}l>��^��)��=���.=�Ju��7�/ys�(a���^���� ����t�I�81��B���}�&t6�m�s��2�F�
������'~Gn3�f���y��_��< ����MZm��
;��3��P����q������9��t^E�h>c���-�������q{����GW!�����Pv{�#r�����"a"W(�k�kA��QmuI*T�BEs�D�Dkq��4sS$L�B�>����|3&L����	��L�M�����t
3���>a�JU��
$����l�3���U����uio�0��=�yp��1��nt{V	���x3s�-��J+��g������q�-�H�x����%LPE	�
"�6�4������<�����#�B�T	��t�cT��f�L� �0$�q��uc,���4�����X��L+/�����{l:��+��'c3��y�UO���W�x����r���~�u���UoXR����: �����Le�����?�������\���]Z:%�iR�}"D�� ��K�p8��5���T' ��>jk3o13Yv�z"A%R%��i���i�t]"�bw���O�V:&��5������� A:���{��_n�t-�f�}�o��lz�9��x���<�������sO��&��<�<c���
;p�S�DOA��Zl���D,B<<�
���sW�s�.=����w���gD�_��/���C���i��U:��I���_��W�A���>�lx���,�����/�$w�=^QQ!�3�(.B[�R�K�g?�Y��+J�~?�y�tuu�C���`�Gq�i���-���L������P=��N���y�|���]|��kD(���C2��F��=�����5G�cl&����Ur��,�������Z
B+3r�@hhh��?,��x������&e�
�|��pj?LX�X�_��Yr�l:���x��'��[1�gC�c|�V[�
E���`�e�G��r��ikk�C=$z�3�B.\r�%��������Oh+sH�tJ�D'��a�FR�O������h������M�=�[Q�7}�1���o�m������k��>�R����������1�����������cl:�j��������]z^�s�V����t���XL�~������m����[���[>%z��Z�:�z�$q�&���G��}�n�
�o[�]�v%$F���������}����c�q���+y�D�v���/�9�����<������g�������w�:q���C[(���N������&���h���*\r%�L��a��=��M�Z&!c,�cyU�J_����7��B<��s�+'��������������D
�C���/���-����9'~N��������*���	D��������;��0A�r�0�k�7(H@	����J
zP���&������8���y��O���2Hh��Lb!����1a�j�9�R�M����0����<�`���/���h)�)&��zOnxG���[���V��plL�$�<Bo $.?S���&h^>&LJ��D	%7P�%;PeUQQQ��	B�Hh���&K�������l���.-E,�y����h���J�3%:�BU$�]�B?��s�c,����V���VZ��J�#��z�h)hUE��	u���gO1w��%�����r���(��h
������4�����	m�AzP�(��|gu�����Zk�����|3T���{��NL��_�U�
��M����h�����6}�|��C[�Dl��^����clzp�c���c,�{{DK��iO�0���	���O��iVsO�������?C�`�{F�g���x�����U�0&N<���"���f2�14��aL�P�X0���"�������b&m9��W+k�|�K��e��8��T��,"�X�^������T��
�����t�\�R���\���m���V�d���5����?J��k,�����2o�p�%Xx��Q`��#�A�
���r���[~�J�x\�[B�(����.�X�6���R~�U>_�����p�k{�.�xR/DA@�G��,:��ygc��O�^~z�O�����`��R|��FY���}{S�����6�9��>���
�m9�Yu�>��JJ��������`����������*cS�����y:�y0�r-�c���}���O�mI80���N�6?|)�V�-�{��+���+��Z��'��k�����*���9s�j�P��&M�&���'��M���������"C�D8aEn8���g.���q�C�`�[PX���;>ND0�B,�U���������a�%���*i�������$���o��Qn�x���I7��i,e� L48��X&�|=�l��b�d��9������eJ�����o��J���X,�|��Z)���j\z)���&o��G����E�:��*�0��7�(�"}������>�0�k7����?�������8q��o�����m��W�H����
%��:k�\q�1r[�0���������-m��G.��� �";|U�_
u���R����a��c*N��-�yp�#�y06�l������{�������j���2	���e����O`���m;�d�/C���-��Q���J+���|,W�j�m+J�E�
-����@���:����X~]�)�"[��P$��x�����8sVMHg������<��_�[����G��f4����W� ��x5>���c�1M^'M��0���[�nV���'��{���\;Sp"8�������D2�������~#-�����9�������y��VZ/�����#t�n�+�"�Hd������O=si~�_����ST'`��t��?���c��T,�
�k�W��S��}�D4�W�������t���GW���j�x�|u�����[��P\�N��BE+/����G�>_<��c�1'M��<��p����'���=�4�L������;EOQ�����`�����8����-:�{mN��hkk�Ce��9�(aBMVH�82U��L�\r	���mBs-������� ��M<�+���'Ty��VA���P��n��W�}��j��U��3?=�K����;.��O
����2���t3;�G�8�}4�{�������D<��c�%C��~�,|�#K���LU,i,��?v���&a����w�%L�{l�+v�������G�i�577'��A�&�����\N�����~E���f���w�=J�h��w*���	?W���(y��(�AEI��@I������anU�8����fN� ���R���R��I��T��)��+Z�1���<8�����@�m;�
�8���5ab�egB��]�:��:1���r�n��L}:��i���o}O����!(!.�@ ���������x�������&�����W�S7[\z�Q��=J��i���	{�4g6���
�	��l��9E>V]WL��X%p(����2��L6'��{(�&����|(��`,�������|~�����{�U�B���T:���x����V�������p��F���/�>j>t���MS����2��������ZImm-\.��%�2�h[�,_���s����X<����1�tlGR+�9�?�"
,q$��Jr����;���	U���_>�[��)&T\��16���D�p��c�p�����?�*~����SR'��N��B44��@��A^<�_����Tv>|#�_}J� 'F�\�����Q��iI��k����o��5�&���-G����w>mY�����Uib�s'c��E���W�1,>��3���3�?������G�;c�B��'��e�^K�TM]���7������*%�����e�������Q�KN;��	I�����������}7I��d����K������L������ww��p%���}�����o��5�yC��[R�����T0�����T{2����
��C{�a�'���3�����������u�^���`���9��(U���������z���+��
 ��3��M���l=��0~��=�P�O<N;�����+T��jU����"��c��4GK�*���X�kr�L����T�r�z�KU2�&'M��<8��
�<��v�����=�>a���*i�D44���n���U�\<7��Yl��,�[w�m���*Jd�<:�U-<Yu����J�����R��e��C�����?��2�?�����^�s���}N��
��b������	���%��vy�{t��_��F�M���n��9�Z�3/9i��o��-�&V-���9��&�b��u������Z&��/c���6i�nX��t���T�}���7�4ds��,@A&��S�����������P�d���}�:\�|��
(��Vx�r��L��"����%���n�&���Xq��Q�O�W68��;�2����

L� �^{-�TX �'`�I7�1%�0;[z���m���*md6���Z�9�o4D��]z�d��s��?�b�5hfr��_�?l��Y�R[�`
����c���I��1�<���c3��xw=���!�O�_s�jw"	�[r��C�����W�v{�1�F��+�=���.o��Io ��A-��#������iL��3gN����)����5��F��X__�-q���`������@����*��%6T%I��������+�x������h�3�h,����qB�5t��-)c����m�����O%�j��^{������(���7�E�h%����g,��f�����#@c�%��7��(�he��d�?a�������|�>8Kk����������O��R���6����K�yf1�B��3$� L}�I��PX��'y�M80���f�����	2]Ij��1��x����/[�J��t8i�M�y$��c3�5�_�-��7�3Y�b����$���{�����'z��
�i*k���8v�h6���I/8F��N.�&Ux����'�^�~�OE�2�IM�z��_���s��;T�#�n�?�������?=����7��fm�B�5�b�H$4T��^�_<}���0����c�v������HfB7�4n"{nN������c,?G�xwo��lV��p������Q�����;���Z��[}��K�G_�?y�0A*++EK����`0d��#�t�s�h)R%L:^/������2��2>�4:��P�VE�F�EO��1�	���[;�5G}����*����_�S~����H�ic�16Sp��1�O(�*Gd���9a����-�hD�r#�8�u���YU����|��bZTQ3]�)���8�~��l����0AU5}��-���[,?�M��7i�e�1vhF�y�E�z��W����K0O$h��T^{2�����;;��;���M���(�X[,����S�������y��`����P��o�;���v����	n���,���_�����H~���{n�A{zfJ��*�^����UW]%��b���"��MnS``^��Lk��U��|%������c�	wI,SPJ�F�~�5$=P������o�&F�Z�-:��SZ�������ct�^�+M4u��7�X����E�N8��1F!�w�Ho�H\n����\$z�1��Jl�q��c���u����b�����'�K��l~9�;�H,j(G�m�w��H��]��bnI��
�!�������Xu�3r{�����<h�����K)�1�����X<�����S�V�Y�����~Nt9�]A�]j2�=�7l�����������D�PC{E8aEn8����c,}���&�)p���-���'�����GR;��#q�y�`����p�y�������V��]R�����kF�x_W�Re�U�[r5,G���O?�;��
I�E{;�q�X�|9�9����@0&M���?�p������_�BM� �&h�q5�d����'z�O��o�mx��gE(�q'M�PQ��`��=���g���� z�1���	���<�?��v�������Z'?a{�����h�)���:�s�N��a_������=�.�U��V��&�q��!/
0V����r�8�m�Z[[��%z�c:�&�]���~H��eso_�T�������S���|	W�x=Z{�1��Rx��oE��=��H����(���b�j6�16������1������K~��������J� 4�_���<O��PR��_��<��H���-C��������h_a��E�4C��73�h�>H��c}}��b���=�����eL�8�x3�������k�9����$���1����0a�Q���d�]�\�u�-�UdO�0A�<�S��3�c�1�%J�XT5+&H�I�-E�PXN�Hf(C�p����E�<�&�%L:f��c��)f:O�����;�0�%2^������	buf�&����t
�c�D�%���"U���ie�����f��?���u�m�bXm���r���EGh�u}����g+z�m`�kr������r�
.��_�1���h$�������-W�(Yl��^xa��*E������`e��|���g�\ibh�����<�1�����D��JYU�*JX�����J�(�35[PLg��X<��~p��E.�����T��a���<��M�N��*�����&��<��c��|���~�y�N�S�V^�.�����HD��C���k1���Eo����Dkk��$	���x\�*1Yu	�:___/�'�tV� ��%i�����w�Q�(��6g������{�D��U'aI]��1�KG;���	
�z����a�8rx���,�p���%U�������
V����pPz���hd�fJ� �;�*'N���P�������0Q�\+eh�X>� :;;������q����j5����@�e��HwkB�Ua�\���#��?(A�]R%��^#P�Va���T��4���v������y`weN���F��=��~�EsV�c�q��t��3��c�'(���w��������r�������#�����k��k�^n�"i�����D/m�QSS#�;::�V�$���r"�d�����?=��/z��9@I��$18B���#�Xz9?�s�3z����~%z���No��%7:��@������u6����{�1�&"��&(xp�	�*�������f�TI���`�����GC��/z�\'M��;0���i?��K~�l������/D/5c��;����-��WX�X~v�V�Y�������>����#�UTT��_���
f��F�=�@�e�D$��.9A�nCz=�&��v��Pp�*)�>/�}^\i�1fF�4�[�`�p�������<���j�CS����X[��s���S����������]W���WD/���O����+z����	J���c�&L�Ah!I�������;i"�����1�8�@}e���;Z����n���s��S��7�+~�����2x������@h�_n�W�g_9Mn�&�� �z���|\I�1�X*y�4Ae�&��y���������R��sxK�*������t!�W^���\���}L�4�����o;Oj�39c��3R>�q7<�����J�q�t�+�S���\&M�%7
�J-���>�����N_9�S�4�I���\��N%��Ud��*�k��� F������������1�������K�c��l�hw?��?��8�������[�����F�V�Pcf�4�F~�c��*U�m�A�tL��N� o���-���Q�M��'�������T����_}���V����'��M����t[t�'�h�
�}P��mg�6�q������}��(P[6s����������(c�%�����3u?O��o��6m:�c���WV�>�0Al���z�q4��3��
k�q���7n�8.a������\~P[���3r����w��*�$h��%p������>a�f]q���T+&�$�P�Q�<�+�1����Dr"D��$C�D&L�|�c��1��?��1\v���J� 4�������]����%L����/����)?S���
O���� �q���,a��1:g�����%_�Lj��������$������s���	o��	r���"���h8(z:��=�q�Ec<�����O�������	B}:N�i�g��l�U�����/����������k��cl�y
Y��c�D�
�$]_(T��5�B�.�+@ie�rP�%��	:�_�As�+6�����Q�E�-�$�J���;�����b�7�2q���h���S�=W�c�M�y0�X��������>�S8<�p���G��L}���z�yf6��{��S����K��[�D���
�U~�>w��{;��o}O��[���-)U�H%��c��t-�%N,>�y�G6ha	�7��8{��S�#�����/�,�����>��,i�5���8nz�x��'���h��y�1�I^��u�����<������<T�i��ca�h��j/�i���"��c�[��Ey�d>J=����u��d�m�J�
n�������d�=�}������}	mRVV&��4�c�<�g�k&@/�`��Y���U�Q��.��G��1��94���V.��'^-z���c�#(o�#2���O�����5V-=�c��c�1�_�{u7�~i��6����p���.���3���s��*�G���u������9�s%�V���I��Z^��h��T	�������X__b"����t��M$JG3�KO9
jKEO��HO������L�cv��_�%U�}vV�P$=���z4�� z�1�Z�.���c��|G,�-��}��h��f#:���^���B7dEN��
b21C���RJ��z�����
��4?�u�A_�t��R�IUQQ\.-�dD�&h�J?���r���~	W�X��U\�O��<U��)(I�����G80����q	��U����j�1=9qB�K�Hv��}�O���O��+��=m�1�3����������x��cl���c��|���$Z
��V{�-F��|^�8�L��o-eK�T	*��zp�2���v'3U��	Z��c�B!�����~�_~P���9��/(1[����NF�����G�J�h�l`�Z��=	g�Z(��v����X�cf��y/m���I�#��u^U���9���L��9����m���c,s�e�16�N<�Q��C�M]�=�H,�����I0���Vx�D���e����@n�E���7�����*Y�	#��T+4����;����bt�}�c��G���t�S7S���1��o�{1��"��H��=E(����*�
��������t
�V��q���g�>������8�c�1���w�v��t�-��f������8�~����c��j8�������i�!_g:Pl�����������r5���B��744$/���h,����ytm����}�<fB�bCm/���\���[	,�+=W)}=O1���RP�/}�8��j�}nX,Zj	��������c���]-%G�S$d��6���q>c��U�����*Z�1���k�������������D��cH~����Y>��y4����1D�Zzm�:���&��i��������4��c����h)��D&��;v�����.�a�U��>�_U������jG4t��+N(�Z��7��E0�����_����;F�h<c����1������=�fK^a�H?���u�F_e�����L���������8p��T"I���>�D��th,�Iw�d�C6�:�S��_�1�C��_8�WT�^��\����S�kB1����[��Q|l?}�u<�u/z����b���ct���{]kv{:�-�S�����w��q*�|�K&o+M�qGv{{3��^�^�!,i,=E80��P��h�������L�gVV��tK���*���������a��==L�����o��z$� ���������|-3�U*�om|����UT�4aBE�hL�	���ccq�����kOH_�����w�K
�1G�ic����c�16�y
��yf�t�y+DK1��b�q��:�<�_��_�H�
U@I���������y�:f�����}	�m�#�����=�X�cV���?�U���0�����t�K%�^��x���l��Q�c��dT��[����)��G�����.+g��.J����B��0A����h�4�����s����Z��T��	��9�U�Zu�{K����������(�l��.}������:��K����������c$��}��<�|�����K�n�]�]riJz������c����1��E��<���~�J/��,��s*
Y�&P`���a���,����|���c�%%%������cz�9fD�AR%L�����E#f������~�S8}�(�������4���.�h)
t[�0��d�����k�,W�a�^y�cy�n���D&�C�h��%�+m�-���Q���|��a���������f*�TAc�����DK�i�E�t^�k�ODkzX.����zXlv�� �~��j���)��5tm����}y��;6��<^��Um�9-(�����n�A��/�C���{��m���c�M�y0�X~9b���=�G<������_{T?�l���3���F�W���4N���k��0g���z������&�%D�+fz�^������EEE���t���h��_��MCl��:����!-
��q�Y+&n���g6k���n�j�[Z��X~P���9���f�q��H��M���J$��^����sD�1����w���_0�p������N�@c��p�|=�z|����7/�o|�8of#�v�VT}������%.���R�F�wN8�\r�%8����Eooo�jt���O�4��A�2�@�?D+�Ut^��B??�c�t� �VbQ0��j.,�RB,��*�~��k��1�c���g����h)(9�>�����������c���<c,�x|�h)F�:�h�/zt�����I�I�-E�P8e��P(����{��u�V�98���z��v'��7��Dyy9����kt���+N�������1��&���s�7$�<���)��c��=qKOYMBCE�<�����f���������d�|`@�w���c��U�
��n�:qDY}A�*SE
(L��cl�<����e{�~q���*x��g���x�gF#��J/���M,>�=�!���������?0.�0�nL��Y������������%(���&'O��~�Am:��BAsh.]�,��Qz��������t�|��%LV5�b�'Tt�C<����s4Feu��k�����1����wE��82��I�i�J?�1�X�q��1���k������tO���Y���@$8 =��g��q:��y4����K���kE�����?���z�#q�����BG�	�+l���f������%M�P�9�2������EKH�k4N��f�x���1 Z���T�l��������s����-�pw��Xt����3������V���-[�9cM��]��X,?�A������� g�2�����>\�M�T<�QII6��%���B�]	+���l,j�^�����7��m����������8�������J��b5A��wqA��I��-v�hw����$�#z���������SI����^zi�`C>�w�
;�!����.q�/����,:�e��+�U�����_������f~��l��JH� ��~�{�d,*M�)���'�`���@��AqD�>��9P�W�p�T�]|x�q���:����+�aYC�>�S�{����sE(����V�#��H��Z �GW<��*c	(9q�V�bX>�H>~��`�p����+.���������^q$�%�ex��O�2E��g�m?��;6�^f���`������=���A���*1L�h4��{�bO���@ ��;w���>h1���}5U���b�O{p�U���X0!���@����S���yC��H0��������YWa��%7:�'}/�o���o�
��D%jux�Ny{Q=����z���X(��Pb%��V���N�N�cL���U�	�c���L\��L�0A��qJ��{r�;����������R�GVy���\E��u���ZtM��YP���/�+GdBch��&��PPCQUcB�����X� 'E�-'X��C���tL�0As�+0���17ab�y]�{
,��2�c�16�(���[����)��G�����"a�PD��'�^z4.�	�2��fK��T��h)�#����t^M� ��f���-���9I���6_K��h����:����$F�G1,=���0A�/���=�KMM���USUV��e��W]063}�?~����������i��C���dIc9~��������4A$�K�p��7��m0�h\Kn(tXP�sH�#FRo�A_���v+�c	k��Y,�+��Ne+C0:������7�&��~�m���{����a�����H^��t�Ru�Q�1���USQi"�"AIc����)�XQ<g��j�i:+M�/��)�w�mO��e�����������?���r�1�T\i"w8��R��c����x��r���v-y�������3���K����!�J��f�4A������������+����c`@�j��P���4��&�}o��_k1'�����7I�ZQBEK������m��O_�j�Pn��o�����:8�%���l�4A����������GR;����s�W�tqcLO;���	�R�c3O,�����NWQ5��/�I8@�}j�7?L[/z�����&��<d�� >V ])��P�	��"�B�U�p��l�>��U�\k��&��@f��?=��/z��9��*
"����Xz9?�s��z�N� ���P�~����w����3/�Z�T�������
xm�+�'�>������M$C������'����M�cL�I�M?�y0�F����}����Q���o�5o�{��S���:,+��`,����s���S/����������9�X,���OY9��+Z[[�oh+��c2Mw��x�N�mI�2��x�?q�Ru��!�W� ���@�1����\���h�Q�hgQ�e�W������)��(���25��3���m���_��k����jk��bn�b����X��Re���8i���y���������I7<�Vk%�b� �����X�(q���6�I�^�n-@���n�
�P(���0�1�:��:�/Q�3��&f#��Z$� �SH����M����'M��a�cba��%�R��h�!���F��9��LS$]�D�p�� �={������v����{�������{�
��h�=A�s���|��O`���7��+��eEU��1�?8i����1�����~y����z�~S�8JU4��l~>{�r���`�e'�~�,�����M���G�x���o�R��Z�J��
�===�x�^����K������^��Z�{EE�\ys2���	����1����e���������mO�
��k�}5������q�V4���>(z��?4�����UK>o���c,N�`���f�Xlk.~P�fo�	=�J�n�/g~�3i�@E���B������o��us�����U�N��w`tHD��6�CnG�a��]�yOi��M�tI�l���]!�'�~=�}vX���Sb������$�P&�q�q�?��)J�`�M
N�`l�q������?�������r���p���=�����h�)��x��v��Xv���7�r�4A���122"zJ�	��w��Q"�����
�������7yfJ��|�N�b����5&�0������~�'�l7<����^4����g�_�(V4V���5w�����}�;���K�c~�2�r��h�Z,�2�Xr����m~�c��@�K�����~5xj�q�#�	����
�J�XT�:�G�G)���[�������q�M�L�)���i���h>]k�tM�6��e��y����7:�����B�ZE�G	�]��f�A�0A��\��DG�		8��.���F.])����tLT�94������d�"
�(Ab�{�����%L���_H�>�:`�1�c,�k��>��	B�h��
��.Z��nzG�����TN�PQrmG���)?��O���4����3�[����%=��f��)�C�{���@�?(?�M��	gIsfK����<��/��lB��>�����c,[�>i��W_-�c�m�|-i"B4�e�'���8���ntC���k������eN,,u��a��y�G(���~������t�&]�>},��������r�O��dq��E/=g�2�dtX��S�)������1{���?R��,�4�D��~�/��K�`���E&�l�V���'W��mJ���R_���<����o�)z��i�!_�ll6�\��nW��Cch,�13���*lfs�I�(h�lp�)GaAmb�U��i����4�2i�lp�����_�[���q4�1��A��0i>��To�7o�,?O���_/?�Q}s���T%c3��y�
��`���,��=��r�P1:��xL[�������>��9T���
&�?���Q�����:/[����3���'�Z�&�0]�{�r{����(�[����������{ 4�$KX�N�Y$�'[��s��[��a������� �^�X��-8l�'Dzm;��!�aL�Q�����4��;�V���'"���E��E0�&Y����(�d��(���}�{E���s0�x{���1���c3���>\������pUI�A�c��.����x������|U5�2]1�\n�A�iK�p8,���p8���`
�f�	�s$��C���x�?s���_as�l��~^���7���=�Hrg�^�����L��3
U�0&L����;���^���6�H(����
���x5>���c�1�>v@�E���|
 P���N�������f�d�z�*��-�yIo|���
d:�����	���V{@�H���(�x����uP{�t�'�7���I3O���	���tS�3!�������8��w����(���0}r��H�����t�rL��iU�I��P���uh+���1v	F��E��%�tM�����j�ld�/z�+�P/}m���:/"�����������7,��0A"�Q��i��8i����I��1f�1�f�����������.�K�HDE���6�>��%��/�=��
/_y��z��#����!d�OP.�&z{{14����|rU	J��d�d����J��e��&�ob��+EpH��
U@I�B#@k��9��?��o�]v����K����mx���C�v�>7����C��p��F���5u����PY��;������G0�B<��Fn������E4�����L~�x�Mv��1�������+W�S���p'�}���1a��OW�D.�[�n]�?lxs3�8�?�	l���>a��J�j��2��'=�*}:����N�-�}o����#��l��o^��U*�%0����**Jt�H����	����
|�.���z4~�cT��O�����9��	�c�1��;�����8$]��
��:v�K,�o��P��\�-�A�s�0�K���O���JR��J�(**��tL��Ash��t�s�h)R%L:�P):B�����������o��0�1�����K6�����S��H�0A�8������D�1��������~����i��1��*w}�4�rD6h�7;}�D0��P��:�<�S��3s�h�^:�g�U���E3����3^#�*��98�Z�m��kI%T������J7<>�����_*����H��	[r8������,c���8��c�!���s���f6���o���c�j@E�Z��C�y�������h)�����7������q�z�!ZJE�T�U�t��������?�+W�T�N��m��I�����A���6�s��t-����S��-9R%L��<�S��3�X2y�����������*�&�@�����]w��5_	4��i"�8z�����7�

�q4��*=�e����)�t���U�3��h���=� �~]���!�4e:��������9�G�k<r���@��1&'G�]�Wg��t��)U���J�(���?�cl�q��1����}Z�	�mb�q��}��e.
'}^��b���1��u��y��*�*�r�Do<�4AcT��f0�"��M�o�oE�8�O�1��'�2�Jkb�xL��CewfW�F?��s�|�K%�"��7o-��I��Gc��(X�~�4Pq�K������������GQ�/��Y�Hci�e7?+�33���Z��� ����B�>��H\~�>��*���&���Y���K�8
����fA	���o��3�Z��	j��g�]N�`�1�{�`����|~�^�nhg��Yx��b����^��GDw�B�
���9��qDt[M&�0a�Cs�f�yM���
��g���(�"����rV��j'J����u�-�������czy�����_/ZJ�I�u����q�F��P0AD8�4��g���v�C��MZ�J��������3���D%�3�*�
����h*G�:���Q�Y_���x�����9}e����#�8A�}��0�5��2Z��$l`tD�����C�����=?c���<c,X�X���i�f��G���4���1���^���kDO�)�A�i��D����D&���I��Sq�h)���A@+")3^'�]��#��+��K�����
6��������-���ctN���5�Zf2�f�h)���������a�1��]��fM�.B�+2� �Z�r��
�3c���������K�~q6����p���.�>�������y���f��������h�g�����N\�U�1����������tL�LA+1h�X�e}�1�������������7`�c��c�16�-������C��FD/9:O�T��W�V~�}�O
&�fd��@��'�^z4�����c�3�]�H�I���Sz�k���
�����
��T���X�O�1�e
�xso��)<e5��#��������5��j���z��I��Rl�c�G��:�1�J�&MP  �t����;��C�c�M�W^k-��W�=��y��B���������G3����2i�g����<%���
��P/FzZ���h�W�cV��z����1��\������.=u%&L��i>=c�M?�y0���w��G��"4��2q"��t^�����J������~��x�'W%M��������+���<h���k���c����`0����I}����j����|�+�p�������>�}���S�O�%����aP:O�T��f��c@�ga)li��ny�J?�l�Wk���b��?}���q*�|�K�tQQuEE:���JZ2�����n��RXl��$�y���7�.#aC)J���v��8�g��MMO���M4�@�gKAVP���y��*��Bch,�13�S�}Q@!G��:�=*W��g#u�:�1����1��95����W��Eh��P;���"A������K�U4o����:��?���]r{���q���G���������4��	tmB+'������3��XL��P�B(�zQ%U��Mv��y��9��V|Y����N��oN�9B�������Q��h����,b�14��aF���R���R&N����)��+Z�1�������������d���q�{]}�l��&���h>]����8���ob�`~�sK�(��Q������_��C��)���3s3Z��ZgI���1��G,�y����4���^]e�K��G��0�O��cl���c�M�?~V��#z
��ct���f�Y�%��4/��[���Z�
}��H�N�y�Nq&C�C:��i�&4���&},��f����PMBE����D���eG�n�V+$���9�o������J������y��cD�\:���������W`x���;�w�Y��\��O�Z�(��`GP��#2���?����h^c���c�%�WI����2��7'f�2���"�����g����}�\�Wy��V�;DO�m����@�A�Bh�T��N����D��rxJk���hl@�cf�";����X�$�yw�]��cl�p��1���]�8-��D:4��O�P��)?�6=u����^A�>gY����8���};q�8�H����I�Ut���O�NH���A�>f��e"�k��C������D[[��<A[o���tL_�B?�lj��������*hRU����swbEMB���yf5��X��h�mz�������'Z����`�������p�(����~KB�ic�eB)��_�U�Va��-���h/����^n�~��	%'U�1pX�~������	'� z�~�1[�l---�'�0hh�����v���9��O�6����YT%���B����*����|Nn��<�~�y�,�p�k�������Tm�P9JZ]�I�@�A���[�|�H��d�K��n��Rw��(^`�7�����N�P������������^|�gZ	p_����n�HH���'z������V~��R�Do���*�>/�}^Q���}^?�b�5(��l���]!�Ii�v�TTvC_�������\��y�Vb���@+=(p�����a���cl6X�z5�n�*z�; ��ys����1�f�G��6�z\���H��
p�����*Z���[�E�m�]klL�v���&���c�k�WQ�r(�ScCCC������mb��8�����3>������e�"UVV��EEE����o���-�_���J�0�+~�����2x�j�v*���(����"��+��������n�K�*Lp�c,}� o��R� ���5k��V�2���1�r����	���M���	��o&t��&L�#�?��q4��C,PDq��E/=7	�J,U�)��0A�d
Cp�8�|
P:���8G���!c����c�1�?����$	��k�
z���<�������I���)

�kVR��y���*S�01%
������+WN�������W_���
��S�14��$��d=��}�&U�X~�6T,����-�O������"a���)- 4��h(���-�P&�~��Q"��.~9�L4V'��P���v�#�0����J��U�r��r���N�WZ���]+Z
:�GAuU���1�����7��bt�c\9}U,�����E@�0A\����4�3^�����g	��^"�41��]�(5G����������_����G4����,,M[���t�cT47�5'�A_�L@{zFDU�I"����<�1�����c���0�e��S8�>����-�+?S_���<3I�8!���f��D$>��@L�Q�"a"�b�x�����#��Ksf������gS��3?�H����
�*��f_b,�8�����$��p�5��D�U����EKA++��
�y��I�z���$�U�1�&�k�������b>2
3F�;	H�!�����f�����|3����dwlg��O�����>���?1.q�JT�[s����E@C�XC�T*}=�!�������������@����}J�XT|����M�~�Mv��x��<�!���G�,����%?'�J@yc����1�����*LX�vxJ������)������O���y��������T9PW�<�G���-���K��$������
���F8�-�T	�]3��:�}��g1}o�n��������
�����X���������>=�����]����t|��+��l���g�Y"zJr�P�^��;��j�1}2�Y��;��7<t^~�Y4u�?3J���t��_,�c��l���-��b��M����� ��$Y�Jc\��clr]{����Q��_"8�)=��g��-i,�����w����*F�����9�,P����������8�}3aOO�
X8�WT�>��T��q�t^�&���%���	�>�����S�f���wA��H7!�P#��q�|UnW��.r��>��Gp��B��g�
&���8��c���|W���*��������y�'7�#Z�����T������������<��I�'��	�"J�p��k%�����s:�(---��e��I?/N��-��,�&O�1:��94���Z&�0�A1�T�5���S���Z�j&	
�b��U~P[��^&��
n��W��+w�^z4��3�X6�2��� ��u��g=Z�a"�������c��D�F��[����)��G�����<3�'M�c
�����8�~>�_����)?N�'���KU4T%��o��������d\���G�,b���0;���_���$\E��%\����c���1���<c,?���m�@[p�S$L�(qB�U��}�oz���R�b�UN~PQR�1q"U�����1��[��$����C�4t��'��s�B_]SE�4�V��J�U�U�4#���GW�����Hj4��f�N�����'�{��GQ��u����\q�1�
�U�d��U	������L�z����V�UT��Vf0��	�


p�3�<e��F$��On���g^EAIW��
v������&^��<�I/�'��}4�{=�v$[�|��VqDC	mCT�R.,�>��N��K��n��t�
O�x�1���J����x�~��	��D&����a��.=���.:���{�����3��M���c&�#/����g�YToY����{���mi���]��j�V��T���#�l���]!�	���]�� �i�3(}��S�j�py
�q��6���3�2�d��F���}�[.��Y!z��|�z���@}�����N�c�c��<�Xk.~H��ut��G&�mQ�m����Ka��v�H�b;�����b.���D�T	��f�y�s�=��Ue���PQ�~���+7��Dq�UW�m���������k�Z,20�vK���[�#>�&�eG���D��1_�w#��g��U�����N�h��%o���H�#���p&�{��������n����4�V���C���"i�1 06s���p�����M}�����Wz1��@����[����>�@�YM�C�����e8�P0*z�R�^�E$P�����W� >�
EI�+W46�����_�p�Dja+"�������#D��?���,�H��O���U��U�������EG7�8�sQu��aud^����Ty������[/�s��'��M�������9�������'z�	+p�y��������B~�w	�����(���Zh�;��PD��"�7����#z���&��4�����c3���]����=�[6�,��E���i�}��X��P6p��"i�tI�(��R+1q�f��@�p��F�O���9P%��L��L�����N�:��%G:CCC�,���^��2E�j��������l;���&��j��T,�������}���g6�/z
��V�� �E�m�q��%���������6�����lq�;i���'�5?r�����7�c�)8i����np3�~i��)�bXm.�E7�q���W��s�2\���--�p���,�|��4����o�����$2$:�}*K��2�G��Kq����S��*�	��zp���$z������V~���������;��Q�hL��g*LM��:��&���(����������& ���&��4�����c3O,6�5?(z\i"�=�!�i!Hb��>IB���+�;���������.�w�}���3Ng����LP�	���^*�3�O+��o���R]�S���yC����������Eo�1��?��*E�\��U� �V��
J�s�%���������1������c�1&y���		6�t#^6Wza[��Dz��g�{K���U4��36�(�"����n&�����7Jz�*
�����a����6����LG��T����!a��sK��������j+u���P�
�>}�c�1�X"Jt�*��xT��HG?nIcY�&r� |.��9�p��$P .S�I��*ab�UV&���$n����^,�v�d,=�0A�Y$B��h>]������Rx�j�%L9���]�q�Y����	��e^����]�uc,N�`�16e^y�I�._M�r�V��>��3����Fa	���F�w�S�R�O��w�.8�%eJ��r��g����i��2�4�2i��0�X8&Wa8�G8A_[a��=�y?O�?w$�] @G�����-]_�8��K�yd���3��1�c�1sX6_K��
��^rt�����7������&F�{�+0&N�=z�q4��


�-9T��Z��ThK�5�����DK���x�q��:f��c@�ga)l���g�V�4F��k&�:v�������M�8�ucL�^��Q��P����N��Z�~�h�|��*�������S��;��pf��st�a���*����|Nn�R����J���6-�y����*z���7��m��a�X�cq��q���%�t��\+z���n���*���41��)��s�UH7b���j,�����������I>%L��,���+,���9��uSd���������=�*��Kw������}��V��WL�;w���8,_��g����!�u+�������9�?�=����;T�`lf����nxF������*�����&��p7�1�����;����^��j{�.�Caq+�l��o���	-����@�wo������<�����C{�6��T���.z����q�9��^��
�������*�D$�=��_t$�]A�e�V�S0�����^�m9\��O�����t�~����v���B".��	�I��������a���?��M�Z��R������!��@�����e���yt�����I/�����*�����=�����Tf��oFZvb�w�=�n-@��!�V&
��&�����W�o�-��KE���1ib����If���x����^%K�H�0A��7����_���)},�t&MDcq|������$YeB�&~t�:�&3�d&-i������v
��I'B�FzC�E�m<���)^�O����I��N��<�`��c��\���m������8�>Xln���Sz�����W� ���
~|z*��"i"(�g��3*n!�$�v+�q��a�[
*)�_��������i��&���#�<"z�������0V��
===���]t����Lv<}��-:���y��L$���V����X~�J�D�u����(z��f�\M"�����m�������.�	YS������>�����_U��BC]A�+�j5e
���O�m�S�c�����;��C��c"^}����V����3�X2E^C�|���k���h)R%L���:C�t�K�-fVE����w������w�C�IT:z��1a�V�7�	3�|{\���WOi�����}P���t?�fR�1���������g��q}��^���Dj&\?�c�1�&�<c��V��#z
J�����Y~6&L���J����@T�BH�Wu7.��XX���������
%=�3*J�hkkCoo/�~���6�'L��%LO�?���1�~w���0A���d^u�h)b��[��!�����'Z��������%�R"-I���	���uc,~+O]���5u(@p�	'$�X6l�t��1��Q��!o�������^4�����f�2A���-eK�T	*J��q*�|f^��%O�bL��8m�J��*#aq�4���1�����z�����`s���]oi
\�r�Am:F�T�Hsh�������:�!?��DI�;�9���		�e[@~�
�w�9���q��������`�c,�`�����q�\9"4���m5�,abA�vK����>qB��?U�0�u�����Z�CCC��������4���V|Y�-]�'�xk���G^#Z�B���Z,4�m��B,�UP��f��� Cha�7�2q���y=����dF%M�/��x�D���VVP� 4��3���*EKz��G<Cv4�������f2�a�E��������~���0�[�n���Bv����+�i�7�g��[��������%��4���:���l����(�n$;WZ����]�����d�����Iv.�}��?��16{%�G���q��16�r��_\�9UEp��p���������+L�p|�Q�jC��O�P�K�D�	��fa�Zq�e���rdBch,�1#w���[}��I?7Q`�e����fw����!�{���<Oy��V���5%�%}��}i'��B#Z,U?����k�y=J�b��t�����#_�%e�
>�vP�~��������N���U�������o�k��G�R�7n7n�����[��'c3�s���-��Y���ga�I���E���@�-;���>�3�.=�8��C�{�M��w�
;�!����.��d�Z�����P�G��;N�����d��U�P-��_�������?^���.������r�F������g_����������=�U(�`*�����io�����gH����Z���|��
�[.��Y!z��|�����K� _?o�1�d8������q��[�������5��������[�m��2�c����8���7	�'C�`���f�C�Z�;&�"���}a�F��!�rQ[lGa��l�ch����J,��>���Nq$=�4q����6q��z�d��(z���>��g� z�D2����DO,)��X$"��.?����G�����G)�B������-F�dA�r\����6�#cF����Wm@����M�n���)3�����\&���\�?�O��S�O1�6z��n~��EG��T����_�F���*D�|�����Q���^j��Z������
,��q��r�4���v�H�������Lt&�LH�����w�=��Wa�/vt�O$�~���s^�2WI��F%(]Ev��VX
�+@4�Em�AU%&'M0��8i���<���c3W|l��[��$��eiIc9�������ISMK�Hv7y��+%n�A�s|6Ty��<(qbWO(!�����?���NG��/�0��������|"���-:�5��'�,z��{�'0���K����Xt��E���{�My�Q=��v�8	������,�����U����n�K����'V_ z�16�>v0})���m��Y��u�N���������[E�1��^�!9 �G	��N��.?&�4�����
���^�����|N�0�@(1a�n�n���~����I��9m��`��p�VG�_�6{�7
/y[��Pf	�&,v�k��U��)v��s�����V��-�
��?�1���<c�����:.a���}���F~����M��<3I�0A�?j(��U���i��"�n�����k���		N�uuu(//�'�Am:F�T4���U����l�;�8?\z�QXP[*z
J��i���	{�4g��D��]�0>r���)K����t��K��	����E���?�F����T�M�d1&�);I��s��u���m�S���n9+�U�#���n���R��U���0J�f%��q*�|fN����hE�6����M���_L�a�Y��N8����C���k�����u�hHQ��+��8U���3��
�y$��c,_�������%��l.\E5��K�G��L}o�\�����|3H�0A��m�cp����*%fJ�x���DKQVV��!z:F�z��f������P���I��@c
��+}=�!�3;������I8c�Bq$�3V/�������v�%��3�o�X.����T�~��J���4�����@��4�d���W\Ld�P
4� z��w(clv����vf�_#���f�p��EK�6ND��t^�~�����*0*���`������U�Ut^�-�~���=q}�5����;S���o�����
k?$�%G��	��q*�|�cS�c�q��1�/^y�I�._
�)*�Yn���q~�Q�PW���
�!=��9��nD�i
�_Wl��>d�y�*1e���7���1�c%��}���|���FjWW�hEEEp�R��I�&h�J?�,�o�u��w�n����Ue@�Oz.U�t����m����=�3�$3sh{��/��,:��-��o���7�X�c���I����F9:O�Z�1[&k�L�~�Su]�{�oc����p�}pV����3��h<�33o�R,<�Z���1���y ��@�H\~�>��*�W��L��Q2<m��t���P%J,�K|V����
�7kR����B�}i'��B#���8�L��u4������m�����g�1�f�y��1�X>ywo�ho),6��Yt�����������k�L����c8�����e�bQ��j}Z�L1�HL��#�[\������A�>f��%�}����<��0z{���U�0����t
3�~�.�R4T%��c@����^�����9Ecq|�_�s[��#�=�u�<������+x���E/=G�c,3.i�nd)(0�Z`6��T����v5��������G�j���._-�8��Cz��[:N�U4ow��F��=�r�.O�R����v�����-9��y���	����(a����8!�����1�3?�H���vD��'�X��C���f��"������7�3^�1���������cl����~t�DOz]mK^a�H?���u�U0�hD�YP���2�b/�*�
���d�L1O��t-}�},���h` q�L�$��q�����2�@�?DK��#U���X:���C?��������u��QXQ/?��Gci��Q�����X3�
�E.��y�g}EMB���c,3.i��[o-
�I��#
"W�Q�}��)�B/��*�9��d���k3����|W���*�������q�t^��
���y���(?�D�K���xf~r\(C��*]��.�d*++p��%��$G�����a����6�'S�%���fds&�`��N�cg�c����1��8���?�
\����'Z���C��G>�5EJ5c��jA�^C�D*4��������c�����2	�HD�R����**�s/;�"��M�����w*I�G��:f�}7�����I��7|��-���[,?�M������\�j���Pa�b���F��T��.�~��l�3��8�W�������c����	�Q5���`��o���i�&9������������F�����e���
��Q7.�l lm��e	���Z�B	�@���k�������G)��e���B����!�Iq�x���/������G��I�kI�����{��������w�q�^\�:���B^�Rq����?K���s]����{�����[���97�����\�.����hA�{������.�U�|�-2�����8M;h?���������m\��&�[����l�����������7���eMnj[����S��m7��kL����IS0� Yd�`/w?��,r�C��J�~��NL�Vi�T����{�nm��]������1�N�}m�#���w�
d'�9�����=������L�8�r:��%=�Ed�����7Q��~������g��������,��^c"�wl-���_2-G[O@-��Jd�~=����_4-(lEMx�<�dA!z��w�a"G���Z���"��p)F�i��\�<(w����@}H�3���#&����9U�����[�����%'�_F��������������OA��8�,���t����i���I�������_������q{)Y����G���u?�B7�>W�&_���o��
}fOq�G���Z���K"�&��.}�R"���e��*�����y�&����Q��J��������=	T-���[0�N�'$�����'�$�:�eU��~�Xe�_�����u�	�������������E�'�=��{�cZ����6��L����V;�[�%��%M���fk
��>Y�������M�Y��X�D�w/������=�������C�\6z�� ����b.Z������1jymJ�J��D���;��o�����C��L��f;�7aS����F����&r�H��6����SC�;w�D"-�����p"�����	����/�k���t���?���Z��}
����T����4H�
H��G'����K-:tH��{��*H���$���u��~U�u>��DN�$��J�8O%m��	��0��J�3r���]�>?n���iu����_X�����=*��;L$�Z�:ud%�i9�����o��"w^hA��p�z��%1����?P�dd2��������}�}���X�P��B�J��c�>�[7x%����nrs#�Q9`����9+����G����%-���\G|rPR����o|�6�o��,y�c���O>��&����4��0M���\�C���u�~ii��<S�u����D��)6t6KW���K����������w���mZ��O�>-_��Ld]�[�����w	�abhh(�����}��[��D����=Wb�?��=m�5{��	=}Vd�����)[^�NPC�������o�{�H���D���F$2l�p����^	��M�LZ~�/^d"�6�uK���Kt<!�������o�������Z��9�j�?�������ZN �b������w=4U,�P���z�7���c������������������4Qq����&��H�3w� �5xs����$��\Q�N5Y��	-�86��`?����7�,�Vz��� W�R
�]�?g�Lh��;����:D6ipQ�O��|�.�?��k��DK���?#�y,��@{��|N�@:���D���^%�|�Z-�S#G���u��hBE��v1���Y��M�FI�3���&�W�;+����h��]w����&�:=�{z��F�D���(��Gq�<����_R�������9�!�&�����9#�TL��b�<g���%��hi���T�x���h��hB&�k��+���Md�g��6��k��]�]��[4�{��%��J-�GyD�x�	9���%�/�v;���/���[������{$y�K&���T/t�H���Y�����$�+?h��qf4&�~��&��}�o�g�(%>5&���M$���\.z��NQ��S!'T���y�����w�����J�^������jbE7U����a���U���s�_��nw������i������N��ng)�~�[j�`B%����%8��j�=���T�r�G
�4��P.\$tV��OX�
L(���3o\��<?�W0��x���"-]�l��7m�>���:F��2-|h6S�fi���Y��}�Lh��(��#�Q9���_�%/���@":q�������	�u��ZM�f��m��,�P��F�_�{|-y�3�)���7i������y&����E��s�����NaD!��T�=�_���-k:��������_guG�9j�����L�!��~���[�~U��N������Y��W��WS���	�r�
�>lZ�bZ"����F����z\�emZS�����d$9��	�����������`e��/r���o[��5/�*��x��\�+�-\����f����[����e=�}~�y�}������i�������,������;�u�	�*�(F�iVr�McC��y��d�s���N��y�u��Z�K�&3���y�����w/QZ+�/x�=��\����1����Ji����D���$t	�c���F�X��]��k���*�}i�k�%�R��oBK�s3���@S���ilh���
&�^#��V���x=��D�w+a�J������,d����{P����;��|~�+a�U��V��N��[������6_��l
�.���	]�S�����}/������G�Y������&r�C�������)(hY@!�������vq�4����:���(�;o���Z����'��=�r�C�������i*�&-������;��a�����9=�aZ����t�rz����F����V��o���-���=*�RnI��Z�C���H"�������v�������,��N������-����B,�����9����!�`������_~��[7]'���j-���$��������z�t�-PzvM5M�����D��I�/���R���J:�������\���6��U�V����]|��~����o����M4��5�e��j�������|,�I�%:R��L�;f"��W��?x��&�-���?�?���;�Cp�P��N����k��k_"o��?0��j9s�����:�������J0�2����7�000���X
���~,�}>��V��vil
���t2n/���aB��u���/�����LZ�����Hd}�_z����g
G�rj<WT�����44����J6=���?���^4�����6o��:~^d(&W��pf�H�E�������>��>V���.�X�#�f���y�&�z������&Z�DJ~��h"����F���&*,65"���:���WY?��)X(���#���f��+����ej8��`�_�>il���L����YKt��e�o���o��l]��D�E�C�c�������'������}���!��]�l����V�����Z�O�P���&�|��z�|��l}-~Y�>w���DR����c�\����s����o��tly����7���Y�GtF	����$��YKt�{����&�=3��<��_��s���ZV��+o��446�=����!���j"3�f��Y3l�Q;���Y����d��^�������|���-�e*�HDt����������3���r���;`.\��;w�����p����y�9a?z&��L(-th��Y2�(o�
w?O�Dm�-��L[��b�3����"}]"k�k`}����x�k�����f�c�+���i�r4��N�5x���������f�����&���G�y��F�������;�%��5���`��q:��y/e����$����P��(�-��E�m}����~��M���Zj��+���#�Mf.L(��Zq�����	�1h��u���cN��m���MH���Z43���������r�������Z�E��y�������O���9IL����u��`����Z-�PVm��=��&� Bg���Jt<a��������L�8
&����:�E?���wP��u&*M�-�K�mcn���hZ&�X�O�k�,�xT?]*�]0����'.��#��f���YBy����$�J�iu�i9���Y
�����X5�����A�G�2q����h��mE��P�_��p�K<��l�}:w��t�yX
O���Y����p�:	����p���!�Qc��K�f���'�%kQg�S�>�,z���?��USy���������������3&�>-�p���6���$��S&�9��o�����M��Qg�X%���y��M����y���yj��_�����D����L
��7m�i��Xcj��;n�m�����3iN��Y.��;����q0�&��C=do��{�n�r<x�������
�L�����������o�v�?��?��Nv6si��`������[���?��4��7����VB)���z�m����jU$w���u���X�C�k�,��Z��S���6���9�7����`����{�3�A�$G��=��~��<T���������IS��D�t�w���?1���j��p���h2-G�crj<!C��O��G�u���q:����H[[���/�u�	���[m�>�~y�[�����W�O��"�O��=��&�����8��x�>�>�>_-h�5�G������=��d�ev_Sn����8Q����P�������=W>���g���o�����fffOx���{������������h��-w~�����J����!^[
��)}!i�����z�tt�E��y�b~��y�l����Xl'�_����M��
����Q�~���i�������%��b",���������M�\��b!�I����	,[�X�6r���F'E�:k���"�
~&>y�5+�U�����=�D���}T�?;����E�������8��a7��Vn��kL����9"�{�m&Z|m�A	u����N]�wb,����q��v��T���j%����nr���(���2���_�g�P������bg/�?���#���+��R������5��V�����t_��+����.�Q���u\n}=����1m}G]�>������,-�D"!�p.��]�����	��&��w�����AgmH��!��G������D����L$����m�C��5r���������7��Y$b��{����>[V]�;�����#�GR�\a��g���>Ck���f����fk[.���_|���CO�=�ia�������y?T�R������_���?�{�~���1X�^��_�_����&�L?�������rBa���w�<���&*�(D����k�����z�r���0?/�0���t:U���Y���d|J�������^��/�������������hB=�����c����u�N����4��bM�3����&�\n]���&W(�^�&��C�Yc?�������yk|f-u�������:5����R�N�?��gMd�V��?8��Y���Na�������\g��Y���~�����zP4Q9�<J#�Q9`�I[m;��i�Lo���8����Il���D~�������,��*�P����h\��X�T�|rYW�N-��,���"�����D"k���g�(errR���L$��7����9���������)�����_g�(�2��uI=8�����t��_�FK��pKY�1��W�����-���s��o����&ff2����Y����q�q���o\}�|�
��^[�c&����*QE��I�9���.�r�Jr!��;.�49����
N��[��RSU���ZP��P������?��Y��W�y�sLT�����}z���3�e2��vIS��_����L�"&�>%���$1=no��}����+���B�aA��?���~���Y,R��������:��n����	@1�<T����>�V>�����;v��j�S#�&��h�O���U�V��#�t�0T��H�������������b\Cw�Ck�u��~�yM;K�G��N]�>���*���Hk�z{������5��}�[�kV�D��/�����*d?j���?��?5�?��-+�����w	,��g4����;)���=��X�ws���Yr�0?g�0��=�����"gf����t���%��;,��2U��L��(z������4�IIl*7��r�4����������������[��������������3M�'���M��2��*;[E.��M(�=��z���{��d��������emo����9�5:�;W
�'j�������6�����^hb0*�I'a�w_|��?c� ��&*�����yF�Xy�����O��zg��W���fiZ��a2���h.��tI�ps��z����K�����-��|O1)���������r�	������hkk��Bg���&�.��[��[v{)Tj��<�������B�}�]��L��\3M9>$�~�_M4��5�}�/����W`S�?�����D��%�(m�!�g�^$�����X��"������k_l"p0�D��3�49�I��zq�o>��u�w�Va���������5�&�cT_s��C����=��(����Z���N�:4���s���:��������-��������~����U0�n�u����o?j���Y���P������y����U&lZ������k�;�v��U����c��9�������c��k���W�������w/��l��r�`9��P-��������L����������`"_��������W|sL���ssW@6t�7���P�����on��t�����M�O�Y��g����QG�!����j�b���=v9��������A�3B�kvM�)�
<�6�>W����k�VK��V����)��oP���%���L��3K*�P~-����y��EU��n�5�&	�B_��{q��_����%)���sk�V������&	u��+�H��y����R��y����<�|�����,g���&�.��>������n���~�Q��z���:���t<j[O���X��_����iE.���;���z<����k]������z��_�!;�Yg}��bm�>=�?^v������������-���S�q�=�&�`y��PM���+��3Sq�T����������y7x!����������u�-{�H-�Y��t��D"a��722b"�w�Rk���_��3hv�o�0����3K���cV�����e��]�����w��s%8vN�QG��K�����@����[k�<��i��;��?��4z\�e��@!������R���C��W*��A����UQ�\�S2�����SU��]���{�����;���$:z*��!��f�SU^�eT�`"����Hn���^������7M$rEoPB�����DF~6����}���s�����by�3@kAD$�������ip%*e%,�1�s���c���&��z���w?f"�.�����R��L��y3L��=��r���M9,�Q9�<�G�#9`e:zrTn��M��O�%��g���=�_*>i��`������R.��m���[���y����^���:��\N��g-��8��{���P���7g�ioo�`�I(�����i^���hi,v���9���oS�M"��~�E<}���x�	�n�<�J�e[	9�h"%������.�l-�:���������JB�������3�������Z�E�&*.>�����~��m_�U^�@Us�.�3��������������}�;c����tq_������{w��?�	`~�hB+�h
�J�c������iI���`B-g��&(~|�oK���Q��G��o�OO$e��5��.�n���6��RM(M6�9?jv�����Nr1
&T5M@��/��y:�A�\�m�.����M�(��r"�� ��\����������n��K��L��d�^��=��z�����_�-���9��:�;N�~�~�����<��?6��������yG�rj<����o����m�'�n]w���Z-��~��T���|�S����=�k�z�jy���,>_�;�+m)rO����{
'4�������A$c�z�������kn������8vn\���o�H���=�D)��1�>m"�?������ k1E�S�}��D:����,i\
E�B�j	`~�hB+�v��?�Zo�#����E/�P�U4��W>&��9�������������Xn���_~�l}��&�RZ���s���Q)}�"�WQ4����#O�]Xu�����_�_&��(��9`e{����CG��hn;�����hyfZ
��,[{��Rbf���dF�Y��r��f��|�!���6{
�����Y�t����T9����� ��V%�R��e%�<4��?�����{�:	�����f�P��/_!���v��YZ���G95����v�����[�d� ��;�|�&��i��:��r���Ol��]QH�.�8�)�h^��������@z���:�D9�Jt}O���P�t�	-���WT����������?����_�n�D�i�Z.�P�����#�,o�
o?�yj�A���/������a������+���i��(�XJV;7���3
|��1
&V
�O����D��On��b��\������	uY�5�%M�K������=
�h�(�������Og��%8�1{�}�8f�0��4*4�VM�3�U�t2����p�i�V�t��t����'�i������?i�C}���..���&�e#��������
���^n��:���L��Hn6���'�

v?�_�}�:p��D�e�<\�t����=�����r{�k^#7�t�}�^�l��B�5k~�^�C����:z^���?��s�$������xD�S��\�c��t�~�Z���������.���z��;�(�,-x�l��z���MIl���<�>j����:�&6�����W�0{D���#�Mfd������\�]���{����3%�����=���W8��tzK����}�����c&�^����������;�%��5��>��:����&P��}�����h�K�u���W���!s���P���Z���Z���e���Yz\�e���veg���&J���2���$RK�k>����{?�o���]�c������������J�.��}��s&r���9?��A�9�����k_,/��U&���������#"	�Z�>j���x��{�=J��5����D5c}O`~��[����DZ�7���x�3�LJ"C�$3�{���t�������`Bg��������Q�m�3��;��/�c��#���>Y��$�������-NO$%�����������+M����{>yFdl�����(x���I��r����U���&�����'���}w�w~��]h��h��tR���/����7��n"p���T��;���(���2=qbD^��/�H����`[�4��1�T|J����N�>}��W���.���S?�C���D��Y��,���9��xZNO$$��e������u�*����O��?�i���Mo�u���h�xs�F(�o����{�E��J��_���&��hB����������x�n?_x���x���|��S&Y��&������snn8p��<�d����7;�����'��v��/[-��m��N�3�������v�^�}�\���_s6�v�<��Ko�������{>�f���f��6��\>��O���Y��E�	$��Y�����II%"�
$��M�a	w���B-w�������3��[9z�M�7J�����~ �dF�#)s���y�&W��Na9,E����I�:]�s����,�������g\��+�������y�=��
���%�21������D��~�3v�(��9`e��'����L$�\'�YZ8;m"��?�
y�[o0Q�9������>l"GW�'-����$�L�7��g�P���.�����j)�X
:{f��
�����Fc���Y�t��s���w~|����r3������������zL�tR��|��~O~z����+�_'�~��K���
%��;w@i`Q%"#v������=����kW��������TZ qz<!�&���`B��8�,��?�!]N�B���\��j-3'�����<����M�O�L�L�b����[���O������cO
���$G��	�3Ph������%^k������&rh����������	�_/�*R0�t�R-��=3�2��(���j(�7;�2�U>��LX���pV���:���?���	��I����`"g��@�*ip���o�5n����,�I%LT�tZ�����d��o2{J�~����V��C����N�~ml��>��E�S�������t��c��h
��:���=������<GVcSy���~?;>l��v>�,��w~Rz��<����?�����87j�u{j���B�e��W3;CZ����b���E��e��*|��r�i3�������i��7(m}A�Qc������������`�$"���hn��ddBfff���k�����n�:�m���/[_�G��w}V���b	�o6G!+��z\�i�w�����a�����?��T<cZ�y�y�`����W���������1O�|�y�#o��}�D�i�H�i�<��eI+�mYkf0"�e7��6[}6�6;w?��)t��n���
u�IK���l��^ih��[���d����N��G�]����|3y7�)=g����s.d��y9�������L$��o�����4i����96���~=���N\8j"(��	���$c�%��Jrz��}B���'t�:���2�%^�pu^�]����-���"����{����Z�����/����H��� =>8fK�)
Q�|�>���r�$��-������<J�z�*�r���_x�y�SKN<x��>v�D���O�w�e]g�~�Xe?��:+K�Y�%X��=lN\��~�^��Mo��;�Bt��B�_���g���	�uWdu�����y�3�����������"-]�l��7m�>=��o&���J��f�S�]�9���������L����@K���t�w{��/���t��w���r��!mU'����'M$200 �P��u���A�������0�� >�gffdb�g&ill�L&e"_S@��?w��By���]�����t2*�#'L$r��7����[1����>%IkS��#�c�M����475i�u����'�Z�Wu:&r�������\�i]��&��P�6����^��
+�_?x�|�G_1��wVK ��tR����'y�3_&���v�c��r����R-��j���B�y+���O��9�.��5v����YID�u���-���+�v��:��������k������]�=K����$R��������'iY��j����������D���7��u�L�|��C��\>Aga���J��"��r��W���.#����s&0D���o�"4��x.U(W��{���\����ay����m���Y�%�? �����23�?#d��J���u�1i�Xb������I���~})�����+�#o�-W
xg�X<�����&�.��3J�eb0*�I��Do����n@�;w@�j	`~��h�]�p�������$����
'�����B�����~[.<��&r��z7�&4���S���F���%o��&B���&o������7�o,z��LL
����\2�Co��l��p�	@��hX~�<�����xH��;��E����)��=�+����i����W�{�z��j����C�>��+|sW�`�D�D<-�Fry�57�$W��.U?-�(������}�D"/}�Ke�����,UA�bM;+22ic��m��2A�GDN���#���g����.�P���L�?n�r��`[��������t/o� m�7.���Y4������t��D�z���{j��xB&�rS���`�My����H&��E+�e���*�P�������:�f����~��J7��p�u&rh���A���;���	������a�Vy���n"]�(c�$�wVh� K���~w����`(�k�\kZ����$�S&��1���k^t�i����s32��
&T{�wq�5y�'�U�8`�&QhsL(���nz�j��O��I�N8E���Q�}]IF��B�WK�Z��&-��a��z���D��{
&��9Gm9v�1�r4��h���{p�h���L�N��g]��6zb����R�z�����M�g���`�ihh��^�������/�����u���;n�m�����SQ����#�cvj�,����o�@���u����tR�c��%8"c�
����0���g���7t����d�2}�q��4�w}��7u�1�<�!3"GO��������������,���\w5���r�:����<=��Z8�_gP�2�����������+���Jl�3�������w�����M����/aR����<�FF�(�m��zE����YY��ow_�d'�zvt�Ke������'�~����W��\�e��J�~��������������NHl��9E5v��:�V�g�PAyS�{�y������<qFd|���45�\�^$�Yaa��5"[�9�������v�����������m��&.��nN���9��.��qunv�d���*w?��J�hPq�
&4NL
���2i�Jxc������e����v����Md������W9���?g�C�����'�<u�z��^A���E��O�is����q����kY��m���%��p���{�j�k�.y���T�M�Um�Z0qJd�]0���^�-,r���2W�h��i��g��'k"�<�>_U����E�`�i9t���x�R?���1tl��>�y�U�����.<+G�w����P4��T6��WKK��K������k��������5U�W�_�ei����z��l�=_<9�E�(��j"��&����lZ#��-��n=v9���&q9m�����D�'_=��y���n�l��l�KkoP�W�G���}�_���_u`���IDATd*#Ccb*��~���54��u 7�t�����O��yj��u�*�U�dJ��)����ai�.Gu������S���s�V?��t��G��������[�|~���$cS�Xe����+��+�x�"�=����������-���SC������7�F�@�����%m
>����$�N��Zo����{6ISs��q�5U���q:>K�����)�����{V�?���}"��7\���[���|��P/�>!����Ld�����?$�}!	w4K��zM�5��>�x��;q����#33#oy�?����'fO�)�����V_����}�W��6^kZ"���L�1���~Y���^�D~�D@&�9Kl��#�*�3���V�s�8�����y����y�Y��������C�	!�y��d�5���;��lX�U^�����z�Mfd�\T&�OH2��5��iW������[�
�hp�2�M��)��h��WB]����I�]yS���������y����y���Z�/�r��
&���;��������dZ����Z\�#��jw{��/��r|�����7����	��Y[���������j���`Z��������x����w���P���"k{�v���"h=�H\����Vs�����=m������
��z~����[��������JD��	o����cYk���e7��Y�m�_I����������^�Cis�hpI�*�P���yS�����R�iE������;2���r�V���O��yP?����D�m��Yz\�e��(����v�=t�?h/l����:�G�[�6���t���U-����nz��D�96�cq�$e:��5���t��`�:p��P����lY+�u}~���1�0��]Sg�pt��%:���<�|� ��Z|�!9��q�}[�1���W��\�e��J�~��A���hC���DdDR���=�C�l]-����cs��o����:N+�p?'Pk"C�7-G8�:�(o?�yP�����<G�?P����~��?a���������������h�q7��Z�a�-�uu�����������%9���q�-]��Ln��������]�5^���yjFC�Z;M07��5������G�:(3:��~��o��O�i(E�K�on���;e���	-l�M^������p�:	��������~�8d���Y:.�r�g�N�����*���rDr�-��Mx�����o�kZ��@yo�����P��O���-]�����=�����k�u����<�y&*M�i�>�gJ���Z����xs�UM�O�Id��������1�����#O��
Za�L��~i�
J�������g��u���	�%���E
��	�����#��'M��e�~k�:�7�M�l��n�|@-k����{�3������iWq�����>t���U��gL�4o?�y�v�����f�lj*oz@w?���e�|�-2�����8�)\�i�>w��Z���5:	m2:)���M�hhh�P�*	����P�u���YB���Z��k~��3�H��Q:�C���pG������_�g��jwI$�A��"t�w���$s����ZQ�Th��L2��G������3-�a��wl�E�q��������W�$/o���~�x�<Jjk��*�@��������cM��
�d}�_�u�G�U��p���P���_���Yzr����M���I9|�f�Xw��:�$��mo��\w�u,���c�����m=	��f-v��z�����hZP���cf�^�#����.���)�j����J������85X�pB����������z�+;o��/��D��QR��I���_����(�-�,}�a���}H�J�>��kW����L�|\����M$��5����l�HO�/�!�����on�o���S?5j����4��5��j����L
�Z�dhC����,���3�������e*.���qB���L8�����MK$��/Z0����_�{<B�`Q�.���b�����POB���u;�5�H<%������\s�
F�X�'��Y��q��g����7����������:�v�/��i��5[rK��#��q�8R�OL��1u��5��������=��*����Y��N~����Z��eA�k����T<*SN��L�`B[����Yz,���w���L�.�H[���L�^�#�(�z���w���y��&����Y"�Yj�o?�y�z���wI��_6�C�'E?a=^�O(������a�Vy���n"]�(#���21��xB�������O[��t����M`.�{�F�r�&�Y��H�����w|���2���&r��(V0���]\�C�=�}�B���4
������f���_j�DLR�HE����L������#|���C����+���5a�S�]�9����\��{��M��>������(�,����)��>S���~�&�k�@���%�������4���Q�n�q�l���D]�cr(&��#��{I��u�������+/��D"�dL�GNX���I2:fm����_�g�8_k�������_+1{�GKsyA��E�?-O���1jQ( ����R�k���i�s/���G����c���O{
&���<���sj�t��\����r���i9R��M �x�y�nMM�k��Dt�z�Zz���u\�e5�kl�=`���e�����\�~����W�y���h?�`������+6������Kt�����5v�bc�=�Vy�]����������7-���n���	�9g�����������\�C:>���}<�����b��5�d������xy�l���x=C�`���W1��A�0�-�HX����]����?��L�����^�����Z{���*'v����q�o:s����y�3_��XP�����7�0\������_&�{��fOi�����=�Vy��X��7�����M����"���b�����9&���9�&G$�vM�����ONvl��N���Y}6�����3h���g������6����&����Y�m�L$�N'%:vZbg%����G�#�~=�����9�:���:t��D��D.[+�i�H_�Ho������?`�:m�����D�W�.���s�|��?#���7�~�X�oXU{�K���(���}������������AB�7�="�����w����^�?L�Z��)��Hy�!h?���t������B�}�Yr���9(m�7�����uKt��������O����v����s�9sf��t
���{^N{��i9&��E't��P�D�=�&���(�,��p�45�'t	���y�����%9���q@=����L�1`]Ow���C����m��#5-���r��mLE	T�;�d�����&�������������e[�%��?�D"���L�1��D,m��r�G�:?b���8��o���kZ���'VX&Z��/xu�2�b���7�?�b�����9�aS��q?sT�c�vU7m�>=��ct��C����������x�[~���?�K�f�?����D�����G�u:�+��qzc	�B�`����h1Qi�O��.2�_��$	�Ld�qw2�=Py�������3&r4��%��'-��G������e/x�i9NO$���B����u�m����jU$.rj��9g��������h�$UI��Vo���i���������ID�/���I{��{�	��c�t_k��9��j�.1�m�v9t	����������%9��giR�����������C��N4��'O��zc>0 �P�D���[����tJ�
��/��Ld����t� ��H�����g�X��	]Dg�����e�V����2������s�
����.�paT��X��%i]$���`�v��!�������A�~�@!�<����#��w�D"�>�]$��]���OI|r0oY��~�l(�B�J�x�~y�K6��+����F	�}K��%9�3L��nz�l�s�����C���7��UDZ*<����"C�I���R7��O�u�'�v�l\m�
���<�K�U�����=�n?~rX����v[�r:��b�X����L*���K�Gn��d�-�T<b}=�L$������z�������:5������������&*n��2��s��z,���[��n�o�n��;w@�j	`~��h"����S\��e��IC�~8��YKt�V-�M`%;~�7d��O�����������]m"���	`���V�~�!�����D:��:�(�������i����W�{�z��j�#y��>v�Ds��z�\��O��e)�&;��6�t�L�Qb.O��t������v������?wL�����.~�gqn<�wY|�!���&���ER�h���#���f���1�X�k�~�����\?������7�^@�p�X��h��x^��V<�;�I�}�4�t�?�i?j��wE������U��Z�rD���%D�|7�y���S�����RJg�p/����a��]Z�3G�C�Q0�2MFD�&*�]�L�r2��t���������RD��{
&D��������}�8��}uLM���B���X�`�_Z{���*`?j�W$Q�`����-�&�&5-G��4�J�:��[<2bZ@}i�iX\�n�rL{�+��\�t:c/����T��/�~?;>l���?���6����e�
7I���f�Cc���}���+��!���*���|�.s�$o?=O��/t��s%H��%6�+�jjI��-���/��n{����cY:F����s��,���o��~�g��pG������_�e����:�t���$s��.���L�Uv��5��O:7��D���cS&(B�g��T:^���������L�4o����/gZ�Z����x�l������O����`?j��[�_iz�-tv�Z���	-���W��������x�.�i��3?�ww�f!V�~��u7����Mf���' ����Y�_���C>�<z>=��>���(w��S�_���	��������Z�]��u5��N�����2-���'���'M`�y�LPIWo^eZ�t�7}x�y�S}���Y�#�:�f�J�nB�vIS l���!�O�{��i������a�[�3`}O���L*#���4��2��u"��/Z0����_��������Y�|������6�>��P���~���}�:tH�@��F�r��I�H(T��@=:rtPn���&i�� >eg���L��0Q����q��}����Q�m�_Xn�~�a9}�V9z����W"t�����{�	�~����Y��@);v������zr��u���V�7����%6���l�������YID'�����|��V���)IZ����+m�g���s�H���"�%&���H�<cKk�
�s�MTY��m���e�4[�z���������j��\��a�ff22z�1��{�H���D���F$2|�D"]�^��ZS��L�;f"���m�\5�c���9"�{�mv[u���33D���a�\�z����6�b��Kj��%��l�K��'dr����H��5y���Ky4-���������������������;�h5�0?���`k�44.��lQ�_��dn��`�jiw���DdLb�q!k�b��dR�EEn}@�&�R=�����o�hn��~Y.�7M���hX~�<�����xH��;?3�H�s�������S;m"��?�
y�[o0P_�#?�G��Y&	4���+\8��k�I��������{r��RM�q?{�D�<[��)�7?>5&������5���y\��*�P�
'l:�DCC���X�`��]j���h�~���s@=3��x :q���]��{3�I�7��������/q�X0�@%i��Q�uV?
&`��v�����OJ2^xME-��Y��^��kL�?��g�9��x��I���"��Y5/�:�Qk��`Bs$KY0��|��i9�K3#��&���H����T�Mff��������"L�=�J�L�z�u����&.�X���bI��3M`���P.��>����~������p��I�����T3M���0?�#����:5j?.����i����M���:�B���d��9$�$G�vl��>s�����]�c_w��+M����T3M��3G��h�����K�L�����Z��;k���J[��&���8hi�������"�vU�X*Y0�L@�s�(�@M ����M���,��o���+X8�zWF:�4{�E�B��Ld���}N������	`����g�nY��y�
�w��������x�l��[nx)-e����S�������B��p���Zo��Z��	U�p�}UP���&�-6���19*=�E@��h5�0?�u��]�Q4,?r��P4�B�V��L����\��Rf�u�Ic�g��
I��0�
[��C�Yc?��Q������=�k*^Hp)2��D�r�-U�������I'\�r44H{o�`��]01�^�r|����&\�%9(��E�9$����O9c�����V����&�r�hX~�<��YyE��8�~a��(��Jt���S�n5���M$�I53i�HLdx�4���W���.-
o��r����N$Q��
+T��e�,e���������M��h5IS�QR��$��YKrh�D��5U�`BQ4T?w������p���J�~L�Kb���=j_���tf�l���s������tz�P�j	�t����v���;�g�q����Q���������l[��`�"�?�+�hn�l���5:�H��k�����?�7-��q�������@h����){�Lh��,�X����F��e:%��v��>z&��bL�=�J��4����������mh��i�K�kn�sA[C�_�1gy�L:-���u��}�:�7-~�?(-�$��/�p�ZW���tm��g����z=_*:a�_��������n�=+����gZ��>��Vx���U&0�|��jW�����y��rZ��3�bB������.�O�S0��
b��2U%�SU+����c������Us�]}!��LFQID'�Q�;^�]^��g����9��G�����sZ���/�wf&����I's���M�Z'��aI���^���M��0�l�T\���6��?�*��==-�y��k
w��]�`����9a"������?<["���m]�Cg����gsKu�{����N��&�24�,�p������2�����\w������E~���^�<��`�����#,���k�R,��5v6"����4�]�[8a��-~�\6;*��9����P4��@X��81"���M�SY�%��'M�����h�OJ&�4{D��U�u��DXjM���0?�,�P3��D�NK�U8�������jn��`��E	��AIL���F�3�Y�0#�^��c�}�KA�V2�7�����66�YUF�����s��	,���������������f"G��G|>���N'g-����W�o��Y&ZZKU4�����Y����:���pg�����h�~��s���}�Q�r�L(��B*����OL`�iC�k�4����B
����Q���+�������L�H�s�%L+]d�����Y����=O�8�����	-�j_�EZ��%��co��}z,K���Z���s��f��ufN46���x��K%�"7(F_M�i�
=��Cr��A�����jZw��������;��n0����`ez�{�,?;>b�uI�`��s��&�^\����=����n`�1��9�|�<����LY3��bcg���pR�
v�CS WTQL*6)��3�k�L��>�\g�����1�V��41&rb��Z�i��/��8�������z�(��D*��sO�Hd������5��Y��&&c�L�����
Jd"i���fN4|n������#lo��T3M���;�h�J���W|�A���NKt	4y�k�.�8p��Y����|��Md�1m[-��N���Yo���H��g�,>_e���&��y�#���bMdE��H26i"M��HS��.z�Kt������\����v�5QeP4����x�D���=m"����J�>+2l~��=�����'�1o��}rj��F�]��S��az�����e�����o��^JKQ41rj�z����@K��S3Vlf������������O;3�4�{]����	���s,�Q��<�w��y���i9|������{�X�N+`6rV2-p��M�w$6��O$"������d|r�&�j��9�r
!�������=��D����{fw��X^3�N����Jm�h�b��F$����	KS�I����B�,=�����X�>O��/t��S�_���	������z�*�r���5�J����9�
u�������c������$+1=lw��L�^�m�]�r��~=�N�[�O�����k�aZ�����i9���%���1�������A&.��*���1?-�hh�L�PX�P��?dN����	��.�a
'�<����M�O�~��&��N��SE����n�kz.����.�q������i���w��n���9�"�5 ����2�S�=!����M�����������l��U����n��&�~wR"O�q���0&2�G�����=y�zk\��Y&�-�Vw��#���&����U��pj�)|��l�Dc��Q�>z'l'�3(���*t��A�r,���������L��js�h"��T��\{z\�e]�����r�IK�Fih�_�?��Jt������������W
�>	�l2P����.i_��&r�'E?a=^�_�Ci����Z�����m3��J��	-���36����T�(����p
�h�
�����D���cZ�����2��{N���{�5-G|r�h�����n�y�5����PM����n�?h�d���|�����"G��s�K�)/5Qi�O�������������Z��5Q���3#����.\���?(�fO�%���U0�������G�W�y�����/��@�����R�w�q����l��w�n��D���D�NKl��$"c�NF�G�u���q�o�6T9��k�V��1�����6���_(�'�V����O����=w�i9"#g�l�t_d��������.N��.y�55��hR���%Ht�������s�d7����3K�y�|z�B�7��)���MT!���w�6���������z�K�!;��5�C���M������{I��u��B�@5���9.��������4������"#'�q@�;�����o�����=m"�"���UN����Y�j�������W��)��8��L���^C��M���]L�Jk����TF��E%O�=���:2���[�'�OH2��5��z<�$�{F
=��W�nMT��w��s�i-����s���%
��|�_$�]��D�i?�����Pm���N�M��-���9�����z���:t��D��D.[+�i�H_�Ho������?�Zi��5.:�#��7���e��p�O���){����}�5�V*�h
��{]X�[����M&er(&��#���n�_���,
'B�D�9x��i9��:��{�&�k���yST�zk�M�+33#oy�?�#��7{
1	����[��{��A�@5I�&$3�H����\�K�46f���c�x�^]��}����l5����Y'��|��jS��Q���v�����8��}uL-(V0���g��\��p�������q�x
'�B�D��zM��~CC�=��{��k��_�	���uO���������������	���@K���nw�B�����O�<�t���i92��i�����dRysNx��$2�_��,�Q�`"��:�^��=�������/'��������1�n��	5v6"�H�Y�H_T�'��������p�\(��2���7-g�OM�/��d�&�s��7�)*,��:*����Md�����{������@k���qK��x�����9�D��(&��/����c�R���'J�j�L&%��GL$��K������<������<uv�D�@{�����M�n��S��j�LdJLD��v!�j���E5��������
���_l���S�
'�y@����������C���L���w�6��4(���s&�ST����ngi"��`(���-����'M$���'�@�D��{�����)���l��������25���Dv={�|�wr�7�.
�����c�>|�D��;�������(��0?�>5$�s�7M$��>e�~f�I�z*�\w	u����iIF'$:q�D�Z{�H�/}�JH'�2=r�D"q���-�&�Wt����|�\������������y�w$���D����#�����,kjI�g����JD%2|�~���v�5�&Z:O�{T>��w�H�k]�z,o	
�������U0���e����V��Fi�	H�e�kh|:)��q�$s��-���)�p�h��������J�R2z���B����dK�5&�����h��x�.�����T��Z��3f�|�jH ��],�`���������nn��`k��.%65(�i�����Y���k��`�zzz�m�P4��D��0r��=5!����L�8E33�|�D9�����Y�C%c�]8�TE�{�3e��\!��OT��?f"�
�DVu���c��	,��o��M�k����;.���Y���4�&���Ge��S&�nc���7�h��9*���D�V4������/L�����&�<���,������s���`{����U�h���0/���M���Y���Db9s,�QE���m�d�^�{z7��<Y���<��,������	��T^���O��y�6r�Il��i��:�,�P:�D�:��2-��4�,�3H�c:����s�Zqf4��Z��L��@����[����]0�R��r,�6��	���_�{���_J���C�D���^���kn�J�y�;1���+W	������@u{��k�B��#�`��R�:�B����II�&��E��9f�pf����_�z�B�s)�~�@��~���LP��~�.����D����{�w���5Z'����/�Yfc.�~�X�>��u�;m�&�	����t��3V�T���	�����r�T8�]h�����B��{_i����{*���JD����O
����MW�{5XO0��5��v���R Z���T2{K�~a��]�������5[z�}�
0~��r��o1�Hs��@�Hg�����.���f�e����s��LT;�N�;?�]��[��$J�O����i����d��N-���=*��;Lt��sxy��(��W.���}�}���X���y\|�H��y��t_��7!P
	����%��0�m�m�����H��g�46O���S�O�H����(���L��F��9nG��-w~��E��t��������k�;�u����S1ID��c9:�D����~�K�<�����m��7�K��?,��j"GO�H��'.l�
E����&���WV?�]&�-:�K���&�~��$�R�"65"���&����JBZ�����9"�{�m&�|q��\.<5a"�����:�MT\t<!�C��_VmiO��%�M��������L��s��^�<hZ����c�p(����T���`���l4-����N^[O����	�s!���
}����[�HD'�[�Y�qv����s�..2�V���Y���u�l"�H�y���xav������	��S���6�.IQ8������t����^s��:4sg&�i�*��O�W�`@������.j�^������Ao�����Ncy���.��V�{>uP������Q�]������7�4�3M����vq�	#����������%>9$�T��il�I�u�uN�.g�:>u^��\�b|��5v���2��5��d2��|��h�����f!:��ld1�V��?�����8Q��������}T����Z�#����c&��y��r�/-�2�=������D�D"�!	��M4[|:)��rE'��fi_����a�	��1�d�N>���4I���o�L6�w���������n����E���L���m:���`���K�{@����}�CeL(����c�<z>=o��|��f�,d[h��t�������	�\g�:4�����k�`����YI��W�EF�g��e�������]QH"�������X�b��*�wN����n�q����a�������H+�sS����p�G��������3���]�����������D��7~����W=�DX*�4��B��8r��xg�h��`,T|zH�S�����ug�P����&/���j�F��|����IG%�����m��9�m"�����){��@k�Zz����%P�GN���&P]f2)�}_��������V���i"+��#�f��,��J|b�ng�����Z�8�����gC�����)�(�x�^�#6�_L���pg����i�~��EU�;��;	0�X�����|]���&�rLM�EXY(��
M`���(��0?�.�P���u��4xf��~�D"���`[�4Z���T|J����N�>�k���W�1cK'c�l3{��	�:=~�)�Hg2����_rfh������M��W�����������RM���IDR&�[s�I:��MTYM���;`y�*�kr���|9�n
���w�,�l�^~r@�!�`%�Bw��JDFM�(R0�t�w�GFL����D������]�q�|�w��K�_f�����}uLv|-�-�(�bL�=�O����
x�vPs��Ph�|��(��.�C�����X�4��B��8r��,�L��,:���%9��k�v)������5��g���4f�j��9.�{������OD�}=�!�r}�������-���|��41c����3a�E���Jd�g���6�~���?��kjn�pG~�[���������r���&���LUJ/��ki���^��w[x��;7`���x�6�����23s�`B56�W����N��?���(��������F��;_bo��}+�`b���������`�y��54��������
���_[���Tb��3W���Q4Qe
���I����Av��w��v�m��C���z��Vr��]����L�4o�t��k����bo�Hg�pk�7JGH��B�h�`{�~�X��q�^5��N5���Bi� �=���f�C��(�|X)�y�>���dn��R����f�,����@K�e7t������/vy�{V�Bkwzi���;�������	��������A�~��!��C���#G��;�j"���
��W�wfz���'�C�l_c�K�M��Dt�n������nWZ:���&���Y�����7uj�)������'e�<G��o�(1�����&�v���O~���b�+m}�������Tw�����]������c�bMK���~��0�%	�<�&���y���k��&��$7�H�s�4ZM4[*>%���&��:$��o���h@-Ig����+jm�
J���D�E�29�[N�o����}&�&P�H @�Q4,?r��,U�D&����c&���h�+X8��OJl��d���Jg���&Ej��3G�}��D"]���6���d,%��#&y����k���s���eilj�`�*���I{&	{	���]���G���L���*��Z���j�r���*���{p�h�yjwKSs�D�DtBb�����Qc7�����%56���D"�x��Js���,���&5)���dtl�6�M|����W�O�:O%7����l^}�i��&�����S��~Y��PE����<���%:q~Q�t2�<YA�ih��_�M�g�9��v{��i9&��E't��P�D�=�&���(���C�l]-����cs��903�<�m��������D"�dF��Eeb0*���$c)�Qc��NeLO��m��-��P4�%h��%��N��k���S��N�Qc�����i`��w�,�6m7�C����������^�Cis�wfv��������C��N4��'O�Hd``@B����c�9|����'wP�_7P9`~G��M=uj�~\_;pT��y��:��?�j��R�)���6��uW�����j���e}�����boP�R��|��~W~z��fOqW����5�&o���E�	$��(��9`ez���,O������k�v)�����N��+6��g?�
�
���LF���7���O�������=�������}W_.|����L�`6w��W	��Ng.L����
���~v|�:���>@)�����&T�VGX/��6���
J���~��]D��?��?5G�ez���i9|��i�������(��O�E����M$�h��u-��*$-�����5�Z��g}����@)MP��7�2-G:7��������'���Z��L��j���y��E���k�+6��H$���Vi�~Wl�������5-����
Ld�q����P4�<\�9W4��NH*>e�������js�iJ�0~FF's��U0������<PE��k�\kZ����$�Nh�D�:���]cZ�RZ����hhd��G���u�K����&I��;-������I:�5�X�3��,w��nJ	Z��m��D��i����MK���:��f�h�yz�K�!;���Kp�&����	���$���:P���s��DFy����X�D����&X���h���u&*M�i��<��_4-����$c�'t�w��/�&X�w��>��GN����~C��O��g��/�\�*���==mGD&�6}�X����\�J{<�B������X9c"Gs�]���%���~l�9ff�������_����r9�I���Ik�G������M�Q4�<<qbD���a�4���\'��5���)�P������Y��t����&����Q�����+�_g����������J�<��75-G��O��&��������i����C���GDf��3�y�����O�@9(��LG��;g"gI���,-��~Y�<~�>�<_=��y����`���H��/��Ai����~=������P4@>��?�[����<?a��46�L�4w�����|�>����O����1Y���F��I{_H��jk�5��z<K���p�DPE,��0��f����s���_2-G[O@-~���z�����hZPE�-!�t2nZ���WdA~@�c�7-gI�bYz\�e��@!MP�����r��/�
�f�H&5����6�������Kg���Y����Js�;~�	�<PE�i��>����&ID'$�2Qaz\�e]wU�}@i��=fZ��@ymz�y�nM0��s�i9����,R8�1���k^t�iJ���i9R��i����=�Q4�<l��w�n��D���D�NKl��$"c�NF�G�#���u<K�]���D�R|�>���r�$��-������<PE���_���m���Gl��L����Kr(�������*���5)��\!Z!z\�e��@!M������'J�~�0?{��i9&��E't��P�D�=�&���(�`�t���}�����_!Wl�_v���=���~3L�mX�U^����H$������LF%:��d,e?j������)���}��=��&��o�������_)?�f������~����]�'`!n�q�l��?��.�19������$���:�B���5���}�#�rn����8Q����P�&�x:s����y�3_&��f�Cc�����&�E������7��.�����4��X��G�u��U[MO(E���'��~�����K����q��&�h�tR>�������>�m�8o�84��z\�i(EV������o�����=�i?���`.MX�>���%��?a"G��/����fm����������DPEV�?���#_6�HS�Q���H������%dm��q���}<�����=J�h����'���Z��L��j���y��EV�'�=jZ"��f�
Ld�q����P4`��0~FF'/��)�(������@1MXqZ����hhl0-��&�8�@���;M$���L��d<mZb�_���D0EV�8x�LF�L$M�D��#c	�=^��P4�*L^�J2V�pB��q��&T�T<#�������DB�����~=�3c�8�&�����+


����C|��{����$r�������o������R���^Gt")Sb2im����������=^�����]b�}�v9t��6Q�����)]U������K|�A����
7�`���F����&�P(d"�B���C>l���T��
9�|�<�tR�����������)����5��_�mi������0��h�`��]e'���q+9�����������_�����{�9��X��q�G��r1�D�^�g�s�=�U�[o����y�����#w�qG�]:E����g%�������c�	,5r���(.������e]�&	�y��|��EU�{�_�w]�������}�L4[���\�`.PyM`������T�;w��U�������D�@��I(}^���wd,9�R�h�
�4�Y����&%tz�rx���
`>�y�EU�}w����M��*q'�|��,5�&��N)��s�N��4z����Q��8���`9P4�N9��fff�^��F�P
EU���N)�wb�{���������������w�,�������,�&��N+�I�]�v�m��f���:��_�	��H��v�����~��,�&��;Q�	����4�PI�;.4��w��9�W:���g����P
�f/��}�v9t��6���^��s�}����]�����������[M�p�<��qq���eK D�Q9y���D���$�jC(2���c�9|����'wP�_w�#�Q9�|����o��������O��"�.�M}W��g�Rz/3{T���yP4QEr�����jf��O�`���J%&��@���P��YOO��-�&�T�yG� �����|���3Qq�|�k�.��T���y�<G�D�N��*�.�������)+%����<�������}���	���?����*�o�>{��
�Jdi�r��*�<��`��y��a������l�K[oP����G��������<G��;-�����������	�,�����9����m'�������L$��o����Z��$T|:)��q�$3f�����kY���DV���yP4Q'��|�g=�j����@X�_,�5M����m��n����b"���P���,-�?5�����2��=�����s�$�[��<���S�jF��:;��i9Kr�*�Pz��T�{<B�D���s�i���R��������y��t&-��0��?�3�������>C�D��i#u[l���Y�\�<�u��c��h
�������<�F�Dihh����M/������*� y�9���j�r���*���{p�h�������d�~)��4t}�bH��F���}�q��&I��[f��O��y��&�����M�Q��w�u�i9���y����J����<�������*��M&%>�4Qaz\�e��@!MT�[o���z�?W�{���Q(!�wfx����-9���W��cj8^�p"I���^���L
k��ml��]:�M�`z���{�iR@��p'
�S����;.�	��9���X
�hTN�<i"���	�B&,��;����&���A�~����Ga�<D�z���w?f"G��/��O����g�%9�3L��=��r���M9��EU�{�_�b��b�����-�	�<�&���y�F��q�O�<�{�?�m������s@>w���9�������3���u:�JG��h��Q�G��rQ4Q�4�o�>���{�A�d��D��+��������y�]j�Co��<��/���/7{�����7,�`^X�5��*��X�X~�<�Kg�r��c���j�5��^��s�jZ(�u�6
&\�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]j��ml��]:�MT��zH<h�����[M�<��{�i9v��)7�p��V�h4*'O�4������B!b��r��aUO��Z�n8�y�#����P4Q����+>����gf��O>�����{��w2b��@���h���G>rPy���sT��&��wt444����v��O�T9�R�h������{������n^��$X��y�(��B�����O�����S�i�`��}����M�������.�Xr��F�D:x��i9n����4��pOO�M���I���R��,5�&�����M��������Y���y.9�R�hy��(�{�O��+9@1MT!������M���{����(��;w�4-�wM�r��,5�&�����|��z���UZ�����`9P4Qe�w5�/���	�6�i#�k�^�J��r��@�D�585I�w�^ihh��n�-o��k��_�	�Q���Y���i4��`�P4Qe�w5���&	��h�d�&J����?XN�<����f��}�v9t��6��i"���a���&r��8p�����������g���]�����OjIE�Q9y���D���$�jC(2���c�9|����'wP�_w�#�Q9�`9sMTo�����e�we��)	�
���X		��%[�Z�����R�hK��Ga�<����y�<G�N59����]�����#JMYY���H��H$��<�#�����*�	�jR�{����;#����N�Z�	�p8l"P-�y�F�����<G����I�:��2U�JX��M���Z���{*���JD���r�<(���58k9���&P
�y���;`y�:u��A���<�A�lz�E����r�`����B�D���s�i9v��mZ�w)	�J"���&�����ki���C������i9�k{*oB���{�]J"�'r��B�D�����;t��k��[������&�]�����[o5-����,�&��=��cZ�r����5����N,h���~��	��`�P4QE��I��s%��7P���;����:%f)��LS	��`�P4QEt:I��
��z��wJK��n���������;�%�T�� �X.
�6�������C�����A����N���?�_�E��}�LT�&#�I�=w����Z#P�v��!�6Q�����9��p�(��R����	�r��(�D(���&�&��y�;w��UJ��������o>�Hj2`ff&�.�B������r���L5 ��������J����_�sV.f��r#�X��E� �&@-r�X��%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h�%�&@]�h��k���������C�D����������'>zNb��M�2�VK���D�m��r��aUO��Z�nT9�R"�@uq�(��sG��-w~�D+��w�(����PK����<|�kM�2\��c��L�(�@5#�XJ�<�.���s��D��KM O�c��toX�M�s1�{�����0k���j���bcW@���t���%=����A�@a�<�g�������#�X*M O��/>hI7}����~�Z�zz��I��X~_����t���%w�u�i�r�#������#�X*M�n=�����04����c���n3{+���{/��j9��������`)Q4��v)�J��E��lH�����$P9���y��F���(�@][�t�L�XN$y0r�jD��(�@���g�i-|��z���2���$P9@5#�X.M���q���X�t��7n��s�i�x��?��@)�<���`�Q4�����b��Uz����s�i�84q�k������-;p���9@5!�X	(�@]��{�i��J�4��z�����o
ukhh��t���Z������:g��w4}��=����\_G�������>����w>��J�����n��z��7��*M����D@9rP)������-��B�c��F��P4��t�����C����}a��;.�o6�\���PC��q�p�^8����.uN=�=�|���(��(������X�y��w�u��`����j>�����o���*L��������9rP)�<���	���LW��ny���.�K���.��]T�����/e�_G����\���z�b�k��R�&��3��g���/�8�N���9�r�zA���B����i*��}C���7��M� ��_�����m���s,��������z�M����<���~
�[�}���F2���}]+������|�� q�� �A�*���P4�����*������R�(z�����u(���za����,�}^����z/��{���&�_�����C�{�l�gj7����~o�\2]�7��}��{�L�����P��y�E�k������V������[/���}�B_��M�\��s�����/�~
���\_�7��������l+��5o�S4�
T���������P��y�E�k����R����'����pw�-�5z��(�"\�w�x����/���r9��]���;@����R��z��}����|�\�y���J!��uM��y/�KM-v)�T*�S@/����E�>+'�PI�7��I��sW�o��T���r��(�&@����r@�P�(�@�s_����^�/�=}>}��w��9+w}-�Dhhh����Z�������+b>U��7�V���M���T
9�K�=79�����Q4��W�t��:Me1z��M����i@+r���V��|�9�\��B%y�[���~�l�~�P
��7E��b!�qi�y@9@-�hu���*��8�R�J��l�@��M\������$�����lBA����	�4���R��4�E�P)�<���	�Rjj1�]
�R�����+Y�_���,')��_��-�c�yC�o`5�P��0����o
k������] �����h$�f��E��I�$�F�=6�
�E��x�|	�e���<�/6,ZB�Y��998����I�� �� o�V~�E���`;8�E���l���F���wW[5�����������yJ���U���=���������<�1��	��lh�N
S���m��`���Te�@@d��%X�����Z0�Y"���s��]�mJ�OPZ!��9b@��y0�$M��fC����Ovl~;!N�k���fS&����y-�P4�+/����9(�V��z�c�411�N�`PI��L���:5Le������F���Q(D !�I?@g�����^��S�<D�& Soh���u'�c��5_�����TT4R��?�����W��&�1S;�`���M	s
J@��<�F��Bb"I��7�X�����8��M��G�4�vO�k��(WZy�!0��?�@�bmJ���b�b�JzI�c��<f�`I���zC���';�|h�D?N���z6;v��JU�����)�k6�e1��{��C��C��`P����6e!������yb�#����49������|��������1tc+��P�v�������m4i%8��X�����=CkA�z�1��r�-��*����W�r��N�hL��=bI�S�z�����N��$:N�kS��W"D'��^���2!�U�~l���mt�C��D�_�fQ,��g��������gq|+*^-� �q�����y0H$M@A��^'�c��`7W!�:tcm���{#��5�����g�|N�~S����hS`.�<���1��	f�:;��N������;{�B�v��,��G=�xnjj������j��X.N�m?����I{����V�!+c��~�}5��@9M��J�'��t�����1��q�������M��G�7WiE7o�����EJ���L���������?K/�a}V`�<��/�/��g���5��'i�u��j@��-[�����Z����}�Yb�����|��H@)I�J��9J��g�W��d��+*�g������'����l�eil��Y
�s{����$��%;�4�%iD����s�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)
MOSQ��ys��o_)��G��c��e��a���i��UY
�Ar���49=-&#+6���	���-[�����Z����}�Yb�����|�@�D�:t(}�#�j�������a���� 9���J�������p��?�V�����I����y�Kb�_����JI�PJn�Qr��*��_��.]��z���3��'��j�9T���;�R���[��7����)Uy�)��d�9=��7�*��i�����;���7�����n�J��o�~�F����z/���{t��Q�w��������N��=�L��3�<Z#�1?b��y�����(�|�@�D�W^ye�j�111�}����?�h�w�������l�����NU��b���RZ�,����S)��HV��O����7mw����t��"N&n���J��m_�D���o��\��[oM;v�����Pt��Z����������a7/��f��P;����]���"I�31���h�����y\H�C�����&;p{��8`F#;['.��>�0_s
t�w���z��]\4��o�\��e��|��i���m���c�}�:1���Lb���b4#i�T�0��l��x�ME�iD`��C�]��vK-xP�o��}���Y� B��8�-����Kkc���.�`����</1�!�A+$M@����={�T����hm������|F*���9��'��V�n���?��6�W���������>�~��v|Nt�����g=�{�B3s����5���O���"�1�����%�A+$M@�Yh�1it��x.D����_����0O���8,^�D7
^�~*��A8��J�o�r��^��}���&�E���"�q�������G�H������&��d?0W�w��J���Z������'���~��AM�$'��70?��n�8@�`!�y�'����G�H��9�����)UE���V�����ko��-+U�:#�s�������<� �Q.C�S��O�7oN����"%r������|$��t��W��������G�j)���oO6l�j��A�����U�D�����@A��Vah(�����CX���'�W��/_��R����V,�*=r�TJ�=�U��������-��]���|{'���C��5�
��g-�H���������|��9��_���=�j��V4��[O��h������&�`K�u���xO��������5�������Z�����s�����v_���W�����(�Q�����-[�����g���������<.4[�T{��~���yb�����y\�?��V�s��,�y\����L���s�����v_���W�/�A#�������B��I{';h��;X��Xh�@�j���|O~�+��'�5���y���F��o�>��h��k��P|�V�E��h��F���F��8��'����)��y��c�o��x�1�b��!�^���,�>�i�w�������g/��B��,I�31���>�F�`&1����?bb��h��F��O1������}J1�|���9Hq����`��M^d��G~���A������������	^~*n��'�qR��l��������:�O��G��<�N�k�����E|�y��C�>���>[|�8����O�Np�����i��8�{,��o��}��~ne��]�������*���}`:K��� �1���y����Gk�<��`1�4�@*����7�����h��o���`��E��I�������8Y����v�Z=1
����U�6��_���x5�� � Dq����.��z�@��Sq�����m��Ym�z����[A��o&��C�1������S���gm���������;���������w�Z��y���_�����2��:���S+�<���O�'b��� �Q��9��GU7���<�G����Cc��r��*C������)��`�j�c�
E��/4[y}�2��*C7��F'E����qq�?)�m���������mgX��	^����Y��G�{k��F�_������M��|hg���i�N�����YS������gh��7Z�[}���l���o��������7��Y����~&�q^��S=������<.�J�#�!�!�1��V�3���-��>e�s6�����9(�8�����*f�������3^��Zq�<(�n�?���''���8���N�� �?k#��S����?o+�Q�3�_�M����	n�Nh��i�s5�^��[��Nkv����8���x�����w���������
�������gl�_��������V�_'�s���u�h�����O���k��Lm�G��*�#��1��n�M�y��$M0��Z���t�@�n5�,>�hj'�qR�����'E�[��ke����)~����j�5Z'\q���IW+�'��>W��3�N����v(��;�o�x��^����SOq_��;(����>�{��������o"�u+��`(^5�H+���n���`!�y�T�<bb�j��]���V��
b�I�+��F��A����7&���%��ME����A����8u��).�.�H���J��d�m�\�Q<�k����j��'�����!N���sU������'��~��4�m��w*���;vd��U��K ._���N�M�'HV��l����N������A��BbU�~�<�S�������)�~=zO�-@�g�3��
)�L4,����Z<���@�d�'B���'��y���n�A������FW���j�d$^'����6�x��1[�|+Z�\���~����Izm1�0�r��L+�����D�V[��y���!��B��I��J��;�<�f���%i��V< 7�,�3���)v�"�Q�����[����S;'��5[��l����&RW���)��V�%���V<�l��5��g�$*�G�{)~'�}��7�m��E��K+�=�c�S�;jt���>e��4������79��7�~=���f�4����@9u����1��L�m�y�'���B�n����I���&i���o��Yn��pw:��������B�?�x������^0#��Z��oG��'d��?aj���/��n��AU�������U���.)���FS��bU����Z�>��@9-t��<��\b������4���7����F��Y��%k��Z�����k�R���{/������nl�Y`$���|��)��v{0w��o�}����b<?���6�<�c1�MC�I�`��6�Pd��t+;��5)
`�-��g�������S��P<���9��f�����'���e���L�t�o:����og*��(1������S��J���j���N��y��	J"�o��'���c�h,hE���NY�������C~��E�^�M^�D�&�9�����g1�P�_1�>����s�����t�F���V��~e�+OzC��� �1��G��G�c�L��y�G�c�H��
-���BCCC��[n�d���|8��C'��8I��}{e�w��L�'t��)~��_?�~~�4�B!���v��&�'���u��?Y��f�A��~�_�zoe���X'��98T<���h��<hW'�1���<(�X<�<��	J!���j��v�*�����Y�����'��v$�v��n:q������P�[^�=�s�R<����g�d1���To��_����W�}�L����Il|W1������t18�N������v�`��y��<.$�Q%��9b�;�&(�|�
K��;['�x�/vB�w (���?���VO��O'���e����y���O�ft�C��{6���N�O�G��V�}���D��l��,~����r���)tq��l��7]|��;��J��ek�5������=�H����>�y0�m�<f����>�X8b�O���k���T=��u���8x��8�����`�u�������?���/�<�+>o�d;����v�;���Mq�x����>������k���N{�l'��d��Y�����P<y��gOVjl���|1���o8>O�w����9�������1���L�y�<.���<�G�c��<h��	J%��7��d�;<�Y�Yp�|^+����o�S<a^��'r�?�iG�De.��b0��	X��x��^;y��r���{��{�)���v�N��.b��N�k��7��f��P�����N����L,��b�}��r������i�\N�c&1���<������H��Te1��P�r���(�1��?�b�(��V���m��O�+���O�
�u�����F'i�:���q�#�A~�~*Z��oW��T�7��;�j��|�o#��M�������~�����o:~/�~���S���V�s������O�<1���LbUb�%��Yb�#i�R�wPo7�^�� S��
R,_��@9t�����j������x�N�?��2���z���@G�������N�Nl�}�F'���'X��z���>D��i��D�~���n�.�����V�r����2��:C��� �q�z�_����{���<�n����2�����	f8s�L�������K��\����v�~Q�V,'xP������������i���������3?����	O��JQ��X����v��N������	u�\���������������M����p��u�_9��K�����^�hM�}�n�M�L��=��?b��y\H�C��h��1�������y���o��(R"�J��G�������=m��!�u^�����\���m�A���~u��������g����7.�X�mY�{z���n
�b��8����w�h;���v��6�8��l��\�������k��}����,~'��]�u�5S�{jwtb���{)���_u1�c���-[�����Z����}�Yb�o�b{!��N���F����O��yW+�<�+�_��}��w���+�N��V��G+:���w����\�Y3���v�A'��l���xM1����H�(�2X8e ���_m����������2��$I�31zI�cp�y5b�-;p{��c���4��~@��y�@����G�i���=��;����+W�U�Ve5������������F�'��� p��`2��L��^�,b������$M0g�!�n����k��J(�����[n����	�J��K������`��j�Y�;w����r���;����k�����G�H�`�bx�����	�W\���5D%0h�<���<�I������{��/?D�{��J��G���$M0/�m�g��J���m������+.��=&CT�L��E�������(l��9���/�i��-i���Y�b��������4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJC��T6o�����EJ���'�����j��%���KV�g5�3���L���+�W&�B[�lI����j�;���Mg�y�Kb�_��I%w�������;�-��m��M�����G�I����d���}?�{��+_���<I�31zI��K>v��@)I�JI�3\t�����k{:�k�����;���3


���o�^���^����~=��u�g��	�������z;�k����z�S���x�P�[�N~�y�|���^��~i��;���A�~bs#�A?��w� �1���z�S���x�P�[�N~�y�����#��#i��/MK���t���g�����[������9�����L��\;U��NG�f��cG��S�����~�7�v��<���W�O(�&`d���N}��,:�iwj�hCkmfQ����-u����|����;����������y�O�� �s#����<�K��G��Q4�������'����)�S-�q���V�N��a���L��o�w�����k��N���j����<���ukVJ��;��J@�� �ye&������	��b#�={�������D����t���O���":y�
���]y�S,>���e*�6�����D;X���e��x����g��� �����"��h��'�������e*�6���<��r�4s�c>S4:=q"V�6����*��A�X��������+.|���9,>���;��y�����;q����!���{����c;�)����y����!����������y0H�<`q�����H��n����<`�I��6���
��A3�L����O����w��{���k�����v'�W�WU��f�g���b{NV�|�g���Y	�7b1,b,1(I��b��J�'�F��q��m�r�D�kM�u�����QRw#�<�v�8�\����W_fwq��"�$��I��A$��C��� �� i���2-vb��\~(�N���N'(��o�����CA��N���v'pt�k[Xo='��C�����Ob"1b,$1|�&H��za*v\f�����be���Yi�?[������$��n��o����/�?��������}�~~~+���=�������7��������Fb���!�-n{�m�3�g��L��U�{kG����w��x���|N��)~�V^c���Y��8�s��D5>_<����������}�\�}��=�����;�����\����H�s�-�������>@�kb�ybbybP^��U�{kG����w��xb�����^<6��yLs=�5��|�j���vg�s������i*
�7oN����"%r������w�Q_u��i�����g&N���?��R���lK7nZ���.�����U�u�cR�E��o��f�DC_�0Fg��!���#�k����f�W|�f�~m�u���'����]Po{�Yjw8�F������wS��^�nt�}�v��b�}3xx������LVKi���id��������#�Nd����c��n���Ym�����#��V��s�n�S<���N'u��_O�������v���8o�����?;�>u��,.[�lI����j�;���Mg�y����^���b��=1��v���� �Q_�v�b��y�������e`q���4���F+/:���5���/��F�Y���U�^��;�e�����vNBt���O�|�Ut�����tC�����U���X6�iU��f��Y�s�����.�^�;y���q����S�mw�C��������cz����v�*�X�Q[����Nt�}���v^h�����v��Y?P�C���G}bU��s+��+I��0���g*?�Vt���=�D#[l(�Q�y�|7N�:��l$��c*v�#��\�,���;<���z������vM|���6�'��:���Om��w�E�����oj���(n���a^~����sBQZh��N������a.��BK�q��k��+n�����o��6c^^,?[?���s�|����F}�|�H��3���NL���vc1X��<�[�cI7��yb������#i��U<Q*����o�q�-3�^���x���6��Ng'�����xO�1�f���y�\^��2������n��)v���c��}Y�l�U��j�%/�Wm���l�����k���b�A�*W����S�F7��;��-�����}'����-����h��c^<��,P����W�/�]/��b3�y4V�m��{���U�7���hUq����f1���_�+b��y�y�K���`G�L���F4/��F9������t2��?�A����>f!�+vRZ�?��\�)v���h�~j���~7�(���s��o�������V>g=����~_�:������8a����������n��������^[
N������V�������V�>����g�?e1���<��r�t��
1�u��/��Xq�b0X$M0��6,�����D#��b���
p�F'NH����^+~�V;k�L������k�o5����l�e�����'#�\y���B7~�0�z���O�����Z�v��n������W��Xg��k+��Vs}��}X�j�\Tp������G��T�l���N�y�!����v�&�F�������hN���	Z��Y�z�����Q�5���f�gX+f�x�Z���jg-���k����1�U0�t�z��+Fj���5sY����3�X/���b{����]����M���sm�" �%�S��t�k���M/���N�����)�Q%��:1\�hw��<���:I���P6j��Y������Z����:����C�
���v:k������=��]����^�5�}S\���HM;�/���@U�������^l�N��{���1�U��s�|�l�k��N�u���J�A���v�Tb�������Tu������v���9��J�X�$M0��
e��/�	k��+^M�Lq��:���jy��	{3�NbLq���{�������Z5�u:���f��v'/�cy����x[��o���e1�������D��7�<Z#��O�c&1��%�t��	Ja�a����F���N�|2N[�\�3�;�u��7S�S�-�z`��T��L1�7�m��B6:}�����^�1�<�^�8O�`pH���'����|'�[���������X�_��_k��0�hw��D�S�8�Gp �m��~��,�<�K�Xb��y�/I��l���;a�u��~����9O���7�Vtk�������N{���d�.07�nw�Wm,����1�����>l+��w�qG�����?k��}.�=�%��[b3�y@��y�'��x�y�%i���CM��R��5k���d�v�$u�-����w�1:���:�����v��^�207�hw��a*C���|�5��_l�{u�����B�����>1����
1�bUbK��$I�F����y�7r���;h�4��	6�S�<��� �w���;Z�0vz_;��v����
\��N+�X��O����zWI��x\,���<������3�o�^�l�N�(1`!�y�'�Q%��;I�F��P�$�;!�t
��vN����V:2�����������T-�>�O�����q���{Z��v��~,O�����X�D^+�n�[m�j�7>{/���n�{�����.1���<���<�����	J%�����b#Z���Sl���z��S�>��������jG�\�u���x��`�|������V~��Qn,����N�;E�m�*�8��A~_���f�'�����]�ve��i��_��k�|�4���h����:�
e&��=����V�������8O�C���&(�b�g.���h$����z�N�6�w\'�s�@10�J����mt�����6��B��x��jg����\�7��\mg�U`v�hwf���������(~�8�����z��F�n���[<^������m]+�����s+�%���{�y�y��y��D�31�u��_|����(�/�I�N��7�q��VO�
m��
h��������F;:�����VF�u��X7�����Tp%3=�{j�^��k��XO�}���\o��W�7�n�2��)vp���D��W���D������8����Oj���1�6E=������N�6����b{��������O�e�\�w�]f'�1?b��y��y�'�Q%��+I�N�F;�v:`�l1
mtP���)����:�����R|����/>gQ��a~���M�3:\��aj�^��x������N�����)���:�D�1��\T�hw�N�C�j������l��L��b�����9jm@�5����}�V5j���7��v���>��=�%������}��|�9�z��j��G{�<�;�<f�w,q��kS��1� ���FmP���T�N�,��M��S������b;��F3��0�����l���h��u�e���j����}�e��s5�l��N*�~��}@��[�K�SO;��N�T���v���v�n%����������v�o��{�L:�;Z���gl'���������__��B����e��y@���[��X���0��,��M�3�;{&��8��)^���'p�tD��5��^4��F9�����T�:���"{���G�^����_�%>wm���Ml7��f������>������������[+�&���os.���x}��h&O������S���.f��Z���8~�q.�����!Ll������&�mvL��i���h�����z��@+�<������
��C��}�n�s�<f��w4�>�\����'��>1����n�kb�f��z���f{���K+�<`��XjSQ��ys��o_)��N�����0-����t��uY�V��R5��/�
�%���������'���?���������|yV����{��\�����l��������'v������������"�1�;�#��);0��������;��.?�\����hw�'E���)���������y���4 ?T4���H�F���m��5+����(?Le�!0�J��������H����9J���'�����j��%���KV�g5Z�j0�a�S�VE��@�����9�deZL.^��2��y��K�����p{�������d��<�%1����!O�W>v iH��Z��F�t������a�I�}O�B��+�A�[>v��0@v�����o������?@;�;�Z� ��<�E��v�y���!�y@yH�������o����#���-
�K�C\yS�(t��'���K������	0��'��v�5�'�!�k�����NyE��6Ti���n���Ub@'hw�K��'n�V����{y��{�~����z#;0�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�Jihz����W^�6l�E�t�������f��6o��������-[�����g51`�|����	�f�5i���JI�PJC�7ov{`VW^ye��'?������-/������iY�4��(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�Jih���SY��+��2}����j��[�������f5����'I��6o��������-[�����g5����(%I@)�p{��/��
��:�}����������1��&�%��F1��_�&�<��|���9�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)m��=


����;wV��b1/���������H�J'w�uWVX�j��q���W��b1/����[*��z@s�&�R�+-$L�"�%j����"y�����	�T�J��."��D<D��'i(��2��q�Y��[o���i6'�>I@)�-�/���Fq�H����;���T��k�S�����4���p!I@)
�����	��v[6g��o���|$O4�s���I��s[�_4�-G-a��<�(q���o�J@�4��z�m9�~�(��j�DM$N4�UG�fTI�V�]=�������3�������;vd�������I���7�e�m�����[�f���h����;+�&���}�����&n����PNne
�I��iCCCL��0���1��y���B.����N�
I	�����1��$M@�j��o������S>�����rKey����������2�(����4�� @����#�Wa�Z�f�J�^N4Yh�����r���<Z�z���ukV�i���Y�=�@��4MD� ��Wa8A�����zv����� ��85%3�u|g���{wV�i��mY	�4
�<��j�l�zbK{�}_{���J@�X������i3��R����^��	�I�����ujj�2���)����|�'����0�ht�������(�! "��J�J,	-����#+A�� ��=�����?�c�FWa���+/������X�%�����������1?&��IBl�&0���4���7�}��EJgh(���" ���7�^\����*P�'��Y������B��-"��e�������?��~}��
b��P1���QD�$L�y����	��,�0��Ho��s	F�\��'f���&�5�&���y��F��U��P>v���D\%1��{��m9`vb�#��\�������#��H��&:=�d��S�z�E��"`vb�!F���B��@Z�K�R��X
p!I�%��O��==�b����K���"�!�%fKF���65SK��Q;��$M@�W\Q����0;1��-&"��������*S�BjS������V1bE�U��$M}%���p[��5K��d�H�hv�H���^��F�1Wq�8O��W��

���]��������?b�F�_�8U�&��m��e%�z�G��\A�F�cq��sg��5C��%�i�8�w����&iP�d

�|��=1���cc5�w��J3����(vr���g%(7I@_�w"����h4��|G���I����I@��H��D�zW�65�����1	 ��(�������ukV��-:@���j���m���4?�F�ltK(I@)
MOSQ��ys��o_�t�Eq����3r���u�E95Uiz�j�n'�������s4��F/����k�
�-[�����g�������n���n��>��]���-��?v�����<��I0m��F�,I���<�C$��r�-Y�<I�����s����[+���"�PO�ec�0,���wg��i4B��m�����	`����NN��[6&�nj�7��\���7+E�&X��1������������3I,nOZO$<4��F+�o���f���[����	����w�qGVjO$[4����;����	�E`��=Yi�H|jk��a��[n�j3�(��4�L$M,��p��wf�E���;�&O���0�h��H���kWV$M,��v[%����o���<#O�	�)��|�����0��	�E$F�h�8�	����^���m9`&I�L$ND�C'�n�!a.$i`�$����t��wfs�S]"&������(l��9���/�i��-i���Y�b���fs��w��{����w��G����mK��v[�\(;0�@��'""F��(�S��I��N�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)I�JI�PJ�&�R�4���	��$M�$i(%I@)
MOSQ��ys��o_Y������w���6?[�nM7�|sVkn���Y���u�_[�lI����j�;���]fb�R>v i�������������;�L��v[V�o��������jje�/I�������=��;����B3b�X`���H�DI�p���r�-Y���[o�\aQ��byA`1���&�H955����={�4�?gO�����������6������1���WC�����F��z�F�����"�z��zE��N�41���N6�����
��]y���� ib�����cGV��x�E+���t��bt����s������|��m��Rs[�n�JU{���J�'�@'
MOSQ��ys��o_CC���75U���������f_>/�Zq�m����:�G����s�OU���/M�k�W���|��t���*�5+����o�����-[�����Z����}�1`1y������hV����_�~�f��3h���-����*������5c�r3�<;���<[)_��������R��|��H�8De~8�Fv������S�����N��0���e����<;��S�������D��~tsVk.�����sg_H�+F[�/��r�����^�41��CT�{D�����/�m�N�y])P6b�b����-�c��-�������W_���z���*�?���_Yn�}���T�ld8+5W\���n����s���"������&# �k���6��k�3�e7�<y2+���������J���s�^>�>���])�������n��b"�!������t����K�^������#D����6���q�HZ�|$�5v��d:��dVK�u���4����`0,d�C����k `P��L�����~3����J9�h���!-^Z��;{&=��/���
���l��R�G����L�"i��D�C��O�����'�Y)�NW����y���t�d��md�ui�O�F���B�<��c����3+Um��-+uW�^g688q�=�`����5Y�z��#�W�#�b^<���+�Z���~"�!�������J��qt��9Y=�|-a"���I���o�JU��������@�
S���e��m7\�^�K�8}���hq��'�V�(��x�&��u��#�!��������Tu�����1��s��Z�E��gY	�7��c@��B������P���^���8p ���~��466��o@z��Dz���m:1q����,I��������?��B��3�=A�C��?�~��#9�q���K����tj�l:~���&�Uo��t�~2�e��1Ib������{�6m���[G�?�~��})���|��[^�)����*��hY6�VI�`1�����>��t���Ymvko��^��g5������c@�O�#0�"	�����~�k��_���8�������s���	�_b�) ����d��b9	�B�41b���m��e%��w��^��������7�?����2E��?qK�9��y�����t�OV�l��d:��SY��(�������{���T��}=k�Wj���h�+>��t����	b@�x����k�wz�/�e������+�����_�de�?>�L�d9LM�K���?�|�#�����$Q��b�X��$M�NS9�+5�phD���o_M?�����o������ln��~�2����'+����?�����{�Z���K����i����c��b�X��$M�����;+U�5�u���TU�n#�z}�<1���?�J��G��Zs�\,?���'����Y-����t��ei���t���i������_�n��|M������>�w���TU����o�JU��6R\n.�d�y�]����G�Z�����l�ei��WU�����O}��Y�j����b��E\4:�6��jU���M�D�+���hG~���o�=+5�_��=�N��|��#L,I��lH�V]�F��I�#���������kb��j����_�J)]�b$��4�o���%��j������>��{{����#+Um��=+�W|�0�@��y������JUc+���ce��b~<��'w}-+
�SOL�>��RZ12����/��vzE�/�+6�A�N4
"��b��0��b$�t���|:+Uo�1� a�&'�������Yi�,]>�v$�KZK�XH�&���w����:q�C���

��;wV�D=<���J�X���=W�=G����Y���r_���v��������5�e����8���;>y~_���.����O�3��{���j��}<c*B,?���t���)�>x~��0<2���+.w��Oe�����Wd���<>�N�n�8qr�\zzz����� i�����7+Um��5+�O�����w����/�����N�����4+U����J��+ngP\��7d��G'���1��s�&Bq}�n��U����ys��o_�1Te>����k_',�{y�<y28p ���i��4>���x�������C��U��<
-�����-[�����Z����}s���yb�8�����r��0:�*-[uE��������'�V�/�����_{K�<������G����Z���K���%ih*�����1y.>q&{������������=����	�����?��G������?po67��6��V^���q��O�o�����	Xxb�8�������o���RZ�fCZ:vQV�������ge����;_�~��w4���s����3b���k�����!d����}�{���}�{sz��O\L�z�����T������T5q��JbD=g&��S�����?}iVL������i�w�=��\,�K��,�/}����@Vk.�����u����]?�9��t��de$��-8N<��N��<F�����s����z/����V^�!�I�@b��#��������q�H���e������F�������-i����jU�OM��=��?�H�1�y�|�7�*����?�|�#�����kb�X>��5I��?p��&F����k��Uk����#i����c���x,�N?_�?xV�o���#N4������o~�2������ ]�|i��z4m\5Zy�z^,����$i����JU0X�l��5���+�����hVR������}������g�v��W���������0���~;}���2=���dsSN7��5G�%���u+�V�~�����5�^���I��=���T�%G����H����#�>��^t����~����_{K���?�>�+�����_{se�
��fK����f����b��E\4:�6��jU���M������3��l8(�/��v��exx(������2��KV�����q���%��j�����$iz��C�d��e�a(�).W���N�:����OU���7�����[Y�j�Hk)��.���@�I��[u���R����F�(.W��0�x����~��z��������~��?Uy�z����OdK��7�.+U
/1n��I��Z2�.�tSVK���sY���r�~lX8�g������������o�����c�e�TE=���_�o��b�A�t��4�������-�<&��R������Y
��$M�Xy���RJGN�MG'��6��r5����;75�����J��_��4�������7�V^�������'��Y.99y.==�\M~}�^�4`�����=�����8	�|��m?�������|.}��g�Z����i|�e�������}��������~CV�:pt��(�1��s�&Bq}�n�4`������=Y-��g���G&�#�N��'&���s���?��D���X��M7f5��>}���?{_VKi��������������5����c�W����|M����_��t�w�HV��$���J�;��>q&>~�������z�>@/I��r���Lko�������L<������|�,�����Y�j������xV�ixt��|^q�A����t����Z��g���N��GOW����z�&i�����#N4�������o>��R]�6-Y:�����c�����hhhIz����������i.���c=�^��#Gl��O�+n�����z�����4�,�>u,=���������G�(�/��v����t��Bz�{>����{��uWf�T�M�c~<���A�,m�1���w�����;�k�5��'�����_�-	,��+
�J��������ih�h6�j�t=�/_��Xzs��-N��{e�X\V.M�/^��R:{�xVj����Y)U�����Ox���}�{sz��O��O<����z�����T�X(�&�
/��������#�����V_<����6s�A���#}��j��r�<�B�4m���^���N}<��<?�D^�������9���{�Z�����q�H���e������F���	h�o�!��
7f���L�J��y$�:�x�<���t�������z���z��}3F�J��KW�K����#K*�Q���K��%�#N?xV�
I��������W��jU�O<�N}bzz����_}qe�Avp���R��U�i����J���
�����G�@oH��6-J~����~���9��r~��U�dG�'+Uo��(a�f����[u{�kY	�7$M��,]�~��^�~g��w���t���g�����}oe�X~�M�;���/���b����_���V��+�&`^}������O��~�����O�?x��*�Q���z�e���-?�DX6�|����r��t��	���eK�K����X6��yyV�:5��������&I��
-Nm�)��t�����\~�X?��+�&��Xy���RJGN�MG'��6q����r5��zA�������Tu���JbD=�Pqp�����~<+���	�#Vl|I�����R:}f*=xd"=��D:|b2�<Wy����L���S�����E�n�j�!i���n}gZ{���V��8��dz��S���-9B,����	��^���u�����\,�,I@G}���H���wY����)�����	�c��C���{�Z�����q�H��f���0U�7U���zM����/}��j)��k�������u�G�%���������1�DU�w���Y
�7$Mqp���R��U�i����E1���y>�����&��8��=Y�zK�F	5���_�UG8����@oH��m���t��}Y-����D~���[�@�H��-?�DX6�|����r��t��	`�V]���Tuj��#����M�&�yZ2�.�tSVK���sY���r�~l�W$M����e����<��N4m"���j�����	�#6��mY�����
'b���Ng����~<+���	�#Vl|I�����R:}f*=��Dz���t��d:>y����?yv*[2U��h��Y
�7$Ms���Lko�������L<}����%G��c=�^�4t�������������by�� i��/}���W�.�564=�r�<�B�4t�#w}(�woV�Z;>�6�I��U��*�����@�I�:����o|�Y-����t���t����n�H�d|����?�4�������?�����p��w��;w��������+S<?��.@+�<�����JUW��U���J��
�����G�@oH��sq"�-���o�=�s�]w�U���X���.@+�<������J�[r4J��Y56���:������zC�D��+�
�8�oE,��"��lW�1��5u�l:~���������"�����l�W$M��;��#+U�z��i��=ijj����;������d�^�`��
"�"��?��������Ym�(a�H�Q&j��������&I}(���?����]�v��o�9�Su�m�U�O���3���V��`��<��C������������/����*�V]���c����F�(.��m���5���%�.��������}���"'�q_��Z���V��@�~�q�E1xP������-'O�L�j)m��)���g5�b��-i���Y�b����N��>1��&�<�Ny�R~��?V��������?�j)�_vM����LK������o��g��_;>��Z3V)7������j����W�M�&���SO<T����M]���67mxQ�q�������+e����I}�x�WE��6e��o���r�DQ����l�8�e�����A���a(����%�
c	�
�&�%1���<�Nz��_N_���.��_���6��x,�k�8z�lz��DVk��7T���_�����WVg����=G���>����!��>��"�@�?x��w���������e�F<F=�?��lILb�&�f&����CGOW#�9:q6�~����-
-L�P�&�H9��u��������>��������?��?po6�*�1���RY��@���$5��N����$��8|b2�<Wy����L����"*	�r&�.�4�G������f���|�����EQ<��R�}_��;�7>����\,������HvH����i���yM�W.MK
�AY�������#'����M��>Uy�z�p�+b���z������w!�����'�q���Ggm�{o��V���#+U�q�Y������h.F�8r��������q�H���e������F��L�<�#�V�-I���k����t���4<}|�99y.-J+Fg�5Y�d(�=�U���b���z�����x��I}d���Y��8�`�-��2���!���y�|+����#��X�^"^3�����U �����abdx(]�v,]�f,�[>�V�,�<F��������5�����g5�b�m���t�����rM�$q����$9s�����Vl��:�X��G��H�����v�mi��=Ymf�6�k��t��kWV�w,+Um\5�V-�������a�hV�:���f%<b�����t���4}D����n��J|��#���_x����6b[���J�d���i��}Qd����F�$~���3�|��%��?/��h�d���D�����'O�d����_���������;$�8T-"n�#J���g'^����
/I����+e�<����Rol��%���?��O��_�w��y4&�t����������j�r{����=z:=u�LV�Z3>��.�>=6���3�W�Mk��f��8>y.=������������k�%���B�<$M��F��J��0���-���_U1�"��=r��bajj�i�`AM�K���o�*)m\=R��l��L���j)m��>9�84
�d��u��W$M�+b��y�4�������'�u'i��#���S&C��hi�beu$����NO�I���-M���9��|�&.����+_����`!c����5�x.���zj��|� ��qB^�b{1�&���b}f7��?f��e#�
9Y\��$b�����(�E�$������c�$L\4v>��h}�n�4��Z��!��Y<�����1e�5b{1?��+�@}cW�(+U��l-P\��$b�����4y�2�u�%�g^	�8���X7~~�X?�CIM�Mg�~���L��?]��<���#�����8�oE�^�q�����C+��,���{�$����W���t��������tU����<��D:r�z��|�K�+n��J(����gp{�E��11���=�����DV���9���J�<q2��t��ci��T:���������#����i������g����_����it�s�"*���e���i�5���Xh������?�����N�J67�ek_����&�}���.������L���x��G�M����/^�,���5i��/O��-+��B�<$M�������b�hf��bp"��lU~�V�V l����X���w����DVK�������P�EG'�����j)]q�������"i�^�hL���������_��������O�|a���+F��F���N�g�Aj������H�LO��@1�d(������I���?I��{eVc!=���C�fOJ��eg�����j���t�?��/�O��F6��7n�>��ozuZR��0�|���9Jb���Yiv�a-[�_��0����
o�JU���NGO�v0&�����~<+@��y,.���/
y��dz��d%a�����9q&��<�B�D���`��OwK	���r�|�9{.�����	����QY6�`�$Mp�m��e�������������t����?}f*=xd�r��'&+W@�c�c��x�&��h��Y
h���'O�I����*�e�fY���Rz"K��w|����#3/��>��/ =�7Y�j�����4���~\_����12�����������d���U��E�l�LQ��ec�N���O���������*���wg%���[����4�x|���t���������X>��A"���/��x��������g�;�?����OU���t����o����n������%���+�'=��B>���l'�2rD���W~��)]sEJ��Mi����5�z���#S�|�+Ym����S�/�~=���tt<�����b��it��������:�.s#i������9��O���y��?<c��fb�X����&��K����O?{���������������z������*���e��F��[����]:�6��j����[Y�j����\�U
b��K�J��{~3+
�O���[r,����51/��+�@�$M����K���o�JU7�|sV���=:g�6���������0]���lnU�c�����&hb@�975�����J��_��4����������8yf�r��NNO�L���H��O�&}jz�z����sq�u����wV����Q�D���������?h|������Ek���&j��X�&�.��4�g��Un��=+5�s���Tu��wf�����P��g�m0��6��n��;��_���������_���c�c���/�������I���_�G�A!���|�s��?���F��N��.��.�<F=�������*#Azv�r��NN��y��
�F��������|�����P���K�8q�KY�z+�V����1&r���t��cY-����wN~�X7�@�$M���p�q�D1@�'���"��1�x��lA���-�������7��-N��{e�j�~����N�����>4�����W���~��L��'��������/���tdYZq�Ui��������iu�1�+�^Uy�&����T%)af:D-Ma��p��z$m\3Zy�x�~�Bb� ;����R���)�P\���A��3��R����w�P���<u4+�I}h��=Y�*q��$��}��'��u�v�����jA�b����
S	����?����^����C3��Q����������31�_����JU�V]������x������!F��p�����t���t���t����n|i�1�1t��$��6$N����6+U�8�fq�TV��3��l��4g����M=S�\V��t���@;^H���ys��o_���x�M+'�s�nX�����'����t�`��4>�b�*,b����NG�MV���
��^���d5��-[�����g�������J�c&1X���s��|�D�<�bmZv��J��S�?�N?R)��xy��o�H��K�}�������r����5ci�����Sg��G�N�s�.L���jY��4�N�M>3qA���%C��U#i���_�y��T:��-:R���1�'#F��]�neJ�\Q)6��c)��\�|���Mo�b�2`~�����[t���8�����9~��4�|���������uk�!`f��H}*N���=?�d3���������T1rD1a"�	W]���WM?����b���_�y���O{!a",]�Z"S~�X?���"� &V�,���O;3#abI.�!n��,a"��,�O����v�F��ng��Y�>g�%������>�U��k	!������5Y)��c��3���83q�����_��H��cq��k��t��wVN��'�Q������:��m�!���A��`vg�Ox�>���#�u�����}���=�4����^}q���V,�~�>�z��~����z'��JV�?b�b�r�XV��3�~�����#����cM$H��X1���i�\l����1�{
�����\V�:�d�������~>����++
�7����Tu������&N���<�������H��taUI?3T%������t���t��}���fsSZ~�����7�
���Hk^�9�K<�?:=}���Z51b�EY��.|���2����3]�����`n����,N��s�-_��V���9�
�,�������S���LTn�Q�rlIZ�r4}��S����|�\z ����%������[t\w�X��x���=���Q2����Ms�t�|YJ'NNo�_Y~���q��t�+����������b���Z���uid���O�>�&���k������{�����,�������S��2�:3����������'?��3&���z���c�X�r8����J��A�����o��_��z�u�f��&NI��Ld����Z�D����NL���0#E\�v���S�g�Rs��"A�����Z����_���j�we��H�x����{d���&b�AO�?�O�%]w���VI�?}�2&b�X���4@�|����/���H?��?K����9��/����������,���s��������|$��\,��z��sg���_�j�[q�"�\�������Wg��SGOg'/B?��x>��~�Y�l8]�r�R�Q!��x,-�m=�K�8�-���r��������fl;�k�k2X^���TF�h����b�2X:�$���xCz���o���-�W��u�;GQ�n��������w|:���?�~�������������\,__�������jU��/MV����F+�Q���c=���?�����8�rq��v��7�|Cz�n�j)��<��?�H:u��4y����h�1�1?����b�~w�EK+#B\�f���P�O�8r�l::�|�����V������m�k�k1�b�������mF��{U6�*�1��?����eIH���^���lI[_�!�[u>������7���,�#i��:75�����J��_��4�������7���'����Y-���u��������K��K+�Q�~�X���X/�gp-���Y�*�����cp���9�y�����^���N�x.�<����X�1�y/����z�b��pZ:<�?l/-\r���JbD=�Pqp���������5l���2]����nz��k�9�n|��*�Q����"[�|��������>���>��7V�(��x���4@W}�#�K_���V5�|u_u��tE�1�y_�pe�A��?���6�������F���U#Y���>�eh��WW/$C4r<�\��v�������{S���yY6��X�������*|���t����;��3S��#��g'��������c�|f"M�=�L������X����&�Q��kWT&:O�������?��}Y-��#�����J�V^�F��LO�*�Q_�����5�^�?��=�����%+Ff
��.�,W�_�����o�J)>����g���X�&�>�1�tI��{]��oL���k��W��0$�1�w���V����`�����@����L<}����%G��c=�^�4@�����JU�V]��G��/oxt��|^q�Aq���i��'�ZJ+FZ��$�\��ap���e���O6N��������e��Jt��o�<����I���@�������S�1�1�U/�,[�<�v�+��vk�X.�X�&����SY)��k����O~��X�&�� Y�|UV�^2��r27q��
[vf��&����GSz����z�z����T�1����5������{�����^wi����qx";5s4�z"*���A�]��S������j)-]Z����r�~lg�D�����W�<?q.+5w|��=>c�e�l�j��_���j�we����#O�t�#��O��%G��/�^��(���s���g;>=�������	k�����#i����c�C-���z��7�)>���
�L�&���+
�Jir�V^�������'����'NN�KOO/W�_����7~�2rD+bd�X��������������<?���P�v�X�j�XZ�|$]2>\y�z�]z~�X��:������*F�.������G����g����>���;s�dVJ����teV,���
Y��������9~�\:�����P\��#G���/�K^���|����UQ��/��/WF���d����jYuT����a��<#B����3�������"i����w�L�8���i~o�x���#Ym�������%]�?��b$���;>w:=}�L:|�L�1�>Uy�&���)���^�����O7����5��L7��s��������>?r�2�0�rYq�F	5���_�U�t�������O�{����I����+�Q��'9[�#���ta6o�����E�;'O�L�j)m��)���g5`�|����~�f����,Kc�O���}��<�&�=��L������������o�j�gj�\����@z������]������gi��N������s�J�|&>Z�f����������!��UWW���-[�����g�������z�<�N�8�x:u��J��C�Xy���u��G9���q�H�V�9|b2��������+���6����l�eil���2��/ �w[Vk,n]#q@��c�&�x��{�����V5�|u^�l�'����3����U��y�nL�����6���L�o���+��G�9�m�������o�����
P������_L����lN}o�|}�?����d��k�%i���O�����~���ZJ7\�,��=���������/�y�/�IZ}�+���?�������j�[�������������L��z��_�^|���V	'�>1==Vy,&L����+��A$@��C��^�����������k�g����1?���$L@�����������0>���ec�g�5/�JU�r�m��\q;�'���&��L��KS�������z^,#S@�I��������M�G��e���b����JwE��oN/������_������/�o������m��������j��cG�Z���u��K6V�(�����3�d�rk����[K^�/��v\'9���ctiJ�_��5W�t���V,�~\S�����5Dq+���|%�@gH���F�.I?�c�K������^wm�����3UQ��������\,_f�c���k���\����J���Y-����i���k/O�+VW�(��x�&��u���W��p������D��&��:[Y�&�>�����VV���>�5e��������y����J��&��W�xy��w���������������c�c��^rY�$@c������X~�3�#jb^<�W\������e��CGOW#��������m���Y�Au��������h�0Q�z����:��@'H�`A�/[�^z���G�v<���Y)������c&L��s�LM~]:o������=Y-��g���G&�#�N��'&���s���?��D�<;�-�*�]����� �:w&�8���V�G+������I�������O�jq;���+�eb���s���Lko�������L<}����%G��c=���?����7�b��r���|H��o<����T�td���P��{���Y	�ny��?����;�Zs�\,��[~�k�R���Ya'Ne�Lq;0�&��\�:+U�=3���J��R��KWe%��K|G:����j�
MO�\,��Z�4-_������B2D#�s��~l:E�}ch(�����j)��������#0�����
@7=r����{�f�����i����a�X�1�a��o�,�1��_��Y)���Rz����@<�����N�4@_���5Y)��c�4M�83q"M<$��\��{��}���@VKidx(]�v,]�f,�[>�.�<F=��.��&�b����j��/���Tu�����M�?0�|��/{WV���4@_y����JU'�y,�9}a�D�;���Y���.�up���R��U�i����E1���y>�������i���Y-��3)}���z,��������#����OO?_�qz���^���3$M����>[� ���K��o}qV�&G}����������U�(��|2E���=G�'+Uo��(a�f�������ZVb�]����U�+�U�-8y2���~|j�-9B,��z�i�&�E��_�j��'>�~�73��o�ve�r���(��������bmV��8z8=������X6��{�������e��V���_���?po�:w6�1�^���TF�hE�L�@7H��������������������;YA�c^<�������%�7���7�~��7n���l�@w<���N_�?�����4e������������#nz���%/ygZ��U����������+#S@������g��>����/|!��X,��:���������5����������U��3�R�y���z�k*��]�3�S�����[����D,_��t���~���_L�y�d�����<F=��_��lI�I���{����Od��U�V����W�(�������W]�v����������w��2E9��s,����e��.XN�{i
-Y�V����#���	`Qx��G��}��ZJccci��
i��ui���)�1/���ub]��]Q���k��/�k~�O��Wn���t���tt��hGO��,W���zE��(|����JU� 1::����y�\^q]`���������-��{e��-�:�U:z��QO$T�~>��7�\V����'�U�w���Nn�=da		 ���H!m�J���Z����b�~���:��W��S�v�3u����cm�Z�
��o+_�KAP@A$!,!�ron���������{��Y�=����<���9��!���|�>��� i����������&M����\��S����+_��M��o�����o������u3�N��2��;L$��7 �[�R���&o�t��[g�7��70`F�5/�r��&I`����HSS���j�q���z�_��T���'�w/?-GNF&Gh��_|�&k����k�x�
�t��m�r���:;��P:^�L4�&��kmm5-�PU&BRRRL����hZ@r��� ���A
M��xkK>����C�q:`2�4&]YY�i�z{{M+���p�NUZZjZ@���{��a"[V~���fIqE�u��I�Sq�x������L����\dzmk�W~E�	0�H��N�F����H�����bsn��sG���fG^��0���*�����<[�3$=+�:k������W{���`��U.��7���_~B���+���������;s�	09H�q�����D:::�����t�T�cB�s�d����L��_�)���U%B�?/x�i��_����T��s�u�$M���l�2��555ET�����f���������eo�+a"D�;��p��dA��UUUR]]m"�*M���[�]]]��m�sV��9:Hf����=GHz����r�;r�u�> ��h����#i��U�V���SMdkoo���������9@�;|b�i��2G�c~���������#�������~-�|�Y�����5�� i�
��#k����K����t���9@��3m�i�����5��q���`@���N��/kd������3W�jk�^��S;����C��+)))r�����W_-,���|sE����5�c��@�t5��:����YS�5�H�d�l8��|��<}�~��?_�gk�����q��sg��	�/^,7�p��~��r���Z���O�%_��r���}�_,;7���_��:k�����f$����MK���W�]�&\�����&��G�8�b"[fA�����m;�X�8s$M��WTTd�������_^$�6��i���i���_"'����V/��i�:��1'����o"��.����d���)yb�k&I������Hn�4��-�mk�^�9:pfH��c�?{����i��
���HNUe���w�j"�@o����I{�O|m=���g�5�~���f������?Gn��3ezDrD���5�����#i Ni�������V���YX<�������HN����\0{��l�GGc���{��sK��u�\�O���Hf^��e��0��tL�s.`tH��C���V����4��3DfO)>�f�Ev�����!Z�������l����GT�����`r�z��hc��D<�Y��s���{F��	�8t��L�VY.R�g�(�?3x��a����d��#�q������YS#���X������0@�8��eZ����U&BR�~�Ww����A�@�6����-8b%L��u�V����DK�u@dV����W�-������zQ������kU�<3L��SM����Vl�oZ�����I.�m�6��a����Dk����~����`p�}�m�e"{+��p���z�d���#�������~-�|�Y�����5�xR=2o���D���D���
�F*�c��,
�����Vl�i�s�uW�������������F�H ��e���z�j��y��Ff<�=�|>�����H���R���/]@<�j�����;M$��jd�]�����&Zx�%��&r�����_�U��9dzw�����W],������j��c��g� Q?nDb��������c[^������#M�Wd��)�pN�|�}���USL/����$/�
�/�v��e�}������M$���J����]���\;��D�������<�+��Gb<�
��S���l��_,�x�/X���&}�~��?_6aB=���5V������D��g��G�~B���k	��#�V�����5�r�������|\�zN_�>o�	���F����n�:����/����u�_���w�7)�3���KIM���%&�+H�D��Z��}�����#oo1�-��D�JgZ���t����5����,<�n4�� �f��u+���N�h?��t�����6����9�)t����&����|#B��T�%�\bzlw�y���|��yC��9��#�S�6�i�i�4Az]��8����7O��_3�HZF�L?Gr��IFn�uh[��Z���� ~�� �h�����Ld��.�����;��:k����8`(7�o��3��D6{�t6�m;�X�8s$M$}@����L~��=��cZ���*B���`t�/���lGb'Nh]��S������<���-9r�L�H��>��=��<$��k�#*L�z�%��B�
�KFN�x�������	^�y�"+�@H�'U���K����o�qE�\k���9��&������m�|�oL������U��v���7`�����n����=�B���`"���c"o9�jo�q�����'x=dfp^N�E&r��'��#3�X�2OO��k:&�9��<$����U��e���?�LI����c[^1-8]jJ�|����O���UHIAx�C����>{�Rk,���W�m,_�\jjj��8�����{lj���2��o>�O���L$RYY)���������{��8��t��3�"y��$��2s���������g/�����hx��s�x�D�������&�)�.YySL4���f�67��/�t�dg��0���j��#�m��$����X�k@|�����M(kK���*w��n?.=�v�=V�<���Xm�-]�yZq�u����&H���+V�0��7���v�F���5r|����	��������8$M���#��)��0�N4�!i���� %�G��S�"%��yH$�@�[	*5md�L�q�i
��z�FD�%H���A�D�.%�e"��x���o�"�6�g���8��3��;d����t�Z�)Ybzmk�y�-����n��B���������������@�`�@"�w���l��L�Z��}�O�����>�j��u`��4�����
�r��c��5V���{@2��-�"p��=2�0]��fYg��t<'�SN�E2�]��E��U��������:k���S.4#�-�c��,�7�H��gZ����e�ekk���9e�e������E������:����e9��O���/���e���i�5�~o�n3��A�D��u�i��^��:���>��\�R���.�/d��-V�^�a<�
8]g���
���S�)UE�R��.����Y�9S2%#x=D�u=`"$���4�-�uNFs������y���>�W��-&���k�'��>V��D���O�V�q�gM�����g���i�������Z���O�%����Wy�4�(��]������{|���`��4���_�GB��M���{D�n��i�fdHAVdU���L�Tf��V��#�$���>��l�����s�"��y�O��=�'�<���s�I=�v��w�hpz]��,�SjZH&��s��SO=%���sE����5�c���g/���;M4���q:������w����KMM�6��
�X��_/+V����S����{'}�b���&
�{O��'uuu&)//�����%����o�]-B������m�K�/`�s*��w��j��G�}U��y�D���I�����=>��7Y������O��<@b�����M���j��c��g� Q?�d��Gl�y����6�����H�O�KF~��g���0M���h��@������e�L*�%�@  ��?�#�N�2=C+++�����	��5�K6����o������l%�"�Y"9�y��"]���p^�������?g"��L��I	$�C��/��p�4�����{O����jQ&�@����kM 2�0���c8M�^9�^`��?O��S4
�)�? �z���7{M��*����/�'�T~>��JJJ�c��4����Gl�y����G�g�}�D���IM�Oz�z����
�#��-W��i"$�]�v����n"[AA�deeY���nio��o��s��%K���=�����kL�w�&RY.Rtz���v�=%�/�I���$5w��n1�k��%�
������	�u������������#���G��@�����D����y���_��-(���0@ba�@��������	�'����:G'L�x&��V�p&Lh����
�C�����m�sV�9#�L�Hz�#����0�����F���c���7�~��w�i����e�i
m<�
�,s���e�����N�����&5%E>��
������
��Q�E����>X]a���5����]`U�	���|<hZ6M����0Q��E�a=�
�;_1-{K�X	!���:.�9�����l�� �K/��?��qF�Q1�����	 ����#��?`���=RU4����V��������r�]?���N���uL-����6����b{��<bc�H�����[_�o6���V�+2��X��"�t��SQhz�l~��Iss����������&666JGG���2e�|�����n0��'����D"Ue"e�&���ZG��E�.)�i&��d�y�4�@��Y�V�h�������!z���r��a�h��9�m��D�L������t��r��o"���� o�<0_$M`���k@�	d��S�hN�x<��[H>===r�}����2QPNL{{�455�H��;��2E"�j�����;M$��J$7�C���_k�����Qr��a"F��v��Ib���5�����V��~��l��=Vb�`�_�;�\�1��k��&J\0���	XZ[��G�H�EWI��n�S���l^�i��mF�}8$M0	rg.��7�a"������n�������~��������W��D�Weee�e���5����@j��<�n��S��Dv���r$W��d���DK�u�I	d��U�e��}�i�^���yo���V���E�_3[|9��+����N:^�%}+#���k����%GHw��Y��G��u��!9�o3-����N���u\�s�������{t�|��_�-�z�:��}z
06H�H �{fn�����7���x����� ���Pt��O{���M�6����/>��uh[����5�������HGG���~�N�*tL�s�[�_p�i��6�N�h���;���i�W���|�����_���W���=\jC�������Nk,����X�W����KMM�6���Y#[�l1��e�b��m�y�y���������D"������m"��u�����H��W��}�W$�r���:_*/����G2��#�7o��;w���-]�����}����j��c��g� Q?�d����X���W[[+�<���D233�-7�+Hh�����������RUUe"�8��>���Ll,��
~�������;�&���
2�����������m�7������3�X���U���=i
��_ALtII}���
L��~�z��4��-�r�,�i�,�����^��w?f�5��dI��q��a&����:��5�[i���b&D�"������I����C���L��9nL�P�.�C
*�k"�&H�6���
�O��0����0�~��=�%Ld�H^�L������9�3���V�<o]$�����=��d��m�d���&�
�V�x�{<������?��#���e��De{{�����.��D����S|��$�h�����^�^�VlH���y�5p}���?���<y��m���r��7���1=�tr�}r4���`*�7X�n���Sr����H$-#[rJ�[g���x��[��
k�#�.3`8��*M$�_|��lw�u�����
}��8�?zn���7C�R��_fh����
)))���\��������s������>,��|���G����j����w���0H|�y�J���]km7:�c��0��r���wI����S����4���>�;)&��>dZ��)�'L(+�"x�)z.`��4��{�a8���%��(����x��H|�?������D"3f��H�p�R���3���E�����7����H}����o����/���|��IJj����)*M`2���5p�={�������{���|k-d���x�b�/
�����%�)]&)�i�7y���o�����y��[2�j���tL���V3K����a�-�6*M��>��C����M��F��?�� �����
b%L(��cB�s�d`�_�?��%L�������<�D����4)��n��o�]n��V����%s�D������0�<��v�Cq���z���4����}�����������^�=7G��?�� ZOO�455�H$#c�J	�1:W��6���V�6�2���P��\d�T�������Hd�yHd����=�`���YG2�5�,G��)�����sc����;�����m�mF����.����_Y*%��|u��M0l�W�T%$6������&zk�-_���h"��o����M��X���M&�
~:*����d��9� ��7A�������	pf���|�y��`]�<����I^;~)`��)�pN�|�}���USL/�������;M���
2��;L�N"W~�&�-�����j�;���t�D"���u���|����`{W���L����kZ���N������o>iZ��	*/���=0>~�����O�S�{-"aB�v��������{���F�0����x7KI�Y�����3���n����0g��	��@@���m��EJ������D"����X��q���l��H�
��a��Dr���R����|����7B��_~Jx4\�h(:N�;����������&����T��,���$��I�ke
7�3-����h2q���g��"�F��	�@�����{d�����}�|�y�������Y���t�������[o����&�hR��	q�u�a������$1�q:_�;�������~�x�����*#�@���J��*�����w��������������=]�����YVd����nie
_��&r���>��l�����sz���y�O��=0r$M0�j�l���]#��m���}����������87[�l�i����"�I�h_ss��l�s]Zf�i�<���TO��UHV�t��)Oz�u�X�=���?��N���i�*�E��LE�+#wu�������\0�L�[1�DvrD��7�����t�Y�����L�st.���4�$���[������q:���������DbU������'����C����B�st��x2�$#��D"^�F������?�D����_.i����W��z�������/�eo�+a"�0x��U�s�����r���j��M��x�:���cu���4�$��-����V������2�4�:k����\qb��U2u�T��������:���cu����HN4�x�M�^oh5A�s>`l|���e�W�/�S��n��1&B4q��UG��B�>z? �����i����8F�9N��}�*��*���*�*/4=��U�9���IDAT�sg���L����&�=)2�8S��2�$']r�S���s�dJF�z���:z�D���xd����t�R�����:���f_kZ�����$�����N��cc��R�;N�OM�6��9���h��R�H>���L��3�>�����������Uj�LOl:F����#i�Ipt�OL�6� C
���A�G*
3Ld�{��r������������H~~�����O���V%�~B�}�D"]~�u"GN��ji
>7�Y��������<�{�7�����iZC����)��GN�2��yc� -�g�}�����#oo1�-��Dr��Y���t���9�&�G^1-{K�X	!�8����9��/^,7�p��~��r���Z���O�%�������,1���M�"y��}��I�W��Gt��@�#�}���4�d�������3G���\���>n���S����L$���-�����i�UPb��>��st.���4��Hg�>��f����s����$���"�H6))����?����3=C�q:^�����"�g���������q�gM��$������HS�Hk�	b��:.�9�m���!���L���}z�)z.`�XQ`������lY�CW��}�SJj��|��d��/��|H�
�+6���U���x��Z8'�4��k�>������:.d��R�B�kmm��dR~���e;�;q�-�_��Tv�m��>�O��[��+����&B���	q��IL�������wd#��E���?�]r������/�C^[c�5���i+�H�x����M���h����0�������gZHF{���M�6����/>��uh[����eO�P*�7�(���O��1�7���j���8�b���=��!3��rJ.2�����������,���9F��=�G�,%�#y��L|��7��9��|��Sjz���-����7��Xn�q��D�^���Kw�q���J��g�5�����Ct��USL�d200 �=��<��Sr������\����t��u�i�!�5�M���m�_<����C����ynu����li���L��D����T��
`�H�`��
����H��jz]��8�����+K�3Ld�-8�;NJWs�uvn��t��C�	�q�F��s���M��X��f�^��U9b$�2��w��SM����Vl�2VeY�iF��	&A��7-[}{��w� �	z�i�����,|aMD����8���u�V9y���lR^^n�v��:���r���wI����S����4���>���L�v))"3K�M�[��L+��������i&�IL���d�
�����9����V�4y{����:k|��o]�yy�M&�V����UR5�P2�#��3�=V�^��D��������dffJEE����Hnn�uh[��Z����n�Sr��z��e����k{e�5��k���H��3���D��C&N�����l1Q�\���4�$�Z�V��0�M��8��+���sK��u�?yf��}�V�=�&=�Z*?��<���~�����^z�%��i�DFF����O�9E�u���4�-�uNFW�}�i���������	��6�0�-z.`�H�`-��"*NE��xn������;L���t��G�ihh0-�����j����	q���]0�L�[1�DvrD��7�����t�Y�����L�st.���4�$������L����\dzmk�W~E�	��V���{�D������*�S����N:������G���LdW��s���{ y����r��b���M��x�:���cu���4@��\(oZ'�������Wd���Yg��?w�3L��k�#*L�z�%��B�
�KFN�x�������	^�y�ZL�kmm5-�PU&BRRB[��M� ��*����r�������z�5V��_E�3)�)<�"������i�2��%-3�D��_�;=�������En����kZ�
Dn�RZZjZH�))�����?}�ZV�W!%���Xm��k��j�5pvH�`��~�_��H��0����:��,����%%%&���6����q���l��I�P �om���~|(�q�i
�'�����<\i���C�~��N�I:&�9��`@���N��/kd������3W�jk�^��S;����C�#��p�����g������;|���v��-3-[SSSD5��kn'����p��@����/��5�LOl��8d��9�3G�#�hN�i����8E�������J���M$V����z+y����:��}�*:G�"���7{���-&�e�H^�L������9�3G�#�����Y�_Z������9n��)�}�<V�Z%S�N5����]�C�N:V� ��}��<��5��edK��s$�x�d�Z���O����83$M0
���&z|����4�����Y8����,<��]�V�.]jzb�1:V� �<���-9r�L�H��>��=0r$M0
^}�i��
1'���������gZH&)))r�����W_-,���|sE����5�c�|�h5-���bI�<=a"D����\���4�(��,��n\n"�@�W�����~\z����Yg�����?x=D��[5�DHF�/�n�An��v���[�C������|=}r���D"��,���9F��=�G����+K�3Ld�-8�;NJWs�uvn��t��B����8��eZ����U&BR�~�Ww*�k`dH��<��5'���t<����j�����e��M+��7-[eY�iF��	�E�9�3�g���i�������Z���O�!9������&���L+��������i&�I����m�<����$�<`7�q:�k ���s�='O=��8p@:::������t��E��3-�U���y���>�W��-&���&��<�Gj�3�-#�@���%wJ�u��I��<$�@  7n��;w���t���9H.W�}�i���������	��6�0�-z.`�H�`^�m��0��I���
�*�.9E�I���k�'x=D������u�V9y���lR^^n�v��:����er���&��#���!]-'����:��}�d
��sg��	��������DL����U��e��KZf��"i�^wzl�+��dP[[+555&
����)RRR"�����m��k!:G�"�����r��b���M��x�:���cu���4���]��g?��|�;��x@����Z��{���d��F�i�[r��H���	�V�G�����K/��M$222L�}z�)z.�/��*����r�������z�5V��_E@\����'�xB�y�9t��tuu�+b�<h]�1:���[�s���e�����^;������v


�%���QM"�^�1!��H�))�����?}�ZV�W!%����>�����Zcg��	74	����W_�,s:�cI�L�}��U&�'=�/�����;|���f===���,2X��h�1:W���t��Yr��+��;�/?����m��k��A���?��tww�(��Jj����S�C���cue��2��z��5��q���;�����m�*!)Q�#u����Z`��4���]�"*L������3���Xrrr�C����Bt��`"x<)2V��D��|�54�����X�����E&�����Vl�[�������
��IW���3��	8`Zv�����x<�'L���������M���pN8i���.}������:.d����%x���������s;�;�-=���e9��O���/���e���i�5�~o�n3��A����3-����A&B���	9q��i0�>��|���;b&N��;�;x��C�;������MK���C���[�hR��	q�u����Wy�4�(���J�k�������8�I`�������5����t��������;��y��r���M$����^���K��U�>�����������s����`��e�ekjj��&�}���&�E�u����L�k�4������dv��k��G��tZ��lE��	�x�������&���'����:;��P:^�!�TUUIuu����4Q__o%O�� zh[��U(t��u#��^�[�J����2����rU�;�x�L�L����|��/�����r��}�:���G�Y�c��	0�t����\���q������&`b<��5'���t<���U�d���&�i���������cu�y�v[�#B2�D���=]��X$7+x.�c��L7��2���e�WKg��������|I^z���vu�+b�w�v���ct,���4���i�L�^4&:�^s.&L�|� ����cPa�������ke����'6�cu��z���U���� ��W���h�{�i��&A���o������t��%q�I .,\���D�������%Nh�^�1!,0-&�m��Yxt��BB	z6[IX�t<��nOz�����W_m�e���������53[��+o�_L���#V�DHa��s��|7��3�����D��nR=�]X&y����m���:p�H� ��^is�uN&K�,���;�D���.9z���������mk�^�9:����g�H��c&�edHV~��N���;�x����x�b������o�[o��:��}z��������n�1�q:_��F[�r8��DFn���'�E��=��}z-D��\��!i�8���y������t����K�/��A���`��z=\v�e��~
�j���r��I��������9L��k�#*L�z�%��B�
�KFN�x�������	^�y�ZL�dWTTd�����i�r�Mc������_�7-��DN�����4��}z�Yq���r����V}����KMM�6��m����������u�i��Z��:�X�B.����V6l0-����x���V<|n}>�����/+++%;{�?m�$���C�S���������O����IR��-n���)�?�������gpZaB&������Xuu�����P��$���5�0�<X����~�<���L$�]T!����O����m
�2���������v��
;7�����D��M0�S�"�
&Z��WRROO&Ht7������m�u�*1_k���NY���,��/\e��s�$d��.���]w�ezFn���o-*�y���9���b��u�e��s:�;�s�=c������+M$���/��g``�_^��Hg�>�3���E��K�������k�8p@�?��v���2m�4Y�p![r0H��x�y���ac��O���V�	em�Q0�j����������Y%���^c��d��������$_d���|���"Mv;�d�,���v�"
m^���}�D"y���m�M��t6�Nx��\)e�9&��v�0�q��V��OII�^�d�@�����c��5����H�c���P�J������:g���~������4�(����Yc'����&E|�������~��r�m�Y��|�#$LWa��t�y�g�@�[	*5md�L�q�i
��z�H:9�o3-;���1��P��r�w}�09�>i"�pp6���tQB�~N�?�v�����:��x�|��D�<@�:��'���f"��t��+����R��&%�i�Y��%�������Iaa��Y�hX�k������e��g������;l���M�����hC����`�c[Uv�m��.Z!B��	��m:���s�2g&��&�[8=(k���100p�������=]LH��]�����q���V�����;�����poxF��__0-[ea��f�m9/�#���U�|�-]�@�c�cp�yH�����-��7��E����,��\(��*Q�>�C��-8N��tu�-v|0���238/��"���E�%���,��_>�^�1!�V�`����U,�����X���b����OR��#t����>���d�Y�.r8?��>��74�s��1��'���bO�f���I�]��.QI��o����^��Y4UV��{�
$�v�?���G����g��d���S���*��%��>��9��T��v0�x��Y�k�c��O��_Y[l����*�n����~\z|�V{��)���^k��d����I{�oM4������W<o"w������O�d�����B�)�&���-|5a��rBz��L��?\�LV�m���q��]�	}8�~P�^}H����w,V�z/}SA��6�~��F��#yx�^,�E��Z�n�i�%1g����[	*7}d;:��|�����?�}�����_���W��J�P��>����vZc�l�y�5�d���+��coC��&��9��$/M���#��)��0�4�a��&
~}�j��c���`}
�C���L��9$L�����	-�|8����\4Jh!A���X�m�����#���BCh!��{�1�#�\��?s����r
L��IY����@����/��5�LOl��8d��9L�<����D���������3q���!���Nz�y�$���!���%��JN��k�X����n��,>s����0��@@|m�����:��}!:V��\�U�P������s!������U�Lkx��Z��b���t��d���&M��!����"��7�����*�i" ���7{���-&�e�H^����v��:����Gl�yH$�*�����H$�ok����U�>�����������s���@N�E2�]��E��U��������:k���S.4#�Cq^���?���8����:p��*iB3�����X8�ZH��)^��N�yr���E��,>b��~�k��_�����~i�q����7O��_3�HZF�L?Gr��Y{}��m��k!:G�0X�k���\,��0�MK�ww����Z����C�x�`p)�i�[����4	���C���e�|�t)�
'Eh{����k:��	8{q�4��"��U?�D���C��8Z��Y�2v�-�.���lu����.�����/um��	=H&O�9rK��)�#�#B�O�9E�`���q�X�������C�q:Fj�����>~�������������b�u
06�r{.zo�],��}�U�J���+WZ}z}�t��Y��0����Z�����H+I�P�_���H��O����������!:O�#y
��IW���s2:|���D2��%-������cB�s@�`�@������V���bN)�8g��0�:c�����X�|����u!a4oa��C%*`|����%�r��lM�>�o����=�Yc'���||�/��?|J�=~����.��N����{�v���������&�d_��9F��=@�c�@���[���<��+�'�$JX����n����<���~j[�|����hq�Y:R�����U�Ld/D��S�[p�9z��r���\`�=�������:����Kff�� ~
��J�������OLOl����I�U�KJZ��A�h|�����6QlS/�W)9�s&r�#
�����D"������w�JWS��D��|���^d"H,��C�k����c�%��A�~���5��X����~�C���A�2r
�������{��[z�m�����=O>���m��,&s���I���n�:�����D��R���`�!��j`�Lf-k�\xll", ��������qi���LI�
�������_}���,|/�,}-�M4����}�M�>����~���[Z!��C'@tw6�����D���X2�(� 1���X�D!i"~���������y���L|K����r����������A�z�M�����/�\Xn"L��G�������we�!W�C�z>����l����t��P�3���.�W���3aB9�Bu��G�����i	%�"Ue"���r;v����<����������L+��?�0�sI��;�<l�y�g��dZ���i�&L(OF�u�)z>����������TB����>������[Mo$�[���nXDj� ��{�1-�s��)����E��~��N+L���2��8�#3�e^i�u��I��<�_�s���g"��4��3DfO)+��
���X�3��Cz������}f�"��C&N�����l1Q�\�ka�y�go�0���Zn���
�m�^�q!����9�^'O���<��/��<u�l|q�u�X�O����02��U�0QJU����`c��x���<����������M��7*����p�
����*��@����&/����D�=)2� C
�"�$T�? �m=����?����nt�O/-�UH41�(��h�9t�A��>)���&r�Wk��K?���D�2�%�d�uv���Y�r�9�_>�N9�j�J��X���=�s�/�<�X�c��?�;����6�HN�I��*8���������*�^:���4�w>&�����b�a�������W�o2�<.i"�!z������=�����#5��{,C- ��s�����Fc�����m�L$2�8s���M�8��7���Kn��7������{�b�6��Ht�(1�7��4���rJ�����j.�����'���V�%�n'zu����r����/����L	�&&ka�y�����o���ob���W�LI��5Ql�IO|��2�l�d���_���}3�s�p.��\�����"9�j{��})��5�:z�>d;n��o�l��Ox<s~�j���5������s1@�5�<�:�%-U�x]`���+�eo�1T��*��Dl������{+aB�V#����>nu���9���m�&It6����	�s�W�<���k�W~N��O�1�H�����������I����u�OOK���O���,)����;�x���4���4�,�����2��e,U�[���G������wv~����8[��o]H$�����7����t)����~0M�^9��k"��<�����l�����'���w�Hda��'��E�;�l\x�%��&r����p���_���L�����+���%u���d@�����k������������#&�+�-�i������Kg��&
>�/�%���R�r��u���7�H$5=U�K2%3��5UW�t4����������KU�<�-a+M8�[<��~�+�V�2-@"ks�i���G��=.�>p���e�e��_F���4�����&A|����O���UHIA����>������0�{�y�� ��{�,��u���@��o������b���M�e��0��_�;m�����%T�	�����p�����7o6Q���+����p��d��=�m��p.�����>����.$*M`$�=~�[[t�����n5�����'v;�d�,���v�DN��eq���0`xT��8�y��k@�Z������Md��)O��
���%��-=�6�Z���.��n�6D�����V�P�GA��?�7����^K�5�\��'���\;��h�$MD?$������q��~�#)�9R��Q�2���_'�u��vq�G��b���m�K�/`��*��/?a��NG��)i<��D"sg���`��"��� �t�Z������3C���a��5�XX���nx�=O�kG�L�����"���jx0B���|������K�$�0�D���z��1\������xx
��s� ������[M�v6�,��.�8���pt�s� �-	�����4���~;"�J�P��p��n7-��;1b0�_��Tv�m���5����`2i���v��x����u��v5	0
�O�/���e��W�����N	�4�|���y���vFH�=L�XY����C,���3-�=��cZ��Ry��M�V��#���'Nh��^w���c����r�TT�f����$t�S�"]��s�kO�z�������Lk�y�ts�-2-[�����=.�>��PI�V�2������96����P������^��������.Q�s'��$@$w��{�&�_x����G��W�z�����������W��Dp�i�!�5���C��Adm�|���t���<�XX�`�@���9n��S��s����$����%t�-_y���Rc��sM$�;L���8�����PI�7��X���9z_O5X�J�s�������O[D��/t!A?z��S�����U��J�����t��m�r���:;��P:^�!y�{��V�����:$�<l�yH$�������f�2r
%��<xL��;�v����9S�/�uw�����D���:.�9�PI�o���>8G/D?\+}������j�E��=�O����%��AD����8����#�����U8_R��M�Mc���Z�$&�<�X����vPa��D���$wJ�d�O�����Qh�5�-�����<���K?hZ��&��	��l������o0-\B%M�X�!�������Z?�_|�D��_���Hp�=�lN��O�x�C�������L�Mc���'��@�b�#�5���/1-[V�4��G&��x2���N���U����}��D�{����O�|�k����>�������H���*o��	m�&��/���m&���S�(�����h��=�o�n������<��r�9�P����I]]��D*++%;{���'��u���M
9}�S�n�A�������I{�oM4�����E`�TWW��;L�8k��q����5�<�xt��?��f����-���r�=�����j���Sr���?b�#s�/�A���98�^.w��M��k	�41Z���D���c@"���Qm��P�����T�J�Hwo@�z���0Wm�MG���&��i���5�?�����f����y�E�|"M��QQ���:c������������c�:�����D������|XF_w�x���H��oPf�
?��������D�i���W�Dp:��A�m�q������nD�D�'E�gJUQ����Knz�u�x��L�^�y]G�n�m��0��&2w����"e�v�DY�kf�zH}p���e�c�&V~n�i)I���N!�q������YS#���X�����I�0*�4�$8��'�e�Y�!YE*��HEa��lu/<bZp�S�<`Z��r��<D���Q�`��oZ�����am���2�����L����*pff��+�{����O<,?���k�/����W��3#`dH�`ty���-9b%L�h���q��;y�bZ���&B���q�����)3-��E���&�^��j1Q�|����zd���3�)�&�`����g"����};v���z��@�x�v����c$��t��0���l�i���OH�7\I�I���S�|L��M���m�l��ARRR��`"�����lY�#���}7koo���6����%���d��0��E�$�< �]q�<����&������Z��8!�����n�5�~���t>�C�%M����5kd���r�]w�^G��L���;�������6�v�����g���|Gx���w�k�{�1�����.3-�w���N�
��Y���k�X���;e���z�m�k?<�[g�����b�@�H��	]8��e��H<)���\d"���~��s�����Qgg�<����3���C�����\	~���Z�t��u���4�)Yb���;*"�.Gr��������$��������/?��M���kW[�?���m,_�\jjj���4�`oZ�_������w�iZHT>�O���L$RYY)��#�e�`�����m�L$2gJ�d�N�h��p��D"�/�A���D��I=��tw�,K ++K>��OK^^��q�#��4�h"��3D���k�v�:f���ke���o"�Luu�����D��v���f��������~y����s����4i��/�����5�5�k	�4�%*�o\����r�%���� ����)Y4�L<��{�������5&�HK���)�:=qB&��{�����mY��'%�2�w�[h��W_}�D"���RPP ���V������]���U7�;�<����L������_^d"��4����'Nh�D]��1A���%9%���	�&&k��5 ~��M��J^;�lzBt�C�[59"�,�t;���Qm`�%l�D��I������c[^������#M����s��R�����s���^����Qm��D��l��f�JV�G�{��-���j�{�R�z���c��]��!���RRR"Od"I �����m;���JY�$���[��}���D�Z������d�x}��M��1�z�L��p�H��8�y �<��ih������<^���d��&�e��'-��f��}���m���,]4M.�_sM4~��Yl����Z�\�410������~��y����u,���\>v�b���o�"-���hx��V�����D���c������V��9s�i	!�8q����*N��;W>���Xm�y�������&^A�{��+�7g������ba���d�W�X��i�Y��_.����}z|��l����mE9f��`�W�/�+��`q��y`���o�G�0�t��O���#FB��5aB;v�����#V���k:&����7{�D �r�HT��0/�`��	���m]@|J�����W� Qh�����_�������*�S����N:^�%���^�i;e����������M�^��D233M+6�������D��[m,�~��.X+9%���h���}`�LcKk�X�$#�XR��~���:.�9�M�3t]L��J�X�j�i�l�0�7.������
��t�)�������Sd���g����z=D����G'��@����I��?2=a�D5�q:^������r{����d���/����,]�+���u�X���\hF�D���1����W"�S����+
>���Wii#�2�9N��}����w������k�.���~&���w����~��V[�.�k`b%T���w����w�u�u�����U�����i�y&����
^wzl�+��N�����Y��D���4�(����������yn�[m����H����Vl�1:����D��5��\I�.�#'\1��rf��iY���,�;#>uvv�O<!�<��:t������Z�t��#��&�=��cZ"k���m���o�n4-{KM��&T8�����������c����D��=2�$KffHiN����Yg���dZ�Ct��w�i����ji�@ `���5�v��O7-��5�,G��)�����sc����;�����m�mF�D��$���TI��_r���=��>�ii5�����p?M�x�����W#_2����$N01��U�k���RSS�����+W�4�������h��Hl>�O���L$RYY)��#+�`|������Y�S��8���m����&�������o��=�q�4��y��/���������~y���D"�_&���������)B�zDIII���p����������1���JY�d���������f��WfVo���a"�^uu����n-Q���V�y k���=� k��k�-�TY���5_k�������+�-�i�&:]�_:��4�]qS�*�^�t5��Hd�W�/��������#�	���V���L�����/�����e=�������3K��W$M�-����X@���.R$����;�-�V;9�47]*
�����W�z�vf�TYq�������p����L��k��T�W��"���� ib����h�y�3QI����k;n"���,k[���,]��w4H_o�e��������9�H��?�(@�q�$��c�x�����-��7��E����t7}+aB������s������e�]&YYY&+9���UN�<i�v&L�X��V'w�wZ�DI�HU����������t�V���5H�������	����qBz}����:k����	�7^	�O0-���&���5��~��%��&X<���[j��Ub"-C��s(�q�gM���6i9�#�T���T^^�|�����GG��X��F���r��%GF���"������f�Ev�����t��<|�/�$�< �d��'-���z�m�k?<�[g��<i��<$�c����X5K��k:&�����KB%Ml�������_//���Y���pN8i���.}��>�{�����sJM�]4iB�����+'��7\ZY�g��4��h�n���'���g������s�k:��	��+����\�(�_W�gF��5����@"`�OJ�9��G�����q9SfY��<�������H���t(�1�U�nU
���tf�&��LI	�0�z�j��y�������O�Z��w?a"��O���rI�<�7��P��{��G�'��V���b"w���I�_�7����,������������<K/�L�}�/��L����n��������j�ZQb8oi���9%Kd�����sO�k*?n�<k���=� k��k��jRC��������#KR<��'��R'}=�_�'-#Gr�+M6��@o��eE��x�>kK���_}�\0���Ex ��3u�T���1��4�B�)
��g?+���&c��v�P�&�������y��r���M|h����^���K���z�����`�3aB��5aB�]|�i���{��'`�H]=�R������n���01���V����8F�9N��}@�a�����Q�m������;�-T�����������*Y�#x��I��<���������w��gM��oZ�9��\&_	�4q�%�� �}���R}��t�����[zvn��t��s�i��Vf��#&�J9����m=������>�����n�z����p/o�K�e��KQ����k��]��l�����k�������@�_�;NYm��I���
�*�.�E��<�g��_�o��<�����4!������w�6m�i��'��_�Qz�����#(=	�J�&M�_XQqb(:N�'�����U.2����'�m=r���:k���u�-�t�i��>��7���%�>����gFl��2��G%��-=��k��/��A�#U������f�>��,���?��p�B��������A'�O�����/	�4�{z�l�����@+G�������/�gEn�1V����{�s}�	���TY���K�e�4=C�q:^���RR�$�d��D���!b�r$W�|�H�y@�����*$��8���)��~���Gz���M�pn��������_HZF����,�>K�,���;�D����.9z���������mk�^�9:������sO�u��� Q�[5E��S��#�z�l�f����[�G���^e��<R��e��� ����/�\r��1Wl��X������H9�o3-����N���u\�s>��y@|JI�HN�LI��5="���vV��6�=���m9"&r%�h�����.��2���2�]q���UN�<i�vV���:���J��==Co^l���7/ �y<)r��r�������O[��/�����S������������\�/���lGb'Nh]��S����H�y@KI�����j^��vo�Nu�O������+//O>��OGT��E��X��_���y���V�\)k���
6X1n��������3�W����:�3��O��1�7���j���8�b���~AIf���\d"�(X�����_vQ��g������k7��z}�x'L���
����4	�����+��R���'���
&��;w�uM��0������b��/���m�-�B�]w�eZ��{���.J ��|>���3�Hee�dg��,���g/�����hx��s�x�D����j��c��c�@%����ba���d�Wm"��)U�I�3������(�ST!i�J�z���k�7�M&�g�h�z}��\k"��G�j��Ommm���"�L��AB%M��a`��{������}����D�ie�i�a"�I�5���0:�4�:�K_��l���%=��_|�uw�����l�i�W2�Dc��	��9��$
 �M��Yt�.)]�VrJ��^����v�0$������
Q[uhE���	M��
83	UiB����L|�u�M����^)�}���zL/6��'���$�tY���4�������a������DU�P=�f��8e�0�v##�8x���m<TV~Y���=*M$���V�\TTd���p�$T�]��R��#�q����gzE�*I���e����M/�� i�|�y�3�I��R'��������{����)�4�� i"����G8 ������/??_f��!,���[}`�8����IV�e��|�9�mSD���X�w|�Zk�����k�0;a��6�"�7�	�o��s��SO=e%M�&���O������fW���3��	&��o�"�6�g���8��V���w�(���D�5N�#9��q��������ct��I�����>%��XvnL���z�u�X��M��H�IL���o��l���Y�.�J����N:��F���'�N����F��Y�z�&
����A�j���yH>[�n��'O��VPP �����m'�s�������_^$�6��i���i���_"'��/q�4���"��m3����e��5&`�t����0��I�9��RU�)%9����j�5�3%S2��Ct^��&�����%�?�%�??"k�s�|����Yc�?p�7u`"�����Kw�)����^zpV�KI	�����zL�dP[[+555&������
)))���\��������s���g/���;M4���q:��w�&V�\i=�O�B�~��l����0v�n��i�fdHAVdU���L�Tf��V��#����������/?�{����xs������G�����5p�o�i����(�HzV�i���?�&��K/�dZ6M����\�Q��������V�h����l%�6Ue"���r;v��Z���WI�W�������6l���]8��?B��	���q�������0����:�� d`�_���O�s/���M��x�_�y����v����
�Iv���y���9�O���M������D���#�ID�k:&�9�-�M���!i"sg���.RV,��<���g���AZ������q�4�y�fY�~��D����	YH��G/(�X�c`,��'�u�L|���c�8�������W��|�D���t�+�����g��t��M�k�"����!���%#��jgd��n����#�(�Z===���d"���p�ct���MN���i�*�E��LE�+�L`4����q�=��w�)/��b����}���������p��~��0����kZ�����L�D�������+����4�HZf�W�J~Y��dHv�����3r��!/�z��_�y�;X	A�99�3$=��D���+�b0�>�i��Z[[M�6T�������hZ��m��i�[p�J�)^wn����X�����v��/���m�
](�������X�b�u��������[��s� �������������D"������m"�/Z!�w�>�D"3��$'����4y{�h[��D���+��:�����'���^���D�g�Jzf�������2���s�-���^����Zv��a��\;L"|��y`�X�Fg��Y��_�H$wJ�x�������X������*�?���o��S���&�i�E������IWs��D6~��r��ra�
�7��
����I^��Y����)����U�H�}�scx�����c8�ZDjO� h��^IIM3��\;����}CB��O,$6$�_���-:��=RU4�[��~i��[r�U.��_~�j'��[EEE���>��k���^A�)����,�=���n���e^�����������&�kk���w������v�&Rd@�2r%������k���opF��@zV�d�0�� i"�|�{�{+	"??_JKK�v,ZY��������D>���Xm7�j�����;M$��*��t��W�����Z^�G�-��=��A�m�1}��lL-9��$�.��Y<L��Y�J�����!b����	���V{���M�6����/>��uh[����Sm��J�PCU�pr���z��b��@����	���)����*IE����L�Dxc������?���<����~��D����~+aB9��AN�2��yG�K����-���l$D�D�%�\��B�>�;�)������Y8L��K?nZ���i�<qB&����U3-�����=��<��Sr�����mk�^�1:����orJj����������~���K�?��]JJ�3�A�
�#qB&������������wZ�,��Zu��'r�Gi_ss��l�s�n��S��Dv���r$W�|�������c8��
��M������*$��-����Ld��:r3R%+�#�������0���p�T�^k"w	���PN�<iz�6u�T�������[���El�Q�!yS����c{����X�Fg�������^�z����_�����^�t;*����IM������K�Oz|���-+��wZ�c��
���h_vQE��g���������?������������D{{�+�3�e�]f"�8��OI���&�;C�(��h�9�(�X�`��z��M��q��"i`@����[�e����)^�B�|�&r�3��'���^����_/f��MG�n�s4����s�-���^���	`�����X&M�����������&���I��S�-u�����#���#9��V;��mU���9!����8���I��O�(�k~Y^��E&���>3���`�m�"�
"=�&���]�S���p�$����&@h����qnN������eFff�TTTHII����Z���O����$�e����l�|��s�*9�v��N���d��"�	O��L��V����1:.�0��>����
�?O�\��&?�]�V�.]jzb�1:���5��\(�L$�>�j%�7���j���8�b��������y$L�I.��:7l� )))��5k�#T�s����^�=�jc������� y��L�Mc�����\�%G�K/�dZ6M����0Q���5���@2Zy��r���L��&�K�1���m�_����-�]���K�\k� ���`2�e�Jf������!�E3%�N�-��'s����.�������?���k.��r����e�����o����>��ct��M��)�x��lMvU�����);v��S��kl����A~��-&�m����y�f
M��[7���_|q��O�T%7��L���+�/8�}L����=ijj���PPZZj�cill����Y�(>���Xm �
��??r�9���������~����B>5���=&k6�<����9B��;����GE�����-�������N�^	�vKZV���g��9���G��r���dtr�}r��N���)4�����.���i:��|7��T����+G|_��u`lh�D�9%U�DOO�[	j�
��ct��Hv�����~_._��34��I���<�Md,a���|Z�DFv�d��[�z��I���hz��H�@b��k���g?��|�;������c���c�Y���V�Xt�.)]�VrJ��^����v�0W��&(}+�I����4#5t�_���w��4b	-8���,"-_w����)������-3s�r���Zy�H�']>���._����^*��T�+6����7��5N�OX�0����V���>z�������St��t�)�������SdU������q����t�`��C���O�����O�3�<#����.sE�����k:F�&����d���/����,]�+���u�X���\hF0>��F�����j�u�]&�et9�X�%��
w��t�a2P�H�B6=�O^�m�#��L�����rn������%��^$����o|�D"eee���g�����GT����;FT�HF��>�ozS*JfK�Y�&�\���5������[�s��3���.�������}�N����h����s�M�x��������-++K>��O�>��s$0}h������{L�������f�@�"��s��
����/�����3������pD���X�?�/�X�t<��V�())1��h����%a�M%�N[D� ��� Q�C%L������:��V�����G�w���JQQ�L�:�:��}!:V���G�D���}�i��{�W�}=�Z��j;E�7z�!��?���������+O����bz���n����<$����[/���.'�:&�9��5�*5md����t����d�k�.y��WM$���+3g����b����mk�^�9:�/�&����gs��A_�H���;�4��
V���}?���v��D���B�.�<�[g��^;�d�CrY�l�i�t�
g5��kn��o*z.k��yC�r�T����t��g�����e�z��=���x%n8p���
Z1�����0��k�����7��0��']
��L�&���tZ�b�i�=}[B����c��m���vi�Y��{V�4I�.
�Y���*�z����HUUU�b!Zi����J�����mk��
����H\�y/�i��I��#=������])�)z��g�����g��c����HAA��	!zM���8q����������d��������W���k��i�	�� i"�D���7#�m���og�X��KGL��U0�zx�'#���=����uO���vihh�m;���J0 ��� ���x��/~���Lkh�qMl`w�����&^��D"���'�8��K$��!nrb�}��//�����������%r28��B��K�6l������5k�X}�)�M~q���7N��HFn��o:�u�����oS�]�V�.]jzb�1:v��3��X��R[m������i���u����	w��[g��I�?{������:��$M$-'*)���C���+#��T[�l����x-$���4��3$�c�:��9�E��6x��h�q:_������.��r����e�����o����>��c�b���5�&3g�i��
�#qB&����2r�/���t����\����9F���=�rD{�oMd+��*YX<��������5�&�.D?���	��X�~�B5��|&����UU"�o9#5���7v[�d�x�b������o�[o��:��}z
����x���!Y�e&	z��Z/������*�^�u����^�y��H�������6��k>z������M�=�M���!i"sg���U��Er���";���t30H+S��_6cC_���:-_�\jjj����a6z��;������p;��������xp��M��71�!_��<[�-^������9��|RWWg"�����
`|}��'�T��_gfN�d��[��tw6HOW��.+��_l��j�����r���Jg����?`zEr*H^�y2�]	���^$���>�n�)YS�JJj��$����U�+����c�%��A�~������k����F������H$wJ�x�������N�z��6����#9��&����\k"����29��Ra2���G����en��������&L455IWW������\����?����������<��S��1���L��������I�E�����>_����n���ka���x������\��h����P.&���|�����D"y%�%u�7)��������D�-,���"�WG�������b,����W_o"$����s������:��^O�"I�;_�+>l/4����u=&
I��5k�y�u�h�|�����;iB�Mz[�$�c�42OF��U���������r���Y`"L�?��O_�SSS��;B�p�%�V�����bUYY)�x�;L���\����n��%���q�&��������I;��d�y$l���Cl���XtA(��n0�b�^��{�u�����
�x��2��x ~�ma�7���1��^�;E�w���yD	J��x$������������	���{�r���������5�n �?o��a&L�D�����6�Z��|�,Y"&+9���UN�<i�v&L�X��:���
��c$������0V2iBK0F?�G�F;?^���	}��������H�Z<H��X��K.,�K��S��z����V�;NH��5x�[g��
���t��i�	��]&�g{dfa��+���;�x����{�f�9|������� q�����x�oo�@��D���B���*��S�sF���`*���yH>YYY���1�cu��:���-g��`��E����i�	�=G���U�VY�[:�9KU��^�%�l�h�CF��G[�2����_�����/[�`]�����"��/���q.W9�������?l"�tO��,�����$	��H}[�����.���%{��&�[5��m9��6�M�V���X������S��_��Rz��Lp����S�=��b�#�5�0�<��y��F��{�7��m����.���&IK�����A�,�0��l�xY$�p��g�����s�����y����`��=r��A�������rrr�����{�,^���s���>��?�����D��M0�S�"��|�E�.)�i&��d�y$\�D�Ct�����s!$��g�`c�U��>�E����,>$�����;�������/>�C}������/�~x���%�R#���{���M&�S�9h�D�&Nn��2��d�Mg^z����[�=.�����V.R�g:Z;E�6���LG�y�-�S.4g������k�a���d�Wm��K����K�?�0f�������	M�}~�lz�D"��y�]Ta���4���������g"kkk����OM<�����m�����!���jcz�x�����%�����g��v�P���Y���9���W~C��:�)gB-�t�!��V0�4�?�v��=W�{�>GfM/4Wlk�C_�����	�q$�@�[p�0�
2=[u8���N���i�b%L(�������~���5�3������l�d��0����q�Gr+,,L��	�S�6��!�e���ug�M�| �����Zg8S	�[�������;���-k�u�V�B���<�.���m
Z��Q��t�` �|g�>��f����s��������1-���X	!z]��8�������b������p�����U�p���z����:�I����M�g�J�h��9��Pe��fZHV���o�W������\)_���Zg���H��f$�LB%M8K-��[���������2Z�}�b-���
����s|�����{V~���r�x[�7�01`����y���������e�J��DH�����=t�P�J��e�p���z�X�`����tI��uL�>����3[f��n��g{���m��=�i�&�������mk�^s;�N��:\K�=t����T�HWw��b���=�����y9%����5?���	���O�����k���#�PIN�� ���h��b��R����������F������+O[���`��e��Y���q���{x_2-[����}�X�`��0�-�;���~rL���Iq�i�B_����^p~wg���?�C���b�?�������zJ8 �='��}zM��X7�v�RP�^�t�����ZI rK����!y����G������t<�D�&M$+����yc����������v���(�i6�-#�P�����Yc���4Y��*%�#y��L$��3���q:_�w�)]fZ6ox]mH��-q���X��H����{[���o������E�Gr�q�F��s���M��X��f�^��U9b$�2��G���{��a"[V~���fIqE�u��I�Sq�H$e���m�L+�D�����l��C���:�x���,8S�n;(����D"i�Y�;�*���4I�.
�Y���*�z����n�?�|�i���?�C�^�q!��p���4�)Yb"�,�Ht9�+t��$�<X�09��OH�w��w���HnZE����&�Hyy�uh�I����R���U��(��s$��izmk��w���L��u����
���R8-[
��%�0C���������Ct^�)��3�f�A��/���m�-��]�Q�8���*_�����-���7[�D��;�X���%+W�4�m���`��m��'uuu&�������20n��������H$�d���E>�8�[�Mo�H�=�f����D��u���|��d��HE~�����Gh�D}{�����~/����W��Dp�#��4�h"��3D��L0��N{����ke���o"�Luu���~�+�T"~��y�X�8k��44wY�z�h���xx�����"�!���'M_
�_��K��[z�m���������k����93��s��\�������Gy�D"���RZZ*�����#������+�|�����*��@���|AN����������������F~���{����=O�H�������N��^i;Nd{��W��^}����\;H��	����Ax��������`���od��.:F���.QkQ �s������'@|����IC��jg�KV^��Jwg�[�*�������j�Q���rh�}&�g{$7#U��=�����pV�Pso�C�V�5���������L$��&23�Oh��	M��k��3A���%9%���	�&&k6�<N�����_�o��)k����?k�<��I�J��=����o�>���1�J��&L;~+b��Er�����=�e�oo�.�3<������&b��d���o��M(��C+J���'��V{��s��x�j@H�&M(��`oL��0��w��]9��e8���gr���p3�X@���Sr��~a��CM�I��7Ql}��m?>�������%�]��EZ�m7�����%�����v'v�'�5��%���oq9Y"^��uGS��h�~�Z���E���b����G$�<�����/>�C}������/�~x�����d���}O�������|���P��DG���_RR"���g�����w���{&����d����~�������B-R��3Ru��� ��O|3��\~i���p|m=���������xRO�� y9���3}K"D�V����10}@�1��z�D\<P�q���.��D�.�H\��Q��d�J �r�H�8&����YP�^�4A��Adm�|���	O���5k7�-7B	*zK��8��\���4�����	]F\X��D��DJ
���*��!���t>����*-*-sd������N	�,-����%u!������@�O�K6�=�De4]��Nt!D�>������1����������am����N__x�9���*!����/?)�/�A�*�^���_��_�%G�:�����#Q��@bb�#�5�@����+O
ReB�fke����:���<m�C�hmm5-[�m9����k�	7i}�I��UM��2A��l��S��dq���:���i���}�~�Z�����SB���������2�s�K\&2]����X:4�"��q�;�B�;���D������hpz����D���.�a8��)__��V�Xt�.)]�����Ic�?���� ����5����Q^;�l"[FN�d����Yc���4Y��<��"��z{{M+���9�m��h�
�m����-H���u\�s�������{t�|��_�-�z�:��}z-�����k���!���������P�V�>���s��x����f������z��$��>��S�|����Qj�v���I:��2q�
�������%+}+#���d�Sr��z��e����k{e�5��k���H��X����������M$���%�S�$+��g�B��qnq�u=D��|$}Q����D"�����s;�;�-=�����x�MdW�	�8���q3�H���w�7Y#�_���������O�}���IQ�f�����7��+��������q���$l��
�-���&E�71���o�cG�`l\q�<�����}����R������Z�G�u��������t�����-rh�}&�����b��=�i�&�������mk�^Kf)�i�[����5�o�)�
���i�I�����l��S�|�[yy�i�ttt����&U���\7H�,6-����O������y����������:��V/��i�:��1'���1����.���`p	�4���&��H}�������;>�N�?+�M�z�m�k?<�[g�����b�s;���/r���l��,L�y�Y�Yc'������������zJ8�P�m��k:F��k�l����4�HFn���e�hpz]���|����e�L����QM"D���#�|����<E��Sa"��i��Q�V�g��1����7{����-�UfA�����m;�X��fUe���w�j"���~i;������z����:k����p���U�����	@�JMI�|�*����M��t��v�5��:��GT�H�����L�*����t�MO�����)��!:����K ��7���;MOl:F��&�����������=\-3-md�8��|����WUU%���&��D}}��<���e��>g
��s�&�<��8�,�f�������9�m��yJ������_72��`�9�[<M2r�C����Bt��u��WT.���D6�����[Z�����%���:�C�`�����?~����{����}���^h��4����t�5N�����?1-���)��aA�G*
#���{��r��[������uTAA�U�Rm;�X���Ia��[�j�L�:�D���vihh�m;�X��FE��5-[�I���������������#g�������kN�s����{D����8#�Os�	���)9�������r�n���=Vn�HG^1-{K�X	!�8����9�-jkk����D"���RQQ!%%%���k��>��st.�Y 3��eZC���/��������k����KMOl:F��7*9�R��3&�����9rR�T�HS�}�X��zHip��w��'�%52��%-3v%��cB�s�L+G|����=^%��Fn�������Sa���~c%��/��EE���a�i�9������I]]��D*++%;{de�L��<�Gxt��b������+������O��+�Y�nm�1�&o�m��{�C�HJ�{��q��o��Df������*�;f"�E����_o"0V�$�����e� Q?�h�y@��������^���N�������6�3����Kg��&Y�h����er��r��d�g�9p�������a����[k!,�����6��e�oo�.�3���%����H�K���z��������;]����hp������q���K�I���Z�����}2g�"��h���s�$d���m�d��u�e��sv�O@|���6K���/��S}�y�kL�>�/n��~|��D��fIn��{]��r�1\�p���I�9�(�}�{����T�PPZZj�cill|kaA+P|�3�7�� ib����h�y���m�k����D���$3�\<��������� }��g��|�]r�%�L�d��jW(**���d��G�k� '�|���6u������$%5r[79|�Mn}�7&)�v���&���U���M$���\*�+�N���k	���f�Y�r��-��V��N���.�����;��:k���u�����	��7`ZC����E�J|===o%L������ct�� ����C��t���J��j������k
��Yc�w&L�<&����0�4b�;�%���{)>�C�U��\�i�����5��	j��B������0 ��e�,�\g�LB%M��,@�x��9bK�TO��UHV�t��)��������~���������z"nb9m��6@
�Uk[�����V�HT�y@��������JLd������d�8n�5v�?k�5@X��w�9�>&�h�\��Yxm�u�X����4#�M��f���H���3���pB��M��9`�$T����tZ�z��_��*5y6`|��7���M�T�e��(Rz�?+x���-�����������^�qnUVVfZ���^��-�{�p�y�3�< ������v��x���gh:�_���`p��y�[��:'�9��UG��C&N������t���L�9xl�u�3�p�s���������;�4=�x��p����C#��	��:�o�����U������v������H{��?�k��^wZt��^n�U#JJ�o�tw���cqn��sG��@"`�GzZ���G�.���
y���������5����t�5N�@,W�}�i���������	��6�0�-z����:(���^���o�O|s�|��k�5��#
���02��j�v�|�r����f�r���m	8�|>���3�Hee�dgg��d�e�M?2�HV�Tk+���xu���&��������
��-����Ld+��HnF�d�{��7`m�]ab�
wH���&r��\���g"�3f���C�*�?n"�E����_o"0V���e���vk��v���f����$_w�>�"sfKv����?�[�����l�%��a������=�e��V���}�E&r�_��T���&���w�*������t���Z�b���������h���>eZ�����V���	G�z�`c�u�N���nL�P��-3-[SSSD5��knn6�-z.�E%�;���	�Hkk�u!7�o��3��D6M��l<j�	:V�$�{�#J�P:N��H�4�����-��7��E�����,����#��t�[UUUY��!~�_��������.��������s��={d��Mr�����>h��>�����I����R�b���m\Q=��s�N+L�}3\MNe��K~i�W�Xg��t����$�W��������m�L�tK���JL$��w��{�q��?k�k��p��|�;�=u���e�^������Za�i��U2u�T��������:���cu@�c���-��{�9y���������a����>��c��+�����g�^*���jYq^�����������^���vG^��0���*�����<[�3$=+�:k��z=D���:h"\B%M�X.a�n�:����s�I=�v��w�hp���:.d��R�r��-e�C�(���e����"�X����u�v�G��]+K�.5=���sk�[ ��7���;MOl:F���d3��']
���t�,�������|�
������d����L��_�)���U%B�_�;m�����%T��%�\���[�l���s^}�i��
Vb�`4��'x��C�;���k��n�C��3Q�`��:N��]JJ�\~��r��W��$??�\	>����t��p�<p��[����'Md+((���r������9��������d��������W���k��i�����ZG2:|r�i�[r�J�����:��`0	����w�)�W���+W��5k���qj^e��v�riF}��Z������x[%����{��z=D��[5�D���#Z�m7��8�#3�e^i�u��I�'C�	�x�b������o�[����	|��>���%Y�w;v��!�!%i�K(�-,�]�e���R�\����.J����M�@��--
v
%�J !	Y�8��[�$���;s&���$�4~��3W�=s�81��9z�=�\���>9GDDDd7\� ""����Zl��AE���FUU���������>9g�92�������X�����M�� ��oyd!�q49�B��&�;����q{���!"�'��&��5kT�x�B�I�C=��h�|����dA���G_w#z�j�W��B��<;���Qa��H��B7j
�(�r"����J<��
�v�$�z��������@?������kDDD����o��A$\.����O�YE����O���
7�(���8O����-�e�p'��f����Y�d��<iADD���o�Qq"'��n���aj�y������T�G�H�=�j��p�����~���[�����j���u�R�\;��]����P���kJ�y5�k�[�x�LA������e���Vb����CDd�rI�x OZ���'�&��p�{�U-cK�x	&I��n�a�ODDDD��5"""�	hmmU�QMb8�12W�a'�����&W0��>(-�3��#�~�S
�He
_��UD��#��i�sT��m�a'��:DD���S��X�xq��Z���+q��7�(����PYK_Rj��|���SP]]
���""o��x
6l��"��+����d{;W���/�"`j�S��c8�� �vU���w��7�DDD4��,Y�7�xCE��v R���5��kDD�M*E��w�SPYY�������FKK���+��R�g{^�Z�_�"#1� G1t�;,K�%G\�i'�^E4�n�mx��O������=��Dg�OE�I����/�IEDD��AJ%MD?qq�����������D�w�1�`-\�<�gx�p�
�1��C����d����}����s���x��*f�d"�9|����v���Xt�C��y������F�&��<(�y���{���{�UPZZ����������\r	�O���������m���e�(1�����n��U��/x�hR�m����p���tg:r��1'$a����Px{�[/�����*���u� �����Qj�^��0��p"��
�yS��*����_%�h�r�$�v����^��/P-C_0��r����CDDDD��kDDD��n����	��~ &���8|�A�Zv08� aB�V�����r�<jJg���]�"` 8�W��j���@��_�X��	2�	D4��J��������g�S-�;�����N�?S;o���wU�^dK����*z�7��X��|n�ADDDdO\� ""�/�z����h(I��1v�myS�YIO�}���/��/V���;���>��{�W��d��#"N�&M���uWx�E��C#��
g��+\��nr��Z@�/�.�jr^������������(u����$���������J�W-�����F���M7}�'�q2��()�4�����MD����	���g$�6n�mO�v�A�K�)�����+������0!�����Y�"""""����=,[�W\q�~y���z����z=y���W?�-}�*2��/�����d/T�;�n%�^Kr������$�#n��^�t�9�V�����������J��==��(5l�T��[���eW�j�K��#0�S���bW��~�z��
���j���M2/�z���������ADDdOUUU�q������\������������2��o'Y%R-����QAr^����ir�V6�_~n��^���u��%��_%�����j$QrR*i����]�*���j��R�2����j��};�Y~5
�/U�A�������>���%���2�������kDDD��p8p��Wc��E�'>#ce��-��j�6�O�������V�G]�ZD�����]�@%":T)�=��u�T8��3X���h�r8�0wZ������6(���;�H���-���(>�c*JL��x""""�'�y�[ZZ�<�L��DMM��5dee��#����kW��cP�d��?��>`�~���������wh���i�6/��X������^�x16l� �	O
���0�X.]��n2N8���T���PWW�"����GED4^~��W��K�T���T!��������:�U�{�\|�s�����W�}�z�g���JL� ""���d����*J���T�s�yP4�y��;���M�6���V�I�3^'�>�h�ko��:
]���hxyU�b�Y�������X�R*ib�2+���Sj����������������28c$N��{��nB(T=����95E*��������w��P�q �����t�
�700d��Y���[t��`�������5""����k�����UOl�=�T��s�	S��w`���6e�He��c�W�������DD�:fW����zB�T���������O����[&d��&z��F$L8i�Q�FM��YN�x���3��pY�)�y�{�WMF���m���JDDDDDDD_(�����M�2F��; ��o<�;��W��'""
L� "�Q�������*2|]��nDo[��*����yv���T�05���L��"����w��P����E��������������q�[��Q�Ub�����F�D��/���QE���<�������d������l}��h|�gzI��4m��>^��4�]�2�s������{�>��$����;������H�	�'L�7~p>z���m��C*J���`�����X����'do
o���$JUN]��,UIDD#��s��yP<\� "J}�������0n�%%%p�"�	hii���W=���^������W���-�Td(���@z00x�oCK�:���_���OEDDD���v�RID�p�(5��a7��/�Bck��pyA�+������?���LW��58�K_<JE��|���pZ�A��oar�o�EZz��d��:
]���hxyU�b�Y�������0i�h�q���(�=��#���xxFTVV���H���}�T��?\p����u�}����*��oEu����:,z|��&�7�G��'���9�������X��=�	�4q��/�v�J�0��iq��/�e��u���Z�Lgr�����C����C&�s��R`^��Zf�V2^*S������Z@nnn��	!�d��:�.:v?�Z��8	"�c����?Y4wz����F�&��h�]��51���Y^
2N��Y���e���n$=.�:d/������%�+�U	L���	�F,�n��I�����[EDDDDDDD4^d����Vi��Q[r�b#s�v�m
W++��$L����3Y������w�[����'p���]?��?����#"����	""UR9b��pIA���!3��E5���V2��'dK����*z���u��������]�e�.
rTE��j���6��ZDDDDDDD4^:::T�����)-���jhiiQ��������^EF%�dX��|�����������_<�&������>uF�7�����_?'cd,���?�;��|��WU�p�	'��!��H��:�z��'�����
���p��t���e�p��7���nv�0
��������B���{o��WV0���<w�$�.����7N9�S�w�-*";���q��n����T����@k���*^���eDDD������k���n�y����Qj�����"���99q��P���"�S\��IU�H�@6�^��3��RA�����*�,�h'��3Td/���_�Eo_rF�3]��u�Q�3L�""�`];�PI����f�*��H��:�z��@41����������T�#a������#�i~�Is����s��w�����O�H�^HCU�y�C'$a��+�@�����>���y*"�����N5����inj�U�Ytui��};����&F�<�Pq���(��Z��@Dnn.JJJ�v<RY���x*���+V���v���ST��(�;tuM@�*��������5���6��K����"8�FRD�����6���]��7]�M.}A�[w��x:2�|�DD���p{""5[w�K��&�T��n��uW8��n���Y��^E�"v��Q��G�7�����*�T��&L�<&L����M�2d%y�=.�:DDDDDDD4����{jJ2���&����@��������%�4��av���f����������0���G~�lx
��uS9�-}r�$sd�d��n�|�;������������*���y"����	""������z����>������U��}���Y~5
�/U������ v����[�x�G��Ur�j�>�F�"C�u�������h�|���T��n� }mm��4=�
���Z��F�7N����y���v���p^�0�UX�t��*��'�d��������?�/�����x}����V��$�~9/�d<Q2&�����KUM<�w4���=�" ���$����>�Z�������`��2���}�����U_�1�1��FEdw[9�V#+�8�>Eo&�{?��F�*^���eDDD���s��y�����=<���C�����!3��rA*Ltuu�m���9���Td/��~	�[V��P�d���4``����NuR)���N�?���?~�*���_�W�H���_��WkAv&��o����hpp���J�i��z�7�|~t�=HK�3�D4�u�`B%M*. M<R!b������-�+�@E���������J8����D���W�}�z�g���J�q��G���������*fUj7�	v���>��M�Wc���W��a������=�B!�s�=hl�*�Gyy9��R�����D>����<0�y@d������M�^\��W�SV��q,_z�������Q���"{����x~�c*2x��p��NC�|]��%NYx>�:-�M2���v��*""��0wZ����������;���	�kWI�(�805�����*��352^����-��j�6��H�v����kU����������$?\u�U())Q=��k��	!���{�G]�$&�d��+�w���{�����w:
���[�AV���W�+�����7=��'"JdB�v�R�����R����	�z�����W	j�e�i���o"SUO�V|��*��4�(t�����,'J<�U�En�,�#2�w��*�:::�c2���%+U���J�G����J���F,���i�6/��XM.
���ADD4�p����hr�Jw�}7ZZZTO|2F�����|X(��d�8�![l�B~c��D�cd�]�L����2Hr�33vR��������ODmB%M,[����~������hb�p�Q�e�w7���HBE@;o����T-�����e���B^�7�yn��]*2�=�j��;����~w�y'~��_����O�M�^���SUdh�j���l��^���J��k�&��7��-\�K�xW��)�����9""���kDDD��/�0dk���<����d���+}{��>��w~�T����?���<;�]�����
!�S2Q���1��*T-����=��
\p�Wa��2�d�OD�� "�Q3���^�XE�Q���}]��v ���{�~9o�ysj�Td?�{�U-cK�x	&I�0����v388���~�?�8��}tw���-}rN��X��s��z��dHe
?Yh����F������z�v��������_>�QKDDDDDD4jkk�a�n�UUU�%��	��s&�#s�������IE��|���^n�Jl%���yv�GMU-`p o{C��	��s2����*�����}h�nV����8�/�!"��I6�r���c�J~��.Q�>{��X��RE������m���uK!�e�]��LO����~[��|�M�]H����Wc����'>#c'C�J�1��M(9�jd/T�����O��W��,�C���=��6|�z�{����2����RS��	�yQ�x��7U� 	.WdUQ!}r�*z��n�-[V��v�j�i�@i����7^%>��8oj���|�Z��X�@�K+��Nt��_G���*���O��d�������*,i���&!":8L�Hqr3�g --
7�xc�a�����r�����/�	DD���[gDT�HD��x;���Y��������E_����,++���V��*��XL;����[Xtu�>���*��{��Q#'�{�y;������+FN�T������9DDD�:��ADDv����6777��D49'cL��v���1�2����*���1�[E���g/����yp������:����a*+s�*�����R���[�X�����/��|�����I)Ln��f~����'69/������Y ���� �eq��&���������b���m7�N+����|[W�0�M_�Z��`r���E_'�%*U������L�*�IK�@v�G���h��f<�~����ySf"����|�����9����DDD4�q�����"���UEF5��X��\���x���?e@V��	���q&�|;*���o�[Qq"#ce����8R���������X���#���|"�X�4��d1@n���/_��o���M�'s�/�
V�]W�s���1�����	��G�a��Wb��>�����O�����H{KKw �z����@r[X��|����T%%���#���*��a�>9g=����&�y��ttt��!Q�	�$�Y����V��w�B�[�"��D2��d�\��$	��?���;��NA������S���1vO���9Q���>�b'NH�����OD�I)Hn����[�5k���n8p��V����<��,����[�r�-�ED��#
f����Q���Th���5Lv���q&�|�`�JJ������;���+,rN���s���h����Miix[U+>�}dURR�Z�/�����^�������_z��o��{��l���.[���,�u&NYx���~���{��oW>��W��_��NYx�>��(&M����uY$8��TI�����W�R&��)���X���� ���Q��O�T������8���0!�����Y�����d������"���i
��+� ""���kDDd7R5�Z1���O����q���������Tt{Uc=��2��;yL��A����<���V>GE_W=�}��y��J�_y����(>&M������'Z<0��V/���jE�^���x`�>�'/����=�����*�n�������?Z�A��W�w����&��S=OE��R�����^�2d8���������K����h"���UYY�j������*J�*d��:�.��������a�H�|�e��:�������q=.��I\����C��'�&���t�������/R=��8/����#�6���,^�6l����~�!�'�|��FW�
�D&�$��DOED�'"��_��K��q�E���2���g4������PWW�"���O�����M?�
�[��hx���b�7�V�}H��[o�UEF��������Q���������T(=��Q�]Rwv��b��i��u����~�|x\*�B!�j����Hg�S"J��%K��o�hb�$c"����1<�y��5"��W[[����_E��"��F�:�T���B�I�^z)jjjTd������.W�vo��~��k�1�I��u���%�d�I��x�e*����A�������TOlg-��/�s��.���u�������i;����^`JQ
j�������������(6������Ic)z����=��#������ea��g�H���(U
����W�c���'��9���o�������U�VH����v�NYD0���R�+V���d_���[t�s��]4Eo������v�=�$��.r?s��m�����]�[��q��5JS�(�������E��$"������5��q�#6�y����>;��H^^23�L�0!�X�{��N;ME�R�����e��%�@�H���.^?���N*%�W`�	��"{�
�k�y;�k��9�?����pL��
}A�[w��x:2��HJDd���&M
��|���QO������]��[?�T��,wj�d���#���d��W��w���o��4J��{ aBX�����M�/�����	!�����������&�y��I����r$I���I?�&d�XU����+dGV���&`w���0!�k�yvw�3�I�p�#�d�~H�J����J%fU�g�&M��,2��<��x{~��������*]�m$L��z����P����|J������|>P��<+��|�Tw�����A�NHY�h�����"C�\��s><K�����M��>o[���s����\�?���"����DDD��kDD������a+k
#ce�]���c�y��l��UOb�G]/��l��f<�~����ySf"����|�����9����DDth���	�=��*����X 7���h���<�}C�u����,UID��'~�]����Yq�u�C�'W�ox�1l��[z[d9��s"�5���'���~x�!��������y��u��_|]l��s���s�Q#RKUR�~����B��p��`�T���K���g�"{���	��N8�d����@�B_wP�5\��k��%����(��s��y$�5����ADd�P��sUObRi��+��u���u��������p���t7����X����R���m��6���U����c�����]
;U,[P��.�����h8���	�41^�DNd�n��o�Y�n]�����7\@ ":|��Z����Op�]�c���L���k�����0���3��8�/8�m-�'�K�;
G~�=a�s[��+~��-WQ��%��{��i����)����b7��N���
����P0���mW�����*""20ib�p�#1�y��5""{x��������� �8��=[0����?�a�z��*�����@����M]���^�"���/�`o��%�;����z;���}�%�Z���^w��&"��1i"EE����~tl}R��oT��x�v[@����n�����G��[~}
|
��`�Gq��_�����@g��W"[q�
�P3Z}W-������2dW�B����Xx*fa��WA����F~�<��������zb;��cp�)�D���e@����5���[{TOl�/���/_�t��[����`�{O�����L�0I�DgC8k�Qg�������h��J�41r����<���Q���w/���?���q����r��lI����?\m�3���N��"{�}�������W9SNF�)O��~|�~|���S��� 3�HE�������_E��u<.c�_"�T3�kr�����C�D7��7�"�
��, H�L�b�DX@�?C���X����������}�G�1�9��pB��
<���t����#��H��J�~�{p�W����g�������M�9/�MSQQ���0m�4������x��{Z����E�YNL+�����X<�P������]4t��m��#�l����&���:*
j����@o��U\\�c�I#�k�q�#>�y���^{MO�0UVV�M����}���H�0���~TE���=���P��8�����Yo��Z���f\w��Td/�������U�xk������*�q��g���(�����}�O����9c%7�DD462
����o��W�z��K��p����\�D���&�$E,]����q�Yg�����	d%IW}l:���#��������o�$LH�Nwr[�X�5t���!""�(��ADDv����Z@nnn��ArN���s�"��5"aB
#���OJ��xI�(-0b�w[�(�y���;�R���-B���#�"�>�<?�DD�&M��d������U���%����j���H�����|F���������O��5���5�,;;[?�(�}�;U���N�m~�����'�y������;\.!zK�X�cd�\�N�������(�QA��.U��{�j���*�'����[j�����1��������<���R�_t���))]N2z���)U��=�����g3����|]�'lnq&<��75�����o������_�r�-P=D��K����m
���m���N�r31��K�V�������Uh �/��t�%������|�t[~��������\�
"��������k�q�#>�y���;w������---���������0I���3�����PQaT���Ecc#f�����h��?��v��l�!%��{x����c0��������X�%�=K�-:��^t5�R�t~%�y�"���\���F&M������M�����1�����A�qk��=������m������`@�<N*�2���aeo`���
X~��g`��W �l��99Us����r�����������O���6|�zb;k�,|�����^�n������������D^������|����i�sp�e��m""�&F�<��G|\� "=]]]�@477���c�����m������!�;�z�)5(--ENN��
���Z[[Ud�������������s����9����?���@�����h������6j�?�l�1��v����h��A��g��>6�i�
�QE@�����)��U�o�~����s���JTDDD�a�D���Q�^H��W_��e�T4���"k��~�������dN����oc�mj��&GA�kge ���4���6�/8�o�>:l4�\�����B��c�.�	�?4��V=�]
�'����S���o�����
/��	���-�F	x���^\9��s���7���������5�����<��FO}}=���?�hlH�BII�����uK��v�W���JE�����������Wdg� ��>`k�
4�>���>�"{���o����Tdp���'��]��q������\�#":��nnD:Y40Y��:DD�����o��">s�_q������}B�X�7�oTw�b/��z�Q������:4aB��B�x����"�=��3$aBrJ�����d������>�Z��V?��F�h����W����>�ZDDD��5""�H$B*F����7�	c-��x�2x���	y#w�r;����1sJd�
I��i���	2�*m�	�4!����'.�:���Uk���g���&�`�~���Xq�Sx��]���S�1H,�_����q2��	C(���w���?�;�������C��'�h����9��)Q�7e&�+����iK��|����vUS:���:|����}�����/�M2oZ���u%"������<��h2��7���[��������dK����*2*H$���\!���5�I�d���S��F�s��Y�X;W�$"�)�B�.]�Z���������F_+:�����7��D��q��<�?>���IL�]�?O�����^��<�����EYCo����3]���q��:��i|�����	O?�4�q����OWH[������4�<���e�*��������YE����/��/V���[�J������Jl%�e�x��M������s!##r�Gb���"��&dkI�����iK��
���u-���v���=�SV��Th�:zTG�v^���������n����h���>����&RH�^�7�x�j
��^P-C����d'���i"�v�^��=����|x���c��*���=����@*;���Q�q������jZg_`������C���]�"
a�����q���O��X�C��u[wN!2�����s2�������_DT�HD��x""���5""�<rss����Gee%\.��*����?��t:U����MMM�!m++sd�I�)����k'{��s<�-��j�6�O����&(�G]�Z�����/�b-�m�S=��+s����1i"��~���eH��c-'oNk���q�����=��<��VN��j��[��@;��W��k��&�'�����=�������
d����_|���*2������L?�m%ceM�@?���/q�3U+>��+���Z9�	�
����h������@EEED��l����6$q"#c�[x����r�T�):UKV�����������F,���������cUdO���I������;��/ENY�~H[�L2V���c�D��.)�l�2����[nQ-��7��Z����8���-�<�TQ�����Z���28,�V�G?o=�N|���
��8i�vE�Z��4<�,r���7��>m��������_W�Q����
�������iK�������494������lOZ�[�����w����\�?��+�������+3�lX�d��'""���ADDv/q"��21c��	S���#��Td�*��V{m���C��rm�������
��|�W����.O�~H[���I��\"":4L�H1RR2���8������G?q]��}m�o!�uY������S��Q\��H�H�D���q&�|�i�F>���v�70�lS����DU~�[4G���7�|S�� a]X0I�����K�5�<r;�P���v"2���4�b��<��Al������N��d��*[��J��<""���5""��X�VRQS�8����i�c��i�Y�"��l%&�d����^��
Y�Hwd��0��s���xw�j��b�D
Z�f��Ey�BV�\�r�/q�M��M$���?�uE�'9�h������6�������J����r;�J&�0a&Q���_�@h`���M�u�]���&�_�ZM"���1&�\�7�m�������}�_���S�z\C�`O�v�r�)����
��<�������_%�~�v�$�j���%��y��H�Caa�!!�l�QPP�'K�!�X[w�\;&L��O�����y�('��n[}�j�CE1&LrN��v��P-"":XL�HQ����)G�
~2�B��X�n����]W��xOr������w���F�Lz��T����q�<�I����-2?��T!%'[[[Ud<E1��k-[iw��6���NF3*
T�w�%L���{��	/8X���3V-Cn����{�J�v�j���������MO���w������PH?�k��Xs����;�U���6���s��`Z�T�4b+������N/:{�~8����
��mn�v
"":x�+���@���h��~{R�&Y��%"�2��DKn�eEY*B�^�J��?�����,��;���JDte���=aB'�m��
���z�T���	����I��jiiQ-{���
v>!���<���\l}l��*���4���.���l�2x���?04qB��m
*2D���]�[U���#^��I�[����'""o\� "";�>����XF�%Yb���	�����m����J
B���O��@�$O{-4b�w[no��y����^��	��h�0i"��7��� ������r�P���y�����C�fQ��&�#g�����c������|�7�D�u��dYJ�[��d`Jn�z��K�����w�TTZ��:����w�UII�j���@{��
l{�ch��g�u���$����8u�}Cow�����sUd$Gt������gH�~H[����isd��B��&�;����q{���!""�(��ADDv000�����Q=S�"�����1�9���4�{�j����D�~�:a���N�����,d�+GX���:F��5&���4l�_���$���8,^�6l�&Q���|���S��&���GED4^�zu����L�s��p
��gH{S��nB0�F�V���N���;w����]�"`vI&�G0A���m��v�C���L���%E}g-�~�4�����F��\op;Z���E7=�����h��Z�������&AHe���n�]\\�+V�m;��G?o�&�3�����w���f�-q�C��o^����D�DfN)���x22���������TVe��?����G{}�t��/�-fW.P�d����*J���T�s��5"��S__�?��**++��~y0$)B��I�FFF�^a"�������)S��z#*j������c��}*���
TUU�h�������4�e�(1����Vc��sp�g���|��uxs�Q53-�����Hw�����s�� ����_:9�dm���g6��M����V�S�jPS6�/�$�U/T�DD�Y����NDD�J.8e���'D������A_�vt���jon�	2o<&F[O ����	"/3�S�U�.����������ZvRVV�Z��!�>����	�:�Nj�q�����|h7��r�Ub+_���;I~��K�����TO|g-����k���Q1_�����B�}"""""":<�aeV����)��H��E_#�����H���
	Y��|����QSUK�w0���AO��&}r�L�K������
������e������	!���y'������	""U�_�Q��V�"C��	_W�v��_%��;�H�g7��v�b��w�%�1H�?=gP��\�.�	P+�:k�N�kkkS�!z��n�-[V����������8�x���j���Y�'��.=-
_9w���%��@q^��KiK����9���v�Hwh�����y�:N��u������h�H"�5�A*I��X[������>H"s����QN��p!��,�=.�:v��C3�lA�����z5	_G�.�����9����v&Y�������R=��8/������	""U�A���?�y��IL����sm��$7�l�#dI��X���b�v����VoP�*C^%�~9/��r-��]'�����2���������^����Yd�����������<��9��U�|;;e�4�|�R���q�7��iK���,f��+��u��M�4E@�#�L��DDDDDD��$-���HG8A���'�!I�p��[��9c����y8����5gV�j�>��7\�V};Yq�Bdg��MH5	_g3z���C��
2V���=��/�4���O�9����y��J����OTDD�&��h�93���K>��|�l����6%r?���7�9K'����?�����)���-����"��n����3�-}���V��x�k�k���k���'�����O������������2���M�}��
���a�.��2�d�?�Tf��d�|��U������8!��-�[��~��T������hrjii��}�F��mFc�d�)S����""q�J�������/��>2���{ck���$W�|��]�d���-��8����2�����4��~@���@aU6rK=��s���*qae�~����G��DD��c��'8R�z������r�J�y7�p�jQ*��fuuu*2�V�x���ED�����]��1�����;1t�|o�PE���Ld[�B�@h;�Z�$�aza����~t�
_J?W�7S�����<\��Y���q�	'R�B�5L�nz�3�U����K��Y��7���-Z�3�8��~�v�������"`���9W	�wk����@����p���4c���^�u�&N<��A���_��������@�;��}Kk�	q������\�""�0����o�(5�D*����A�p���h�H5�?��*{���~�����n��~\q����R���������j�*���������Qr���v��Udok��������vt����L��,�5��[r�~��Mxs��*
����z�*���X#����~�m*""2X�R*ib,>����������@D#i��&�!c��>K�D�;3�2�_���V?z�7��1aKe�Y�n��������3��	7��C�}����#���{U��
�e� ���4���=xmK=�����.����<��Z�����T[t���r6�P8����O�BEDD��41v��A�p���h��w��D1�I���������N`ji������I��U��lBV���Y���N��Z���"��|e�'������
\�)��FOk��V4��������m""�u���sD����`^}�U�CDDth������lW�^a�\o���0Q�q`j�Un�Uban�!�e�\C���]�$_K�f*�BX�z��	B��X�c7W\Y���n��xX=�R�2��	�������n���0&���O��������B �rD2d&��&�y%VRR����;d�
����nH&Q2z�\C��k�!���):UK�U��A��}���@s��eGs�K�5ab�6o2&LI���	���$L����8�/�!"�'��&�/_~�H$�q��]���-S��Ph�w4��v%�pT��Qf����������.#�YHu��n��Q��D����J,�\��2�O�/�����k�Z�5S��/����F���PVV�����2��������x���S�7u�@c�oG��\������TO|O���>V�L���W��S�j�r��K����DDG�k����kDDD��\.���9222����`0r{D��9��1r
��\3��:�C��������Wu����@m��V{m6b+_����%;3r=0-}��#"���RIR�[�hR^r��u�	���y�}2�J�s2W�G/8��DD4rv���G��~�oXz�=��{O���~�1�{m�i�9��3
�pX�h�ZR�5S�\������v�*?�f�Y��k���k��Je���x���U�&^JC#;;[?�-}r�$sd��L?O�����$��	9o=�n�y��j�d�h�y��)������2g�xr�����������_��nK���qDD41p�����>�*fCC��3����H\PP�W}�C�����ZrM;U��s��z��dHe
?�5����d������0�"�U�	���[�����*""*��&���+��"Lr�/�q�p�	�'6#��"�\�\ ��r^����CDD������Knz���
����^��=mz�g��1}������T�Ri�$[p�K�0I���U����k��Huo���j$A"���'�����A���P:���^?�~���(U��i�J,O`�yS�6O������xt�6i�py�7e&�+����iK��3��kw���:���_��j��2N����5""��+aB*DX�p�����SQXX���\�����9����&�%N��?�-m"A�6N?�<�in��z\z�����O����O�M�*�T-������$D�"���/��DD��T����)�o�d�`���h�H`��@�� DDtx������7T�����v&7����l���p��d~27�����I��/X�ID�s2�d�k'���+d/T����(U���x��J��h�����#���*��a�>9g=�n�r����?o3s��-�DaU��*���g�	"���5""��+aB�p:��aR=Bq8�>#}r�ZaB�&���%qbpp[�0��Y����6�L/�&���A������_7`�{�h�
������_>�Qkw��9Q���>�b�o@���U�|"�h)�4�~�z�2�S����Z�2V9��O>Y��^xA����PH��
�������!3��E5���V2��'L^K���t&W%"z\�uR������+�$���u���.H�EZZ:���:�|]�$V~����2����r�s
���0a�s2������i�Qa"����
��<�������_%�~�v�$�j�w������<���R[������������	&9'cL�`P���'j�q
���Td(�j�������V2�N�gw��\��������x����2���u&NYx��������zr��+�v����/�M�,<O�OD�HJ��[��X�t�j<)K���<�U���m&�NdT!3o
\Yp8=����_��d���v��*D� z�#vD�~�����'��u�C=��9^y��d'�2aJK�|���E��%-�����G��

g~��G�3��_�O��O��v��coK���;S������r
;zf���e�-v��YU�$�9�y����ZDD4��ADD���~��	�B�������u�97V��|�T���>�lY�" G�m�WL+J��|�U�#����fm����{�yg�C��b��L�i[�X�cwW��M���|]A�4��[;�Ub+����""��R�1�C}�"��F���yO���2d�sT���gj��Z��j��4�<`}�;Z�F��@;�z�e�G���������4��)�8�JKKU� OQG�JJJT��r*�a�)���l�qWtc�y�W��_�O
m��e�p��2aJ�z�[���Z���q�j[r�K�0�y�V��DD4���ADD�z���PXhT:4&�jD�C�B�@�Z��	�Z�5SQ���T�PSd�y&"G����V���d��f<�~����G���Dv���>;_?�-}��Je���3����K~�3��H�$&�d��+����O
""5[w����-8$1"I��n��uWx�;k����O\mB��8��������X���
�k}������`�x�d��G����T� "��K#����@H����t'���u�����u������������@���W�-��a#s322����5�k�*o������g��r^��������#���*��a�>9g=��2N|�?���\����S0��F�1H,�r^��x"�d�T�����UX�r�j�W_}U�b�<
/��=m*�~�d�4������U��xm1���1��
�M��~9owee�J#���	$�B���s����o�y�s���q��������*��{[�V#�M��Z��"����Z����$��q�Mv��a�j2�����}"";\� ""���`M��=���"���&�d�)z;�v�V���]x�UdT�H�u������u[wN�v��$�d�)zK;�7�X|�����V�y��������/���F�V�x��T���r�-��~����u�������ReB8����(��m�5�e����
��]�9��,�(�p���6?j;�h���_%�~9o�y��y���=��2>�*�/V-CkkkD5	�����rD�\�ix����c���jx[7�^�������h��M3*������&N�������(r������Z�~r��D�����y�Kvv�j��G��	��s2���	�d�������I������CB��F�cd�\c��tz0�b��JDt�R*i���OV-c_�3�8CE��9�==�.]�Za�����~M""J����e�/=(��E_g�IEOF���#~L�s!�yg([p��bGK���%���y��w���=��CW]]�%K���(EY__�/��������Z�B��\����i��p�������vw�Gf�����������mkP�!z�]8��V>GE@p�-L�q2_�CDD��kDDD������-G������w�^����������O���9��y8��=���UWV�����W5��c�#�|w��GC[����H"	 -�c���p�""J^J%M�p�
OA������������NP���XDD4<�#
s���O�f�:n��"�:v6�������xd���3Y��}9��$eSS�~X�S
k��~��U�����@M)0�F{-3b+/�)�l����t�������;����@o�~H[����isd�]�(��Z@_w����bx��8�u>�=�y�?I\hii�#zR�&������F�������9��u���=�SVY����6��U3&9�d�y�:�N�����!�?�Ch���7S]��Z�Gh ��6��DD�*�������U� 7�x#���,&X��s�$E����u�T��/4z�,VX������N������QQlr^��������B6��������o�%uD�����<cl���W_}5-Z�z��126z�P�����W�0�2�Y��
������^�X����^��������<�h��Y���������!m+{�6���/��jzZ�q'���%r����>�ZDD4^��ADDd��{8��Ie��S-Cm#�'qB���U�|����SK�O�$������q2�#H�@m��n�m���.�e?Y��y�������i�ID���K��z������`=�
Lr
��@��B��Y�ZDDt(.\~�j��Mq'�_�[}��#U���;�#����2P��Bi�S�X��2^���,�y��8��sq�G 77|�(m��s2�������Z��2� GQ��v��i���eO�t���Sp��Y�'�������;�)���?v��d��t6�����O�����_%�~9o�y����{������
�8L2cRY���P:���^?����4w������/�M%�<�oW3*
TK�R�&N��������X�����M�/��	��Hdr������^��#"J�|����]�x16l� ��!OT�[ ����o�K^F�~�"O[��|�"u�luuu*2���x�����F�w��
\�<�gx�p�
��-9�&��/�g����;w����]�"`vI&��#�ak������,��yNd��~��@����
�+R��P������v�������g���%{z����|�,�<r�����l�!%��{?�jl�������[F`s�o��������mh�2��<8bj>:�
�,���M���:l��s7����O�BEDD��,Y�7��LI���T�s[q��L\� "=�
hw�q#�������-[�`���*2�����t������mIg�������h����������Y�����>��k����y��K�~�����{Zp��_T���h�)���lG�m��-�������%\� ��d];H��	�����&�-&����w.{u�[�k�{yr� �p�hb���k�a�>
o��J��������X$M�j�n���U�-����_5�����MP^ff�T42&Z���M��u�V444���W������)S�j.���hp�W;U$�-9���.�	U�Ytui���,������RQ���NV�4�_�����
_r�����b�����q���(��������W�v�QRR�+rmG'ZZZ���K+\z�����Q�����o�i��TO|�GUK~����]��~���xt�6�y�p���`�7d[�������?��Xk��N8�d����@�B_w�V�\� �xl�4��W_�_�0yp�h����>�l
��4��XX����i*c�4�^����b�l����<��� Zz�7���4Y>�?�&J�DOO�}�Y���{�'�#�<��vrr��Y��z��������y5@v�
�}>�l�i�'^Cv�GTD������[cW���r����`F�<,_�i}K"�D�41�p�c���Q�{��G�J���J=q"I���/���T����TdO������@o��Ha�	Wv*}%s�T�����H�
�g���ZT9�aT�����}�tc�r^u��j'��![n�����-v��=tM��Dw��mIo��>�����[�0��.MRi"q����������v
H��	*LXY��|���H��o~��a&����2�n�J�W-�7������]�uhr�V6�_~n��^���u��%��_%�~.�&�y����&�2����0!���1Y��Q��w`��W���+"aBH,�{^�
��8;{�����������%�'�0!��2V����Zv����U�/aBH���Z��/�ED�}7~""�	����3dk�'�9������.��4�d���+GT��#=����@*L���?�OOOGAA����C��g��2�ndK
���$$��kI����mk��������+��r��5��B���X��\��m�4�o�AE�������"����������I����0a��2�d�OD�&��h�l�m�]�ENw8�UP���)pe��)�_%�h�r�$�v���h�HG�-������i���-NFo0\YB�wF��%���]�i���
����:u*
������>9g�92�n�J>�Z@k7�1LA
9/�L��DDDDDDD4>:::T�����)��n$ZZZT�>�rDW�s*2�5��6�5eFl%��2���/�Y�ML�4���@H����t'�@�u�l[*�!"�G~��?nSqOs�����������d����{ML?���x��m*���t��h�~��*�=i.����/=���ml��B�u������Zoo�g������`[K��@���e�����gkt���z;v���RMB�#��7A�P{������p2k�,\t�Ez�.|m��{
��N`j)P�B�=@m�W��lBV����#""{���)Re� U��V\� �<��R��� n��V�������>&���"�S\��IU�H������p�MW����,���<$���U���O�
O�1*���u�������vl���zb[8�����z��>�U��=������TVe��9|u�`_?���*��m�T�$"2Y�R2iBn���]�V�y�B��D���~����m}K��)z;���������i���G���ci,�&�y�T�e �54I�70��� |�����������������
�T�H������Ry�k_����yz"�L�<e�������>c�k�	1u�J�{������&��<(�<��R��U�$A������Do�#�%�������b�X�Bo���W>���W��U;a�$�����[r���v��Ud?�=}��/���/�mY�3]��u�Q���z�E*D\��e*��7T�	O��ID���-����u����"X�Rn{Y<X�l��-���$L���������i��c�E^��L">h�c��f������~�U��}	v��tvvH�����������T{=��NU�A$����Z��yh���g�E�y2��(��&6�y�SYY�jAO����*����@�����o��T�x8$Q�����[������U��0����'�9e��!m�3�X�cW��0�|����?��
�8���	"J$�*MD��5��E��SD��M��{O��.���9��.CAz�jU�����`�������n������7���O��
�~�3<w�����Q�}h��2nB2��.����M���.������RQl^�����_��W����"{�}>�FU���j�J=��H�6�����������&Y(�Q>��)L+/:��Jc�k�<��R������Td<"�&���z�	kR�e�]�������~l\�TPS
�&.6�kn71-�:�����hH5k���x�M��|dV ��w�����@o���;�x,���������������
���G���At6�T�t�9����TDDd���T�����������K�����p����O#�D�T�Xz�=*2s���*PQ|o�������J8�� 0������O���?����O��.�&�2���2n��������]������Y��U�!�-��-���C���������.��5���KTDD4�&��<(�y����>;��H^^23�-��DtMyOs�i���z��������y5���
��I���i�'^Cv�GTd�s��xc����T����=$a�$���v`PU�<~N~pY��I��Cn��e*�t��c&N�At��
�������#D4D�&M�\�7�x���u�����t\@ ��.����-6�����M����u�G�g� ��V��t���+I������.N�����_�rL�y�����'|��z;==S�N���l^(���{10`����=^x�����o���*9��OU����/�a�����Y0}1n��/TDD�Ic�k�<���A�2��������H�+��������!W��0�+5�����?~��!��m8<�+��:���4Jpdg����9z��b= ������@�;��}K����:k�#D�u� ]��)��DD�e��b������QQlr�L��f���=
��[?7aB�56$yB��x�gG���S-�������B4��sf��8��#Tkr�Ey��	M�&d!�$�UY���V2^�������I��UW]�o�1#c��0!d-C�f�T~��z�;-����&���=�0!��KpX�ths��k��$>��V� �����z���0!��0AD�H��	+.M|.?J�������/��>s���eO��z����P����|����W��d�������#���e��&!�px�^������x�#s�DC{�~LF{��G<q��L����+����<qff��K���4�<)uIDD�<����G��������z��126��$v ���Z�����Y�S;/�L��vb�
�G�T�����q��ID�J��	""��fW�������P��zcoBA��*���y���SS�"�ix�1�{�O*�n���.���|J�2P����J<����7�<�oG�O�����jz�J9�m�0!c���'��7��-\�K�xW��)������������!7���B�e�O����ZDDDDDD4�^|��![s������X?�m%ce��-��j�6�O��~�����kU�^J���-6L!��e8�cd�\���r�������9����^���������J�X�|�jQ����Gc��Jd���F������-9���yv�����e���lW�_�9.�~�*z�]�����_�bD��xd���949
��Ol�O�����GkW�V���O������X����U��-9�%L���u��|""{\� ""����Z����*����nTUUH�0�'�O��d���O�1�Z�RE�?|���h�0��hn7b����������cUd?�+TK��h�@����"�d�iNU�j����9����p�e���_}_8���W����t�ID����4�re�*Mlw}������8ow����Z@I�g�|r^�����F� �?�|�}���={6������Y�f��d&&������sxj��'����@+s�*4���09���{k��q�~""\� ""��7�|S�� �rE> #�O�YE����c�G^��*2�RUbk����%�����<;�����0���{�b&NH���1��GV���m���;���k�E������kn�_%�~9ODt0R*i���oV-��^P-""JR9��[���'���i��n��V��?x����0!�Z���.��=L���:N��u�l���������}
�]v.��r�}�E��hr���w���CEw^1rJ�����d����]
[T���N�m~���������}55��������&M���u���9�Y�rD2�2�������e�Uz;��o|Mz�^9�-}r�$sd�����x��;��?}�o}��"+�H,�r^��x"�d�T��	'�p�\���k��Q��SS�o��������+��{�_���'�������\FV������&&�w�y?�0���N�w�}���{����9��6�n�����H������)3�]XWv�~H[���I��\;�Q1_�����jD�����y���������"���p�cd�\��d3�d6��L�g+�^������&��lFOS�~H�ZaB����w��~�O�'1'�e�pRn{�5k�XD���q�gp!��(9iX0�L�l$i�]P�"�'�7{��[H��Y.�g'���x�����������Gww����O��K���Gn��U4%"9�$}r�*z�]8��V>GE@���6�q2_�CDD��kDDD��n�:<���*2$�2aJK�\/�k���f�S��~�
I%D�������0'��nyD��xd���9vw�s��o-j��s"�4��!�[��{���������E��
�/��
�9aY
d� sq�`���6�����:����x�~xBD4Q���k��V����b�M�+~.�/8�m-}*J�;
G�.�G(�=�������%������+����z'�/�b
���4��BdW��xz[�����������������wko�K�~BE@~�������7���>'s>��&�,Y�7�xCE��v R���5��kDD�K^~���$���R����(��������O��e�T����CO��*���	@��k�����k����git�MG��������'��S��?|����� ��r��Y��9�!=*��n���4V=�C�Lr�3��hA_?z����Yq�w���3UDDd���)�
���#�O��>. Q�ix�1l��[*<N*�2���F�70��� |����W�=OE���s����_W�!//��F�|__��p`����z����|�~|�����*����"����o�~�����2Td��;p�.S�p�#��3q��DO��`�������ie�jDD�Ic�k�<��R�$M���+*2������DE����DT�vJ�����-�����[��2� F.IG��Y��
����|��cTd?��\��y�j�P=���(�/V��G��O�-�[��RPX�
g�uTS0B{]���������~�"""�u���?A��hB��yG��:I�C��.R�T���V?�v���Gko��*������	�g��������	)WYUU���bdgg�����Z�R��\���7�"�9�iQoy��#o���t6.��5*�~����CW�>�gI��_�X��	2�	DDDDDD#K����
L�>]�[���~
%�X&d�\�NU&�����/aBHu�
���w��=���;C&�y��)������2��Y�[��&�
\	&���q&�/�!"�'�*M�^����*S�� J
;�����w�uW+��	��;��f��3��95���������O��n��^N�|���"-�~y��<��l	/*++���)����S0�|\p�*";��e���_Td�T��]���&K�	�m��w�������q���""�H�41v��A�p���(��������E�����PW���5���K/EMM���a�#�i�����l�1}��Lh�v[on��U��/�n'�w7���/��py�*��j���k�jZy�I�=~T��
<������V�_��;'�v�&O��������������(r� ��&����������]�C�k/^���}���o�?��~v%:���z�+�{<~��eS��V�Zu`���-U)(V�X�������5��b��w�!�(��Jo�>�F�]oO-��o���&�'7<�?�<|�I�0��%����h(&M�?�y����>;���p[��{��N;ME�08�����k[5�@i�
h�n�k�U�Ytui��{ �����u�����*f"���~������*�-��M~DE��������'L��+k���L� ��X��=��k�&��	!�d�d���_L�0an^"Y��8oG�$��0!�����:F��5hr��:M��6}� �~��@������A�	"?<�����GDDDDDD����OFyy���$�����	2V����%r,+�<��q���������	B��S��vq��/!7+����W�����v�2�	D��&��hTI��
���(�'����.��_%���2��j��F���*2z��wbj�K-�ba&O�x�g7Q�4����*--�S_�<A��9��Zo[d9J�^���AE���v$�sH��4�����a��q2�������F�����W_�E����d���9v�Ur�j�������S
%�:v����)n�
I"�12W�aGGT�Z����@DBD,r��~��:��(&M���^�Qa"��DVA2����U����J�����I���?n7=u[���w�p:�0����7J��(�d���(r���7�������JK#�[������O�u�m�A��`Z	�_:WEFrD����mo@��S?�-}�d���92��d[���#+�d�:�[����l�Ub+/�������ht� g�y&�=�\q����&iK���1����l��U�PE@oT2D<���
�o��9�zU����GZ��|u��K���9'������`_��	���V�����1i"E�\�R�t(G�^}���_��3��9OD����yO���2d�sT���gj��Z��j���P-��<�2c?=������U�=�����Kqq���}<�c��C�&��������S"7=�w���e�~H�J�^����=M����tg:�+<�+����qff��K�C;o�y��;TDDD4���ADDvw��G�S��������k��iK���������
t�� �N���3Y�����|�2����� T�P]Y��.�u&NYx��d;��������|�!�K��7���<}>Q"r7�?��x�bl��A��FnJ������>��H��:��`�E?��|���]����/_>!�>�uuu*��TW��Ir34"5�~�1l�����-9����D���#�32��N+��?�����7~p>z���m��C*J����v��A�S=�������Gy[��QYYw�I�����������.PM�{�_?��z���YKf��?����)�n�mx��'T=1��YU���DgC�����9�_~����K�,�o�+�L���dL�?7�<��5�H\� ""������5�]�[�u�Z
�x�J*�����/���b{n���_�9�E�;��E��S{[���cT��Z���^7:�)'���|��+��q�����9����h�/�9
e];�PI�7��7��s��H27��h. $�x`��\@ �xB�,��*�����V�	x;����"`��W�a���:>xo��bi74�Ng��`���
bogx���~�.�����������T�������$��DKK��p��e�]����,�=�ul��`>�����L&�o��������mh�2���<8bj>:�
�,������{�^mB�RQb8]M>�g��sEn��^�MDdb�����G����kDDdG
o���
7��P�dk���2�v{/[wX+L��KV����Ud?�=�O�����W1������^t5�R�lA5n��#*���Pz�����R=��y�E���/!��%4��&������\�l����o�]��s�
�oD�b-�[�'�p���2�/��B���^D����yG����*�nx�j�Hb�P����Z���q,��mG*���������_���%�������78�-�m+�,�4�]�C����>;��H^^23��e�T������Q���v�i*��������_���6�:�W�@f��������������������0[�,B!\����\���l�1_g���+�}s6J�"�������5��p�c(�y�]m�4t�?����U��9g=�"{���7��E.�����V�����W����'L+Q�=�IOI����9-�[�Ay[�����	"��M���W�����#e��8X��7��*���-
D/6�BF�������'����Q+M�$ib��w��p��&f��L?�+*��P(�{�����������+���}?��~������TO|e����1�������&k���}��?~AE@aU��*�/����z����_�[��\�"""&M�$�y$�kCq�����l�������*�/��l�>#�����������m*2����T�����V�m:o�\|�L{nYb��=G���$d��D��9�(�	�4A���n��F�/����(���,LX���'/��@41]���a��
�������{�������Js�������"V�D������������R0��k1��kTd�{M~�l��Q���h�"}1{�����fp����u��^V�B�;�_��f��s���&M�X���P\� ""�2+M�o�dVw&C�	���~�v�oW=���R���dd8��t��+����Td��9�p;|�����
?t&NYx>�:��[�����0�*I�H�������H�I���.qID4oF�jAO�����(69o&L�y3�WNN�����=Y�@O�����'"��	2��	���I�(�j��K����V2�N�7��Ol�O�����?�0!�-}r���o����$:����0?SL�q2�	DD4���ADD494�}���9����\��D�?��^K��J�7h��N�~��d��E-�� cd��&���tD��3����l��z����G;�U��J��^�x~���|"�D�4���7�'���m$���M^.?J�������/��>s���e/�3�����S���+7qB�����O|U��E��X�z��U&����2��Z����-�T�d�j��m��(����U�#����fm���3y���_?��6|�z�{����2��f��S-��;o������3Y���y����m���������O����<������m���^��k����I�/����Q���z��12��konE�9���VNO�~�*z>Q4&M����j����u��h@D#mvu!��x���� ������>�Ub���&�7�F�K����G`���e���������h���_%�~9o�y9���p��_Dcc��yyy(++�i[�X�cG�#�
��k�n"����������=���]
*2���9%S�C�V2V����E�V-Cw�?n����h��N?�S�EDD4>��ADD494��umuP���(�/U'��6��Z�o�#��B?&���
���U�����R���q&�|"�X�4A1��������	.@Q,�=�h,YP�"�l�������Z���%���2��j�_��������� v�����-9���yvT[[��_]E�������*#;;[?�-}r�$sd��x�������_9&9/�L��v�yw3]�ME@����)3�]XWv�~H[���I��\��)���?��e 8����|�u���_%���v�$������ ""�3�y����'��W����/�2����0a���[����r}�:v�h�#3�X?&��Gs�>�w��nNwr[�Z��|�Q<�U��������aCj-��7�����_�
7��@Mt+W����sppP�>�� zN���R�����:��-S�1��5*��3��X@X�n]R{�������:����x�o�h�=��wp��PQ|Rab2$LX��]�~?F�0a��	��#�`��-**++#�#��~?������?\p��R��{6�i����S���}<ci�vZ�!\���*����'�m������w����~�v�X��7]���m��w����`�b���_���(��%K����)��v��n+�yp���5""K���j���o�_U��'���q��kPv�W�p�k��P���j�<�� ��v���P��7HKKG(���w�����(�v.����FO����=U�sO��c�kCy�=�����(��WV��������S��/<�������hr���d�����r�-7��Cn�S�u�|�!��������BB�E�E�X���F�&��I��QZ����I�4!z��b��{��Z;�8�S=���B�)��[z���U������sssQRR���iiiAww���
+V���v
t`����9�=z^�
�,I_���m1���s
�]�����u�v����������������
��/�JE�I���/�DEDDC1ib�q��k���ADDc���������h�[Y�2�&�v�V�}��X�F�\�p���k�.��y���U�8Pm"�����������������������m""SJ'MD?0Ri!-���'{����d'F�&�k�6G�?3�Ut�l�q�����f<�!t��������\��Tp��j����������$Y����.Wx��T���S�����"�*q���	hR[^���p�%��!v����?|TE��r�d�h���z��m
g���;������T��i;�y�a�j��=��U/0�|f����E���� "J�Ic�k\���kDD4�F4i�\�3E���+Mt�M*0%q�C5I\���g���7���"�pjv�m:������8~�����nS�!��&�:�T_@���K�F����'^x��!Od${���"�x?ma�^@(++�[�������n���7�����!=����P���~�.u���Sq���*"�����w��o�a�*2������CEE��R_�����q�k���I��>`������sQ��?��>�4u���$'*Si��t���H@�_yfO)P��i���1��8&I2�]���L�[\���G,\� "���k��]kNR�&��8�>E	����\�p�8�%xJ��hlp�#��[������"h��t��x���������~����:�F,�����&��\��_!��s*, ���5o��x�Iy�b����S3���H�`"_�`���"��7�<�����;��b���^|�7o�Hw8��[����Q=aA��MUp��E�.KbOJy�s���VPZZ�����N��+M������O&}��1�m
��vS�����De]���W��y��Y~���C��|���U�}_J���N�����
wF��&�@����}(�����Y�R�Tc�c�0ib�p��k�p�����R�k�o]�"����,����6���_uh��_c�_��*���@�=�K���RX��p��~����?�����#o����<�������^P�������8�ag���+��,����8���UDD�x�y�?GRb!�fx��g;:��\���'���9��xOJ�Z���8�����������Ol��o5�H{[Pg��	S����pf���+�����"�;��$�#Dnn.JJ�G!U&�����v���=��QE��|���^ ����E�:�8+/F�����~n��V4u"��"d%~$���gY����yz������
��Gcg-Z{T�v��S���|x���)��z�(U0i"9������<��������y5@v�����;��5���)�������N���1�#=�ta�'d�dv�<��h�����
���4!�����~�����a�5���|���i��&��3D��II��N����}3����1�}�c-8$z�c4q�hb���oaO��w�l���7|��������V��[�0�7'4>^{�5���WE����������,��S�����6'��m���-�gx����,z��7�xe6��PQ�r��~/�v�X8�W�8ME���s����`��h?2k9N=��p��o�S"�b�DrR���5�C�5""��3Ib��	![udk���=��g$2$��c�&�DI���Gb\� �7&M$�zC8^7��*�������'Z�=�-z^��������I4�B����!���pe
��^�����pu��W]����A}}=z(�oF~�K�	���z�@@�2�����/��BTUiw�648@����m�]�'�������"-=�{f7����;��W��� �x��j���%*����K����"��$����+P����^u�,|�3����|��(�����I��k��kDDD#���������K��$N�5�	":ib�/�S�DEc�k������k��3���=��x�<(�N���~
������p'�h��5���v7��~�
����Mv�a�E/ TWW�����(��M��{O�H��*���9��.CAz��w(���q,�]�"��g�}v�����<dfw�Ra����Db�76��v������_������D
�$H8=��Xx#J��F�����~����"�;�N�����j��������<VE�t��+����TdHf�S���N�^EDDaL�;\�8t\� ""9�M������y�d��8�����#�0!��&�}�5d�}DEc�k���"���J�i��z��������E���V>?���!���k)�SBnNi����:>z�O"����(U-C(h��/z\�u��N>�d����� IMMM��0!ce��I���~u��E��."aBH,����
�^����vu��Gc��Bd���e�~D/����9v����#&��Ve#����<<�!�Vf��M�ozT�ODD��kc�kDDD�K$�A��a�c�01�p�#�{���!	��HNI&�J3�W��d�=��DEDD��lj�<E@�n����5��~�=^��$"�-5�N�=5�.��u��iE��c�q8�����m9�#4��A��IDATcd���+����������TObM����yv��H��_:g-��z�;k�,}����7���Z������+�'C?o=�����<�<���&6Ix����D�4�^_�m�,�Uh��FlM���y�<�$L���~�u]3����_��R��z��R������!���a|P��F[J��|�������5����h��O��-��y3�I_��=*�M��8���pN�
�p��w���E��'cd�����\o�&J���2`z��*������M�ii������O.��#�P�.S-m��s_9g�>��>hxO������	Er^������h�q��kDDD��l���Mk���%q���5�It����D�6��\A�	�����x�{�C'�{����g��Q��T���G+s���rm���R!���RI'�p����Rq�=y!�h.[�L?�8������B��DD����G�����7qB����gN?R�h�x������"C^^����C�V2V��Q�����e����A�^O��V������GT�M��<�?Y��pn�x)������g����O�M�������"��N��u���������<���&���i��R9B*��5^M�y��M��)H�?<���z{���!����	�}��W�����"�x�~H[�L2V����	������'�����J�k��9�� 7�r#=Yu���4n��v�
;�=:�8AD���.��/V�T��Q����x;
��W��_��d���"�dP[[��_]E�
�������f����9����v��;\rP����I��x��V��'���l��l�3#����_Y������a����y-E�s�#j����*�*PH���q&��T4���+U�8��_��ns�:����_9��2d�����O��d���N����k�*>(BDc!��&d�@�~�r�+7��D���p�G���G3�?�-���Z�X{{���Y� ����g�%*Ud�-8����V��Z��2^������o��A$\�pF�I���U�\;�6mP-�L���Xb��2�d�O����Aan�����}���X���F���G��&"���5�yM���U$�g0�B��T�BkK,�r�� �������\������8����I5���
�;,�8��s��/��'f��|-������X��,�����c�����.�a0�~�F�i	y%Y�>E�h����x e3���[7.O]�|>����/��������";z�����?������&&�U�V���Uo������Do�������n�-I+V���v�������"`��@[	�k����j�.�	w�����������U��������I�h���"��9����MEDD�%K���7���Ra�@����k\���kDD4�z���������B�U�<�}�VK�P��+�Q��)����-�T��]����?�'^Cv�GT4:bU�p��#4 8���pHU�D|M�uJ�Am�C�;`���!a���U�8Pm"�������������als"��r���6���v�r�sLvr�n%7�r�]�Sn�e���  n��f�*�\�kK�DY< �� ��z�=i.�N��vc��b�����g��$�{��	"V��h�12W�a�����\�A����s�j��}�bW�����V�������<������
����$L�����/����C��N*�Uq�Hz�o� ���1F��]&���#UK�7������7&�u>Q,�O^M*<u!7��!�����G����dn�c=I���^<�SD�aG];Z�.��j��=mZ���~��B��Q���~$��D&T�����W^yEE���J����~���T��:�����D���S����
�U�o�������������5�4�~�<��Q<yN8��o�C�|]A���������nPQ+M��y��1�<��h,�v�	�eO8I"#���'C��{,��83��)�*M�t���idm�i��_GRo_����:"3C�K����x������K�QMYA���,����%�i���kI~J������w����~�"y�(�%83�>i�?(��C�?�k�Y�����T���v�RI&7��og�S�Wr2��\[���������9(�=���/G�c.--ENN��b�����N!��4����cw����5@V�E
�vs��rs_0�|�:�M
��v����d388�o�%�4nW=��V>?����,BGDC1i���<"q������X$M�iZ$��x��s+g���@!���|��5�FJ����u���o_���'���Iwd o�L���z"
�����S{��29�Z#��_8���U46���4��WFS����������/�M�����>Ur0���Q�{���&�k�&��	!�d<M.��W��iw���%���`�y�e�mz"wS8@n���U�|;{~��������'q�O��iK���L$����g��IL��x&L�D�5"""{+���_�����p���W�����x����c�0I��n���#�I�qn�&����~S��J$z������0!��<��TDD����|��R�� ��bU�py�������F(��@�_�:k`������/��v���zo�!I���KN�>'�t����i�}�Kh��JEy�B����=�wq����t�E���v�������'��S>P=���x�|�q������l��6�y�a�6m�����2S�jPS6��)��z��%"���&���<��h,�v��D$�s������J�^���Hkw���Z�b���	����+�e5*N�	�@�CGC�����4!�����^�?<���TO|���E'~	q*uY�������4. ML�k����~LE�|�
�;����.�=t7!d��~���1��PEdw�s����S������\��/J����������.�L��o7����G?o�&�3�����w��l_9�?4��V=�]
�'����S���YQ�/�C}�nTOG�����(yL� \� "��4�I�&�5���4�)(Cz�CE#���M��J�����m_{��
�pZfn��F��@��&���S�4��{H�?��4�>�<�ID4��������3����'aB8�~I��zh���E��,����$EH���V���������0!s��0!$�a�y��l��UOb�G]?�Z���w�$L����S2U?�m%ce�d%��*�3a��������/`$���l��L��t&��!�'d�I�#�����z{��3Y��l�s�F��-������I��m����$L�X��Z#u��y���E�}�sz�	��8�0�zS%�H�4e���yDD�Q�V����������_�^=T7�p�jQ��SD���~����m��#3o��N��k���:�N+��?�����!
��{�Acc��I���W^y%�����(z����_���6�:�W�@f���������e���6�n�
�_R��� �x��j�����_5���$,�n<�ADD�c����52q�����Rt��#�D��u�8�TB	�3�U�����w��F��$��A��O-��vzr�]\�W�M�~/�v�h|*M���J<�)\�Xx��p��wC�+!��p%cq���q�i����(��v`�I�Y8���[�v�Z�sx�o�0. M<���^����pe�(���}�����+����MX�{y��5��q���m��E8��3��6��}{���O�|�;cWo����'�m������w����~�vS�SE�����Bc�c2��Dt��41���A���ADDciT�&�����'QK:������4x�����yk�O���/M��	o[��V�/g|e�5����I��}��������9���k���=m~��D�����eG��"""�u� �j7����e�Fl����F��]-�e�}��=n��f�""�$Jd�.��	��-�;�0n���s2��������9�x��Gq�������|\��S�����_%��6=6d�/�y���H������fU��eE@I�����K,�n����3r9��8�I�D��x;UO����
�126z��ys�+�e�)��0!����U�|"�h)�4!�DD4���Y�>�lX�@�����=������*
��9V�^=l�	!cd���&���A���coK�1�{��O�cd�\�nY����"������y�]���H,���]9d�\�����<���h��V��9�cv�)����gO
�U�R]�y.D������r��&������n�?�p��='
�01(�6d���
1�e����
�^D[w��4��Z@V�NW��z���3Y���|""�����P��|�����o���KUthN8���T�R�D������i��.O2����D���#����s�������>��[�W^yS�N�E]�;\]���������"C^��o'������]]����?�a�z��*�~?���?a���8�������j��5�zFE���0�{:��Z�"��+N���"��$;��*�!�ZJ<������q������H��9��<(�y�X���C
��^a��^`G�V\�vC���=�W=7%[rHE������C�5�7��y-!_#?*)#�}��Zh�k{O;z[�i-3a"L�����P@��F0�fWFT�I����3�ao��=����W���� �g,�r=��u�O��R#����RU��1��'��F����N\�_�����o=���|h�	X4���/O-���������AJ%M�\�7�x���]x�O�D��~�*i��$�J�����@��_G�n���������y;��u+���������'�N'jkkq����3��)JJJ�r���E @KK��a���K�k�A=aB�e��'?�y����R��]z�w��" ��
������o�~����H�O	'M]T��9�p;���C�u�s��4AD��41v��A�p������H%M����>��&L�}���j���(M�s��v������9z�@D�De10����M�����Z��"��3\��U����:����A83s�[>M�#+Q��l�)�+��O,+��W���:�Iq%�u���K~���TDD���v�R�sp���(�\��(�2������X���@T����~�j�]TVV��4\SQ�~�a��yo����5I��'��d�\C�eM���%_��CD����H~v�������vK���p��+�VxQE��,��Fn�Yy.x�C^%.������g�������$0H���0!�U!DV�y������u��q�	�$'�K�Hw8�C��I*P$� 0�
}$���Kn������o���@����J������7���^�XE@(�+I�[px;
��W��Z��7��95�*�O�V����QX~�`�����_����p�Lnnn������1�\��\�$_�3����5�f ����7���hFE�����-a��,6HiK�u���5G���)��arz2��V����h�p������B��0&�T���&�9�7jG
�u����%�D�08`��_cM�0�K����@�b��0������L��A�{;*�$q]"��R6i���R�g�>KD>��u����m����V2^��=��������};Z[[UdT��u���k�$QB�FA�}> ���
v>!���<���\l}l��*���4�S#�����V-��m�E���5��=�N>hxO������	&9/�L��DDDDDDD�A*HZd[�d�Zn�e�\�NBAx[����p��$L���	c��]C�5~�]�F^�a%nd��_�j��m�o�n�!�;qB���I��|�?��|��_?�C���� ���J�X�|�jQ*��[gDT�HD��x������Dvv��U�0���c��$���	�v08��~����w�}���3����'ND�k������i%8�\�]�w�����N����Y�)����\;j��������Nw��	�u��������MY�����@�0�4;��2�d�o����T�P 2����3a�$�d��\C�5~�ipy��s���w��:��p���A#���|�����WO��v��y�X���N�'\Xr\�u���ub}�d�{�Q�K�J+W�T-""J}��e���do���zrCf��;�`p��Au�c�
��\S�m����������TObM����yvw��Gc���6/��������!m+{�6���3#��IK��S"�T�5"""������{��'NHmxGW]��v�4&�V����S;���������Vr��M�	i��������2`�������}��y�8l�o��H"F�v]&;Q��J�����Ux��T���&�k�w��
��7�����j�be�������ROrp:#��;::T+��n�#��T��k�E�?���u��%�@M0��x��J��i��.���;�t
�Z<K��w��Y�X�cWY������_T���*�K����"�T�5"""�,<n��R�>���4w[v4���,��S�y��=RY����Y#���4��mo��8!}rN�����\�='�'8d���^��������8�����a����ADKJ��p�	�U�]��O^����6l�,����iov��]T��Jl%�eM���z��u�
�4���j Zcc#��62W�QSS�zR_�����e����L`����Vn<QQ�o�J|D�q������v���w������O.��#�P��Qg�����>9��s�c�nV���x�	��yoG�G��DD4���ADDD�IE����l�!U%��j���[r_����X	.�*f��.�![�v��_G�.�����9����r
�]'��<�:#�$6�?����/o������vk��9��p��7��3��B����-������UP���)�y����J����{�����	`\W}�����h��$k�-;q��B�]0q���a+���iZ�iY��6�����1�ByvJ�e�����8�I�8�����eF3����w��������F�;�O{��w�9w4�%���w��q�z�M��������}��Y'�X$1�C��5�d��o��cU�.�`��=��w�����]�Zn~�f����T>��������+W��C�r�:����3NLO%��n����#�����"M�������*�h��q���������{6���i{�n�vf�ph_�c�~-���MOl�"�XC[�4v��{��4g�1�h����c����o�>m.Y�D�&�I%��&%P����������k9�� ����3�����]6��l���DF��H����T�������������G5�#JS�������b1��r�����r��w��x��y��>{:�f^��"�fe���r�u��e�s?������M��R�:��?Kz�����k��/�a"H��i����.�-���*����r!�(���_��o=�D����n��P��8�UsM��3M,T��.	�3\�������v�`�������i��������)�X�^Z#�*�Ps�TVU�#"33	����9�*�R��%������=Er:�C�k������~H��dr���D>r�VY��j��8 ��z��ZV�I�6����-s����oe���������VB����e����%�okYO�T�x�..��TB���o����;e��
&P
����*�pO�=�fg�oJ	`iz�_S92d��%9��v;�����4s�����/�
����L&�����]Q-���?�S����7=�-6~X�����~tZM�������������`��&B9����������S��G���\'���[��Kn:E@�D���@.�<�T���SC"�L`�6��E���H!2u�+���=���M����F�>r��<����x���}U�����Ec�S25n�u+�`C@b�Lj�5n���M���; 3
X�����	UY]XR����#��u<���kZ��g�g��8w�����~+�P���r��)b�h���\�y��l�����?�b��2&j�E.Y.rQ�H�2����oqb=L��+��q��IHK�]4����{$^f�p���������0�������
�o	��aL�����mL��q��l�o����B��\LUY���Xz�������u��*0��er:*�C������R����u	�:���?�y9u��9�_gg���-o�e��{y��B����s��
O����z{_���|�7�n��18���j
w�w>��i���+�j�@v�4Q<�<�9@1c��#�[��$�Z��{E_���L`ik�YM0Ow����kBR��m����DO���P��&tR���;3���Z	5���pL&�����_��&��qH/���&<�5$�4�>w����&�\H K����M�7�Hmc���5�(�xdD�������~ ����R566&���\��O���������D�Y�f�\v�e&�����^g���v����d���M$r�*��<I
���a�����^)���&���D~��q���!s����<i�2y�e+�yW��������=���<v��
�:3wZ�N�Zzd��+�����Lo���	`���S1�&>��6�tI�Qb.O�L/��Kx\6�o���hBg{��Y�5K,��~IL97��p��f����Ee�/�G���������,��������,��'����h�5(������E@��h�CX��������P5�����~45vR�Q�r�����_��Z���s��q���`������D"��y��-�7����b�
-��G�$O��M&���EVZ�{��y�7������}�E�����{���ifvV��;����{����/�?{����'SR�;������&����~��t�u&��Q4,>r�bZ��	-����&��ji�\�4���zs�	,W�sn�/�R4��X��u&�_�'Ir�I�Z��u���er���&�o��*���k��|K�"2�w�D�?��Lr�.��Eg����`�	y�s,^X0�/v^8+-�H�&L���OL���|+_QD�&�<�_v�������:SX�u���zw�D�5��������~8g��������:����*�P�O�P�&R�
����^�fgg�L�����*�>:V��7Z�P]��832?�`"�0-���~):����p(�,������e�,��Y8�����U�u����������|��Y����+M��5?�Z?O�r��5@��_e�����9�7b"G0�*
m+�M�n�W����0q�������1`���wz�^c7����������-v�8�l�I�%u��d���n�<����i9�sW�Tx>�s]�Iu�3s�N���g�4��JSg���i?{�}Kj<�R�E��w�i��g��������>�Ss��LNKd���Gd�z���q=�v�'����^K�/��������|�]���;U0�{�r�kd���n�]���aFN���g�����cs4�e����_��x�:��i�{�#&�7�!	w����.��o�7m�1=��ct�_�4c���@�4uY������j��1u�������������<��:>`�������)saC��(����B��������\l���[21w��dx�Q|���'��+�s�13Mx3�v�#\c�u��,4I�X��]�B6}�N?���Ot)���z��w�^{�v��i�guA$�4���-r��������~����O3�������/|�D"��/�`p~����q�q����d��e&r�����������br��	����o�+V�ha����r�W���0�j���q��o���>`"������=���]k�:���~"����M$�eC���:gMR������'|�D��OWH����J��&�e�/�uz�S^&��v����^�S�J��T�n7rH!�(&�	����i���d�Hs�	��9�N���W�\�;�����Ay����m��X��u&�_�'��-"��L��e���59xBb�v[&������-��X�a�|�������	�����������zM;{��V:�f Xe��������%%:>}V�,{�l~�z���v�(��AIMh�`��-&�p$J	`i�h�_�Q4������L���gf������u):;;��n���.��	}�R�����~-���c������6�H}�r���;����Q��q����������&�&
�n����Gej���gu�:���h� ����#�/r�bZ���`�z����pbdB��_$�0,����X������$I����(�Y�PS�$
�+M4��h"��9�
�I�����wC�PzJ�hb���B�#�,=Y�f����/�����qg�����������{���|����bX�����)�`������f��c����k�G]����{���R�i"?MD�	y����D"u��R��9�����DO�H�������Z�����������j��8����@z.�/�w�TUV�(�(6r��������d��Tt���_������7���Q���WW+���t��s��-�F�=�L4�B-������Rcm��E������t�Q�}��U�x��u�>�����u-^����M�:8Q���f�����i����	���t�D�����w�!�7o�k���A�"�,]_��������&����TTZ�fDf�Q�G3�[x��7��x�&�bZ��	-L�������U���B��'���TVVJWW��|}�Y4�����G������9���"u�������zo�M �_�.i]w��;"��p�����7Q�;.Rb#29�^���7<O.]�����x-+�$P;wa���9|<b"��8E�E��������{������^�|Y����x�Y4�"�}256h"G0�*S81�J,����.����&f%�^+U5zC��L�ez*ib��R���Y��.�H�vvA�^N�cv����>��*ih�M.��w2����z#�sr*@�q�*��-��<����sf��-]]�>���9��w��A���G�2
&��R��Bj��RS�,�P���8d��):�P�3�>��x<n\�611qV�������A�����.���^K��������e{�b��
eL��5>/�ioq
'���X��'N8m���^�7�����&�r$�:���JfQNO{�������L��	�Bx�y�(r�����]i�*l����-v�rP��)U��%�Hbb���y&�o�5fi��&�F�����U21�Y0QQU!���3y{��Y����4��J�=h�5vH$�3�8�������d{�B7�0��
/,%U4�w�^�r���Hmn�7u\�w���)�=�%��=�������������i9j;��Y'���A���W�y��z}},���{��N�>}��Xt�-�����7m�1=���rZ�1_�^c1�~����Xi�xd[�S���*6�����E�jV�5�H?�OOC�K2�^~B��mi�Kj��\g"���u'F�~:��9`����dvvF~�d�o��3��&O����8��������*Pk��}���0����Y9����`��BZk%:����2P)M]!	w����Fj�A{����N���tS�N�^_GTIM������i&��n�jZ"����*)E�	�$����Mp!>s�o�}�y���Q8q�p�Cg]�C#���
��r�p�jZ��������?e��zllL����M�^���a�����L�Y�#W�D���~)��~sqW�i�����N�������5��~sq�z����������G��~)����#������S"����Tf��Ig��^��5v������6=5i�#n��c�$9��	$��}uL�k����y��$ge�dD��	sD���BZ���c�7�4�%X����M�7�e.����u�z)�8�x��PREn�.���]1M"������{�=�������S�p���3����<r$��Beuak���=rd������	����c�	���L�L�@�R�p���z?z�3���#2tR��'�Xd��D�X?�v�kM�11�Y8���2�6y��^cZ��F�����_���?e"��Z���tD����m�^�'�8�SN[�t�b���������d6��~>n���.{�����1��5?�>����'3���
TJsw�T+�s�Wmc g�D���~):^����������@�Mh" �|����v�2-�����VYQ��z�p��3GU����\��~�@oF��������hkk�������l�2sGMM�tuu�9���-�@�7=n�W�������n��������\,���6-G]a5Gg��^�/6�n�Wn��DNq����er�O�����m=�.����c�jU�Zy���w�$�gd�����G%:�k�&���z\�������=������'�iZ�U��oi9�������&$��5R��%��V{���s~R���eNn��F���X):�l"��a��--���������k^^o__����@�M����"��������N��}/8�p���e.������9o?���`BO�C����ymMMM���b_����.x�Bg�G"����������jo�v��:&5^����k+},}���s����T�v�i9"�W�������������/��m��H,��A�8fo�v���������K7]'.�h"�.�1>0%��#���$���:����~��gZ"�"�9�����/�=����ug-��cz�o�[���U'-�ugf�H�2gR�#
����N�.�a]_G�.�c/��q�F��oi��q�Y�k�r]�3u��������B����F��7=UOO������.��jk�e�L�V
Kmx��Sc'%���5U����hL,��������=�\���E	UU�����)9y����~���[���e``@b�t���PB%�I����-���8q�D"o~��e��&Zx�ig��hm�����OX���q�]�z�\�����Rw�����}�D����+�<�N�r�����O���{����;������&�Mg��`@>�6m������4r��nr����Ob������1���n���{"�y<�6�
��/6�����h�n95j���~��O8w�Z��u���er���&��vKC���yO��eug��ok�����z������������_V+����W����y���Z	5�}�����7��4��#(����Cz}���^w��]��n(
��AI�4�������3�L�i(���kZ���m�	-�H��B�Dl�D�M[�S:��	��$��`B��9��Stf�l�=�1=����.�Dr]��M�)����c�OL(�x�*����`�\������g;Xt�<@9�:3������E���������,���OYu�`BUs�Y����c���=_[���*�8[�L�d���mv��+�1K�����g���d���n�<I��QRE���5
e����d+��J��K��x�]��T���J�����`bi�;�����}��&R��f���O���v���y.��
nZ�c��G��	=��������w����_���e�B��/�3�PM�uL�i��#��AUM����g�w&p���+M���5�D1�
M���:0�
A���zOg�X�G��hl2s�Q/=�]��)��	��;wfMl��������^��'_�D29-�����ING�������`b������M��H�6�������v���y.������M�Ld��K�<v�Y����������D���Y	���W��T��.�������n|E��g���_���e9<���^�6��+[V��{�����,�<���ul2--�����J�������'��/R��������i9z����3���eb0��p"�����d����l�T�%u���t�J�c������\kw����w����b��-&���;2��4��'���D����|b�9{	/
&��������������4�t6-n(�7����bE�N�by�{/���?4���+�/�^����D~��qy����N��_qq������yW�6G��H���W_|��D*����`}f�����&������o���j_k"p���T��;(�����!����G�$O�$���>h�}���YV���FzOY{�g�=������&���?�[�
8���I��n�����	�M87��lk�;o�n��ih���������B���_��G~l"��VIu�R�{I��.}�<��?0��[����/k��7���;(��	o �]%�k�.��7M$d�c��=r�5�����4�K�KW1�&�������vihh0Qvccc28������hB��R����rk�y��������������������^�����=M*]������o��<�mY��BY&R�������M�����;��d"pP4Q\�<�
9���O����2���I�.(��w}�/�����F�e7��k>i"������=���]k�:���~"����&���Gnz�3L�O���w��|6\�Qnz��L�J�hBy��;+����l��Ci"�,]Z8�G���LN�+|����6 ���WS0�Dhq����N�>m��O�S�����������f�s�Y&R_�X<������M8�~M�p��8L�4�?#�|�_.3M$�3r��~$��\�s�qqW�|���IuU��N7'�eBg�Pz�E�c��Bc��3w`��\'|��6�P4Q|�<�E��G��3r���Kd�~sdnu�W��W�J**����������l
�5!�yw�{�D<j}�N���]o�lX�?���������^���5��������l��A����
�s��x�6��U�V����|9q��<��cr�������,s�q�E�9�
�I$��z)MMM���@�|�G���{������Zj��e61m�IJ�~����6��)�v:��t�-����k�6���Zn#�K]]�TWW�����T�,�\r�<����z��R�}��B���~HN�g���jmi��X[�z��^�E���%6��TV�KCW�k#���=����p�[%��*5ua��H2�N�LL�T<)O_�e��Kr&)_�a:
�H�V_��7���x$a�G'���g�Y*}�|p�>��O��SJ%wP�_�"�/r?�G���VINO�d�/���:��KY���JEe�\�_t4�I$����N�k&����������?9��������\��Ry���2~�3M���/e��?{�Zo�Ycs�o�W9�;���9���;(���N/�	��r����hZ������7=F���G���_q�������bgf��^�[�=��qB��u��Cy����M��EZ<������L�������o�����5�"�,���z��)��hqb=t��[��C�3��x�����9w]���H}K���7������N�c��p������<����`q������4]����[^x���n1�#66(��M�n����������,�a����
ih��p{��k|&�l��:���������c��3����������i9�M�RU��[_'����Z���W�y��P��J�+%����������I{���s):F����?nZ����x����y���hZ���_f�U[���i*�=��u��;�/.�����Xz�|����,>r����9��r��0G��?�v���~�K�~��������M�h_?/G�t9����&�TJSW�^����Fj�A{����):���C&����[��v�ZP5����6ImS�3u�a�@a�z<`��~)gVM���n�*����3:q����%V�W��Qd�W��,���`"E�k��x�9�7bZ"����^�[�i��X?�������L$2K�V~�~:^��B;��wJd�~9���@��Ng��������]"9#7~�Gr���M�h_�g��������5(������q=�v���fZ�E���^�?r$��Beu�^�cj����=���z\�k��G�Z�)��� UUU����NZZ2� �F�h_�7����;qP_ksp���z���rl`�D������w�����;��������&�M����~)��,��G�$�D"
�������_��Y����k���|�ik��������n��[��������}u��>u��Dj9&R���Kq��l�3+����7��}��	��h4*���&����P(�]�@����7���a{?�������w&�k^!��I&��*�Z0��Kt�$2r�y���"+:\����5+��;���������?��LMM�#���������HC��0����_��o=�DN�����I��v��	,�_�s��x����p�����7�H�kM��&TlbD&��W>z�����L�:��M_x����*s�y��1IN��D����(�;��U��e���w�
V��;(������o���d���M$r�j�P���1������|�+��|�D�r������?1�Yz���eI��DO���]o{�l���D���I��~��D�j%�Tc����qH����=��	 �;w���&�o�nZ������@���C���[���+�2/^YYm�$���$��0�.���[�/���?{�Ke�����w�-=�����C���a�����D"a/�13������/�W���&��!���M���z��>��rzD�e4����MK��3�'Zg�����R��m���M������HH���z��j��}I����2��TzgE X%��JI�f�%9�3L��?����M���(��?�<p��y����VJ<����a���) %��/�o&_��[!W\w�	|����B�H���w�H"����M$�eC���:�<��8 ��z�����@��y������H�������7��p��{��br����D87�)�P�xF&B-+�HD��e�@amE��l����w����w��]����Kkk�Y�o$�I��Ig�����D���J����x��%:ZE.���y=qRd��\Q�z�\���8��\���g��6.��e��9��'$6������Q��qq>�*���v�x"�A�\6\�Qnz��L�(��?�<p��y�$6~X��������0��������!d�?.���M�9���o]n�s)��3MX(Mx�b������{���������
�k�.�rl����_s�5�~��@�M��&�~w�D��N��hb�}��_�C��m�ab���gL�h���c���8q�%�����m�8�_o����D�s��#7�Y�ddB��&��=�m��?c"9��."2�w�D�����C_�������\�Y�����F���!�Q8r��y��������g"�u+���u&��[4���F�y�GM��xB^�������
��-��5��Ko�#���:�f�c��Bc��33m��\'|��6�P4�Q�	����{�1��)��j����n��8��m��o�y�	$�ss�h��mh��)�gg���Xj������N	�6�#i�S�25z�������5����������&MD��R���~���������=���aq�e�Y)��/��n�Et�w����j"�`@de{����	gY������~��uz��82 ;>�c����o�lX�f"J�4��O���v��K��6��i@>M�r�#�����ON��V9�����yi��LdEo���sTX����E����s�'����|������nI�M���������W^-/��3p|����'����D��B�O/m������t>�9Oy��c�M&�;wPi���olc+E��<(��e�e��9G�k?�]JKUM�B���Ud::�)����f%:�'Sc'%��t��k����;����:z�l�s!�>g,����m�A����G������B��"+6�������$t	��#�s����N|�:�.�Xi��k�����Wn��DNq����er�O�����m=�.�������������Z����
ih��p{��k��S���,�l��bl�����*�P}C"��

Y��T��r����~�PF��r�����8���|������7��z�i9&cvaD6���}���O{�i@vK�hK�&v��i"��]q�w�������Y����t>'���L�������^����S�:z=w�g>��,���z��%���&r�;�J<j�O;��������[^x������$66(��M�n����?;���|���0��B?Pi�y�SV�5�Hm8h�5��U��w���<~F�����Ct_��M�8z��9$=���t��f��&K��K26G��O�j_+��;Md=��{&	]�#:������x�d�>���Vw�3dG�D	��Q��g�Yw�����k{�z���w����6��w��/��lU�A�o�X��fv�~�_��P8��p�^b#%�����G�65�����{���#
�3Sh�rP]U)�����o������.���?����������SU�����=���i�x�y���~��_v��D&c��!GN9�k�:{����Oi����r�[8��E$61l"�{����%F����e|`J��G���n�_��\(�(A{��5-�|����y�i� ��������W���P�J�=)����g3�TT������p��tuu���Km$�I�M�������6-�����t�d�l-�LG�~V��R�$%���$�����Hmc ���J�k��x9�C��>!u�W��10�����)g����_e�����{�|������?�qD�Nf,=���"C}&r�X��^��nz����5�M�����P��(��7��M���yPN���^�r���/��u���/y��w`���6�c&���N[v~["6!�������	6�I|r�z1G5���6tJ(�i�kB��	sg���q:^��bNX������8�M�3������������`��	=���O����$�����9��?�S�CA��������.���w���{�3Gr������:���3I{y��@����s�;r�Q�:��"�����A��]�/�c�_�#�u^��v���~���U^�q��r����S1v�q�������i[���)����X�������������!��2o��uL�i(�����}�v{�s�:��������rN$�7ob���94��w_����o6G��2:vJ&����9.�d��v����x<2��TV��y�������Y�f{�q�:��St\d��}�k_����}���s��/�Eu��W����+�'''���c2<<,�����m=��Rt�����?{�D�7���IdU��E��^c7��k�����9�������Vih[io�v��:���=lZ��`a/�����,r�E������Fz��y���KZ����6e�����+���9x���5�-&r��eb���i�M����w��w�xb��������o�! 
m�n�{��3�jsY�E������l��&�n�r���l����.��^W
z���M:����{�{�Ap�n���6�(S�:^k��`f���u�6�G������/���Z93N��������i�=����1~4���d��O�H�����_%��S��Y����k���|�ik����O����>b"�������H}K���7������K�1:��.�����Xa�jx�y���<�#�@�i��"k��Uy����y\�_���k���|9���������7^b����M��}u�����_�izi��@�4u�$�����
���z\��������[r�E5y�I�s�wb�K�{�������4���;%���
�~!_��.����hA��KKFdN����P���������j�l����1~4��7M���S��U��r��y������$G������=����c����JVw�3��t��e6��t�^�p�y�G�T��}��/g����_%�}�&�|�
i
����cz�]/����w��������5(�����nz\�������dw����7��}����t�I��:m��.Mx�e��7���>�������B���|�Mq_�\�]�hTz{{M$���#����p8�/o��;&	�;��:����JLOIUU@*R�Y�G&��?����RS�l��tY��q�t���e��j����$��R����9�I��KtX>{�Ke�Z�L(�d2)���������|:;;�-oy��o��>p�J�������~tz
����/�oV���[!W\w�	|�����c�v;��"����v.��'$61l�W�5��7n��~��{n�����&���\	�L�������y����n286m�$��;S����;��R���y�G�#;r@�
;��v����r�W_|�=���m�3J�e�?*S��v[o���h� ��;XR3M������{�n�.���M,��������>���[U�{�x��L����.���i�DU tA[����)"[�$��U���2��g]&uM��>N*���B�D�0~���U0���������}u�����)�P:�D!��t�^�o�����	U�����G��5�h�U�5-��`Lb�Nr�K���L�x��^cZ��@���������X0��I�)�P�`a7O��9��}�eIMx�<�dA6Z���7���ol���F_�-�F��t�������Ns9M��|�?�kXZ��I�����63��a\�������v���9c�8~�������_�����H0�+VHkk����������K�1:V������I|8��y������]QR�y��{z���eU�Zy���i"�}:#�}Q����h\���^c=��St�������G�cn�<�u��a�rT�h���{p[R�s����U����-��U��������7�����=�g�dJ����{��z�x��/���;U�����@
@���?����%�5������v>Sc'%-���K���^ ��is���~���������s�.��br��	�=����7?x��ke:�<��e"+
���^�#Z.�^}�	|Dg�~�������VH�>��?SC<i"�/�o��j�M�?�����_�hn��z�����I��bO���������ydG���o������b"���Z	5��(7�qd|`�D"/�����l"K�b�<�|�U�	���;R�-��������\��%&r]��tP��-oA��\�	���ccr��~��+Pu�+�%6rI�&$2R�����o}��]��������|�D���Fik�_%000 ���e����d����MT���K?0��e����<+QD���5����z��5��|�[����&��L���/��<!��a���_�b��������������9��x���|����uI�b�hb����9���y�r���S~bm�<vmc@�s��3mN��eK��N�y���lk�t-f�cI-��~3������,����� �J������^�����2�����qc�O<���x�]���������Z��>�"1p����L��n{�i9������cr�9���������-�7���!��G�o�%��)�P��~�/?��]01���5��L�uL�i��#��9B!b��"=�`
��R��R���]%�omO��W�Ro�5QR���1��/���lw(rE!�.�����X�����I{�PxlD�=<h"�.�QY��@P��1�ID�Z�c�e�r�%����+;����p���8�9��:$��):���6�r��W��_l��?�%�����~��6|R*j{��
���H��WL�hk���5�����On��Q5'����K��[Ld�����L���J��wK��w�3��1�����I/=R]���n{���G�e9t���mk��s�G?{�{�����D��:Xe������TH"�<+q����H����&��1�Da���M�cn�<�#����z&w���&�TJckP��s$M&&b�L��#"�>���U�2��j1s��~�G���o��Szz��u���RXc��=���}��c���2����������\�h����L$���.

��rQccc28�.�y���Ur�OL������M$R�x��<�klr���������,S�s/��R�����{�����b�������J|�1;V���R��9E����o�{�D�`�U�pb:��Xf��K7���<���d�;����������`L����-����l]c"K�b���(�8�<9����������D�Q$���`�$b32��F��l�^�_�:X�3���)�(s���&����===E����{>�������\�ue�|�}/0���>��3E������f�s�Y&t�	���7�p��.%��~y����D������Ur��%u�W���L\���J��tQI.�W�����wRQ��D�����
�O�c���LY��oJ �a���DrF������O����gMw�|�O�Ju��Z�n^}z�����m"���P���-��K������L���&�����C�c���(��F9�D�u�\6\�Qnz��L����f���wT�:��7o6��y��y�U����{��	������i��c.E�!b1g	�l����L(��R��k_�����lsD��G�s�L�v>���@]���K �}�`B��u2
&����+��	���a���h2Gr�>����������i��`B�y���@)#��OZ��g��D�i?
&��	����w�}N��Q
KmC������&���0�k���w�����x�Dizlhh�D��RRU�$�^��^�BsD$=%���]&�����{��W�����~�_��x�NJx�������i��O�H����#�i��c�*9��#���H��)��w����u���n�N>��/�3f��\g�:4�����Kv?(Ee�{����[M���G���_IO�VY���R����f	�����!���O�q�z�v����tX):����������I{��s�B�ctl)���
��_f��$����w�'c'~h�8ZEV���_�3(8����q:>�i�Ke�����/>���p����p���t����������}��C��,����:�"r���c������E��{�����i�5�������P�&J����}�v{��������{�������y!�Mz���4�������i9��Rl0Q��u��:���{4-�Mhwvv��166&�����m7���$xE�\������_m��+�"fr��j�K��\�-��"R_k���X���6��=������m��~q���r��GL$R]�p��o���p��i[����c�����L���6�����:9����*Y�|��������s������9kg/��
�����;��;
���|����T�f�B���Z��w�6Q��^_��s������/�hTz{{M���
��nZ�T����)�q�Q���w��|��NJ<�|0~��V���}��F�����O��200`�d���&�x�;�������c2mm*:|����������_����LdE�	�8n}��2W-���M�v�;L4B-�}�~��X[����_�������5R��w?��X��&���Gnz�3L�/��7�Kl�����;�~-4���q��F���;2�Mgt��?=s�R�b)~��<�#��9���3M�M��z�������������q'49*��9S0�
]����#��u�����I&����}n��	�}���)6-�8��g����z��o��	���m�1<~v���<�/��/tK=�T�H����6��,�PzN��x�����;���������#��#�g
&�{<�F��X�l����;��
���e\j+�) �l�2��|=���\w�d��\n��6�r�|�����=|8�C��@�����{��?��Gv?������S&r��aimm�7m�i_S.�5�18f?i�2�'��������Z����G��5�h�U�5-��`,g����������������y3M�]��{WC�$��m�Mx��p�v}M����w�J�]7@����v�r$�3?�����{����G���L��e��g�%R�zL���[.�8%2`
'����i����I�rT���������t�);J������g��D��tFF������LO%���z\������L@���,�����|���YL������}r'�����7�J��g[����HI]�B��PX�X��������P5������v>Sc'%u>��t�2�����n�����o��?l"����gG��b19q���D.��2y��^e��7��{y��m"��&�����k���:���	����	a�O�X��U����� �9��Df�+~.��0Qq���%��k&�o[!��fe751$���t_�_��P�<�[bn���r���:�s�p�F���3dr����B��K��&�Qrg#����;�w�K�hB��.��|�{�r���E�����2�s��b'	`i����'���GL$R��B��
&:["6!���&y�s.��~;ws��O}�S28��466J[[���e``@���et�n��n��hb�*���W�8'���WZE�e��S����t/�O���+��"��v��erJ��kR��(�P�l��%:����~Y��������+����5g1}������k���\�Y�����F���!�Q8r��y��[�E87:��\kp��{]s�|��,�����X($���P��\w��&��
H��#k��L���%�L����^+�V-3�,���>�!9E�G>cccg�,����~���1��*F���2����������S>C&���,=���5���4�<Y*E����@��~�k��{%���D,"c}�M$�eC���������ib���3w��o2N��k�1��|(��b!��F��E>�Zs����	���}��o��yI��IX������_I'��.�QYe��VZ�������%9R�����?^r���w�����O�D���H�Y&t������-��b(F��H<t��`��=��$�Z�(2�'���2����R)�8pd@v|��&��	I]k��wK�����O��������g1)e��w�|���0���1 ��*������
I��25�.PS��;����3�Q4��F���,�&�;$�����[�p�?�M��������G�����~�D"������{)���&�.�q����y�V��m"�0I�R)�P������{����`�U�pb:��X���n�T��SMT\C�����
<n�����)���?o"��@�4�%X0G�b��21����9"���o�e�&�_+���{��B=��A����y���h�CX��3����O���)s$�+�w����/�@�NC�r��O}�LDcc�����
@g���&�.�q�
7��b(V��z������H-���������[i�y���&����W?9l�����E>�'[��jq~�:q@>p��&*���P���-��K��qN�9��������7�@)�hX|�<`��s|"XP3�����GAJ��������tt��>�b�X,f��MMM�)�P��~S��gx�#4�S�1�������UVu4�#�i��X�A���W0�����rF�`A}�?�G���QS�$�p��u�{��92h�Cy�;@�t��x<n�4=64��o�;�/��������US�����:��t��?�#9�?j���}���)�`�i����Z5e��	����}��?:h"��@H������.	���-l�5�oYe�O�q:�C����Rt������������m=���B��X�����1��5Y��v��e��������'|^8���? ��2+J��V�k��7m�i_�4�Z��n����9s���`a/����:����>O�1,eM��C����>b"Gm�C�\�nU5!�������|l��U:;;M����~{�����1~s�������U"��oOG���6g������0h������'N��{��_�����5R��e�i�7m�1=��ct�����P��u�m�[���5+lV
o?�N������3�Z2�4P4�w���7��[�#�9������E*�kM����~):^���Cy�������mr�UW�#�i��c���"C�&P�&�5�"�9~�B���VC��n���<n]�o����L�Q��;�8"E��97�X�����om���t,iZ����x���&��d@uu�&����K�<TTT��^�"y��_.Oz���������������KA4.29u���{EF&�E��X��:�EB��Az^g��o�w��[���f{�s��y.�e9�
-R���E�i���~R]SmZ"S����6Qv����/�=�E����O��W\!�y�k��������|��i[�����H�����MD�-Z�`
 t&�B���C]�z���<��n�<[4��c��8���g�Q�>:V��GuM�k�L�rN��������g�(|��wo������TZ��*��I��/�Hj���:z=����f{+Z�0��D�\W �	8t��n���(E���IO�^���]R�����������i�&Q�1����JZ�S�$�gd�/*c�Q���ez*a�5��z>E���Z�
A�`AlX�!O���D"����$2�j�����a�=^������M�3E�GLc��*rJ�z����?$�~�>���?;S8qQg��OI��{E�J�8@�����^S�����UJb�	�wf��Ru�A���\fC������{�^�Ci�����?�\��7��q����O�@��F����k"���	�
������}���O������Z	6tHU��?�������%1=e��|��?�_��D��1��9��g�Hdu�H���`�����oK�Z?��	�?-��`A.[%R�g%��u-]�#���1[a]+>�3+�L�>�����{U��D��Kt������v{`,*�Nu��S���B<n]���8d"���:y�\b}�5��=(����`�2�_�m�s�<!�	� ���V���'[�����/���tE����V�x�2��js���:i;���8 ��z��ZV�I�6���P��1��Y�K+���^���.�9�xd��-�h��3Q��B1��d|���������B��Z��r�uw���L�l��I���o������
dC���;w@�|��t������4����I���[��hF��)�G2?d}������l6��x�&��:�"urP���i��%+D��������������,����S��5��E]"���
v����N���V�nk;�8]���������;����C_�f�+�����5R��w?��X�a�~���������=_E�hBFN������U'�5�	�R���U�Z-�~�o���9�-�H�uY�H������&t�_/E@��hX|�<`��s,�XP��3������j�Dt���������KW/���@�,Y�2]0�����5��D��Wgq9="2h���^c-dpL�Y�RJg������o�D��'��O�S�>:)��kZC�E�\�'�\���/-�5UU���qrR1��!2;3�D�>�i�6���V��A{���[��u�������x���GMTeE�|������;������n}�=(-
"5Yn��e4�<�"���y�����M����y���qJ�����Z1v�q�������i[���)t��R.�P�U����3�Y������L��g*�Y(B�I]S�����^c=��T�����i�:z�}}<}\�#�&�=����������i"Z;�Kt4�s�lq�/���Ps��/���m��Y���vM�a�������c��A�8fo�v��U�7��^[#�c�����*++��;$5�'f�2q:�*%il
J�>�{�����):n���*��������_Q4XP_�����	9jBa�m�����Y{��s�_�pZ1���|�j�%��l^���v	w],�5���g�RQU%U���8��>�W������z�l�y>�>�b���Q4/��`}z�	��o�S����1��`"E�k?�����~j�����q��(�,�G������D"UUzW�
�
wKM}������k����w�w�D@y��9U��B���)O��)���'�p"��dB�����g�>�7E�����TQz��:����.�(��A�y��h!F�u]�����	�����?dZ����6�(S�:���}��M(OG�E"Z���$���Q����k���{�q~��p�a�� �p���t����b����S0��5u��	}���i�E���&��~��P���.��rA�`�<<`Z��Z��T����������1��QXjE��Y�)��,����5~R�s>��5N���;�6iAD��@����Rk6m�1/������!s�����M��$q��c}Q��JsW������	��H&g��#C&���T{������#��u�M�����i�:E����5X?6z��;�O�����f�nHN�d��Wf����m=�i���]f���Jf3'���SQ��,��X�{��B����h� v�2��A��������M(/�g��"�9~����Kq������������TV���H��2�T�O��)Ng�g�?U�TK��4g=%c�Q������5vK���Pn(�,��.n7-��w|g����Pb�"��	,:�D!��t�^�ofgg\�'*$^&�5�'��O��N)���>N���;F��GuM��#�\=3F����g���W�k(��eK`.MDUU�\���D"3��n{w��t�2�:@���2
�������J�@�A:z����vI�=s�����,(����T�TIC���$s�	[l��������A��(?M����E���$b&�n�:��R�_�fZ@y��^���>����&\�I:>����K�$�b6K�@�S!0+�3K�\P)�kJM]�e6�|�����rD�`��n��M���#�����u���^x�i�����F��H��U2���X��}�"��azjR&�{3��H��:�\b*]u�c+*�����.��23Nh?����	��Y��"�~�F�$��9.Sc'%��t��k�����n�2K��106��wI���2g�p��r����l�����/ZU ����'F$O}c�%9�Ku������r
*�c�k�d����0Q��D�i(WM��x��i�r9t	���S29t�����P�_�����������s~�����{�s�DN�8�u�������<\�g���l�<��`X��Y������4v����Uv;���5c�+2�Y0Q]���1����rD�`�}�n��q"����� �����~�'N9{��MUu@*�&J��	�Kq��m/�c�j:����X�7����lE�����:.O:��P4(
�9���V^��K����wz_���>~���d�	�%5��l�O�����_f��2��k���)8KuL�7m{�Nt�36���DG������I(\c�u����#�?b�@9q�)�l��Q����M��D�Q���5�HOO��B�i�,%���<|��\vq�TU��#^�Q|��L[��������X�����j�L:�x��F�e�����kS{�=w�k�U��n��9{V��E��~IN��������Y�!!���Y�O��g��"U����;��������L�4��G��*�{Z����w]o�U��:	�����>2&��Z������]cjbZ�NEM���M�~Q�D�cz*!��#&���;e�r�����i�&����J'wP�_7�
9���E�?:����};L�hm��TVk�HdJdp��4Vl�%]O}����[41��%k���\��.d��+f������&2g�w���>�r$�HBFO��Xh��|�h(}M����?w���9����o3
&j�E.Y.rQ�H�2����oqb=t}�~���������`�U�Z��M�i�G�z�:^ZFOE2
&�5��L�`]���������\P4��~������in0��_i�w�?���U�2#����kB�^#�-]Rn�7m�1=��c�����i��{M]��zV�?�~N���@���-����	�%(2�+�r���U0����_�{�b�_�-���/hkh���@����*���B�����JG��1=�Va��k��c�ce��e��\�YI��������	:>)�IW���-�u�G�cIs0�T?{\�[j_����8���M�'��2�&�����D�7�H}f�@N�~:^��X�jj�:Xw�[eu@�F$93Wt� ;W����#"����5����9���pY�c�c�cf�Z
��9�B�N�d�x���IL�����T������&�Kvd�L��Tj���A��������yfTc�C���o�r���	�����N)I�c��GM�L4t�HEeUF!EUp�Bw���k�'���1K����=:L�rN������;;k�7��r~G��S�v�i9"����"S�ax�SJ�	5w��T�DeU@f�3��sW�Tx^�j��^�]8�������CCrzFF��2���h�^.C��q=�V���TO�)�&����j�k��D"��b�\&]�:^�S�BM�����L���
�}J21����Y��F�pBC�xf%���u��-��P��^T*$�:�O���>��Kp�L��e�>�$G���^��+d__����z����<k
XB(�X����nZ"��"#&�A�k���R��?�
&l������p$c��[Q�~	��.�RUS%������C��U'���Km���{��������
f����^#X�������=_����i*,eM,A����N�.�������h��M����%�[8��E$61l����,X_-���_����H<��Y��A��5�c�O�g����?�h`	
-{�����D"�i��N�<qR����d��a'>d�'LG�Jk\]�SM�?��-�����D���	=�3��;�o"#1�BUhe������Vk/��{��+fh���	�%�����������Kp�9x����\�Ci�Nk��U��Km��DNq����er�O�����m=�.��1:���cI�L?T*��+$����5�X�?h�5��U����'M���	�%l�� M=/1Q~�O�����N��	�����c��m7��c�,::mZ�����&�����n�x(M,a�~�2��]�j
9i?�_**���"�
���i��c�,�Z���1��`"E�k��x(M,Q�~�!;�C������Q��Mdu��owb7��g����Y?yX��S�@n�G���������H$�2����S3~�@VM,A�����};L$RS-r�r���E:�����}����u>��5.:�;�Sd��$���p���t���������D,s���`a/�������h`	:���M���!��`=��:���M�S�256h"�������H}K���[�M�zL����G����{��|������h`	�����%8rL��y�R��~35>lZ��e��)zL��y��EE��|k�/��cI����O��u��P4����$$2x��D�kMc�~:^��G��������]0����O�{��T�.�����&�M�]<2m�Kq��rA�D���k�TTT��6�������Q0"�mZ���5����)���I�����Kl$�c�QD��sW����X�F�k_���y1�5L�11�Y8�����G
g�`���E%j����U|��o7-����6-G$js�x&Q�^���#�29tr���p�yGu`���
�K^�F�k_���y1U�TICk�DZ 2#�}Q��Jt4.�S	{����):.�2�����(��9�;,�����PQY-u�W�Hd��%&]�:^��w�D�l	��J�8�����RS���]������{�^�Ci@�#��I�-�����e��}���o��l�b"�;����
�c��:7��M��g�\s�5&Z�hTz{{M$���#�P�s��������?k"�K��47� ��	��N��������?������Ay�����x����~Y����<!��a�G��*�{Z����w]o�U��:	�.\Q�����#	����u&�:��k��z���v�(�6m������tr��u��G6�<`��sM� ��b���)*��qA����<����H$X-��#{��L����]��_�����5=~�
�Ee`��p���Q{�P����<zb�D"��5R��w?��X�a��[�L^��b-���M����N����U���T����g�v�_�������,��h(}M���y����?�&J��]�2����ug���1�H �0�~�!9�/�n��F�z�O\]�H$�,�18nN+7������D�s�������M$R]���n{���G%2x����z�se��6O��&"#1��\���1 ��*������
I��g-���\�%:(�JE(6rg#����;���%��{�S_o��������N������t=��^�|9�@�h�����������J�^��R9�c'���>�O�������`�Zk�bL�mV���L���u��fLT*��+$����5�Hm8h�5��U�����]�B6}�g����<�F�D	rO�u�V�Z8��v�i9����7�,�u/��4���D�i?�_���+dMw����A�8fo�v��o��,
29�����Mg����`}�D��xc[���#�Y�{!�>��z �����(�(1z��B���)*S	��c���)���z�{/����t���������Rn}���{Y�9�[wk��W��]�#W�DJM]���RA��(�@N�)*o��f��p�����?���{�[Ez�DVwY�v'v��}�8�������rrh�������W���@�������*�2B����w;=�q�F��o�6���]�7�������{�fWz��Ney�wJTT��R�)*S�����e��V{���k���D�#�Joo��Dzzz$
�����<��+M$RSm�}�i�2������~�X��\���Jh�SL�?����@�oEe��6.��j�e�M$dj|Hfg�v��l���^���x���c�����?r\���1�H��:	�:��|��2|<b"�W����y������5�~Yc��(�6m������tr��u�;r�������P4Qb�	M(�z������}{���w�������]~�Y�\�<{�D�N<v���'�MV��gL�/����|���m"���&�k�����"��dB"�}�5GDn��j����M�����������j%�Tc����qH����������f�P(�@1������?w���9J���
}�_H�@�8M
B��2E%���i9Kp�+�Pz��T�{���}��i93Ld+�PzL�i���=x���EVw�3��t,=�F>�~:��	�b"�X�4Qb��G���7o���A�����{�J0�u�E!wT��]M@i��I���/l"�U�"�-&�������&�\���IE��K4���|�2���PS����/�������i�����v�o�����=~�D�s�
I�>`���&�e�/j"�-O~�����5����w�3����Gv�<����yP4QB�o��\o����*]4�|ST��BA�c�d� 9v�D~�:��_%R_k�<&�D5���i_����&�������76�HCG����&���c2��~���W]&-�s/]QjN����~��L$R����`��	-���Irz������t5Y��,i����V,M�X�y�F�@9X���s�}��o�S�z��]��H�O�5e�&�widK����/�rD����2
�{?����:�=�����D"3�3�Lc�Q���ez*a�5����	G�`1��,�&J����M�*(�No"�;}��;)�wghr,����h:�D!&��������X��9!������c�8�D����X�t<�D���i����{�����q,6r����e�=�������q>�-6����������]�\���Y�L�<v�F�S������������v���J������j;��I&d��!��I��U�t�_�n�����}����#�4QnW����e4�R���{*���RD�J�b�<(�(�u>�Ior!����===E��`>
����L$��l�^81:!r�_$�0,=�K��.=�O���a��7��D"5�MR��uV��LD��$>9j���x��������n���r�����|�z���w�����
m��^�1'�&P
�y��;w��8k�JM���������&��Y����M�L$�vf�x����g����N|�:�.�Xi��k�����-zLd=��Q{6��H���c��m=�.��1~/�������	e�2�#j��V+������]D��u~D�0�&�����M,U]O}��W<�D��qgV��G��i'v����8���%WJ}m���^~#:zZ&�{�M��%9���1~v��Q��O?a"��~�R��B�I]S�����^c=^e�O�qGO2����\P4Qb���j�|�{���#
�3Sh�r��P+w��-c��\����1~��_��i9[���(�o�����������`1�����������4�)��m���w�hn�������&$n�����-u}O��X���=~���~��������k�R���.�\B��b�����)��w��=R�������V��i���2�����~�u�7�|/j��s��������Z�#����mHq���J%wP�_w9#��9������2	��o���o��I�|4A�^���;��;v���y��	��H��@�����D�[������js��h����G&���H~�<��g����}F���9�4��J�)�|I.����L�H����*�y��MM�x�y�F���;w��%d�������B!�.	7����i�D}�3��`B������*	5�KCG��i[��h_�W#�M�Q,�e����:9�b�h��x���;0�J"x���).�	�q�������o���k%��!5���i[����c���������i������B��X(�(1��4SI�]�v�#�NR��t'��7�lZ�i�C�M��a���K*���uC��9��?{��i��.�����D"���i�����Y����yE%F����9�4I�s�N;a����o�,�u8.�#��MK��qY���=�}R�1-�Y��J����������G��~)��,r��@�D	�)+5��NB�����<%�����)�TkM+7w�k�/�k����^kZ���X��	=>>3���O{�i�x�y���������w�w`h� [2A�i�A����~�������D��o�k���fU�Zy���i"������Ee�?*���LO%���z\������L��"�(�
k�����e��}�JN4���^����H(2�-t��������jj�Ps���%:�/���&r�^���r�s/w���_�Q<��Ds�p�F���3d��i������R*��R���l�y��s��i�e�)�PS�C2�L��lzN������@�g��G�Q0��Q4���3C|�����g�7GDfg���Z8�����Iy������Y&R^��:����(�y��dug�������%��3�&P2�����^p�l��c���'Ge��!{�xt������\����z�r��c��c�M��7~Q���=r�uw�{�������'�/�&Prnx��R_[c"g����i����7m�g���:�\UUV����= ��	����Z���m3N��}������	�$-���u����Z6^�-����m_���>�}(�dC�J���_,������e��������7o�b� �&��Mu�@!(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�*�mV7n�}��i%������{���������
�k�.�rl����_s�5�~)�F����k"���	�B&��M�6����MT:��R��� �����?w�����}�v���{Ltnfg���yi����n��1����sNH,0�(��b �����?w���9J��&
���-[��;w����r�b�h����m�6�������I"���� �&J�N#��g�{��B�\tOo�@��{�n{:�������XH!�.9@�Q4Q����kZ�k����.�N;��I�\����Xp�$�7�P(r�b�h��{����L#9��7������&���
�B��E�J��,�7��Nn,5�<nM� ��[�n5�����y�����]?s����uan��f{�I�t�J�b"�XM������"u�N��M6���Im�NS	0_�yE%l��mv�`���RQQ!;w����l�b�d�B��v���3@�"�(�&J�{N]�S���>��d�&��.�By�;6�� �X�6���7��}���%L���;4i��������B�����X	�h4*���&����`0h"��P(dZ��i�&����J'wP�_w�#��9�`1sM�oA�w�6Q&��B��p���\x��u���M ����L�@�jmm��b�h�D�#;r��b�<X���x����M��]�o���M��NY�M��o����
9�b�h��hB@���M��,�.�����\dK��������,��(�i��M�RN��'�r���{*���RD�J�b�<(�(�v���;w�����/��-�	������`��P4�R@�p�����(S{��5��t-P������\P4��4y�k�����m���Y��d���P4Q�6o�lZ���n�<��{���r�sA�D	�5:u�H��
�����{M����	����yE%d�������y����;�d.$@���,�&J�w�aZ�B����5��n�jZ��%�1I��D��X(�(!�59���\I=�wk������2�v�m����-r��/�<�����krz����D��-��E����Ng����yK���jc����o�>mb�����lgs'�����������}�Y������O���������H���GB����c��M��~�N��T�n������?w���&J��A��}�tJ���M�[�S�������"�(6�&J�&���c��4�������w�^�X\�<���>�Z�����4p����D(�q�SU��cy,6r�<`!�sM�H ���hX|�<`��s,��E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4�E�,Q4Q�v��%��������8��o���7r�b�h�D������&�l���q�����&H$��D�PLM�,zWE�	
����:r/��pV7n�}��iK�N!�wD��q��U�;v�����V{���k���D���~
�����������H���GB����c��M��~�N��T�n������?w�����}��}�!t�I�\���h����)-
0�(�@���89����s���{���c����:�%&���wc�Op!�y����t�������m�L��y���+1����_������E%��{�1-��[������'�
]3����y�9@�Q4Qb�o�7o�lZ�;�e����c�i�?r��@���k��\��h,5�<nM����F_���)"+**2����<u������5F@y"�XM�0}��I�-[����;��4]T�k2a�D�{��I��p>�y����f��q�F��o�6��i2�|ib`���&���n�~�h��8����IU4���^�tttH04�C(2����i�����D��;(����������r��9�&JL��w�!�7o�X�S����'�wAh�;v�(�o	�.$�KQkk��E(&r���P3���%D�����g��p'��$�&��Miy��n�jZ�G+���(U�D���A�#7r��(�(!�$�;"tS�<�&�4�����wI��&���LJ9���y����t��(����I^�T�:-��n�����M��?Z��=�s`)"��o1sM�	���;6.$�����?�&P
�y��;w��e�����{M�t��\�&`��
��<�P4Q�6o�lZgsO;Yw`1����&J�}��go��;�e��_��^�7r��@�D	����-[���������������\s�i9v��aZ�]�v����w7@6�<�������\����q�w�Vn��{�i�w�m�������9�b�h��x��,�����\wF����kJL=�Nd���"�X,M��t���}�$��w���;7r����N��+������T��A��X(�(1{��1-G*��]�S���z��������M+;���$p_[���7y�����/r��Pam����q����O�X��
���;MT8M>�&�{��\�N���w���6m������tr��u���8��f�(A:��&��{!����l�wEh2���:��0�y��������}MdK&�9M4��|MRh�!u}����k�\r�bby��s?byP�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�(�e��	P�*�mV7n�}��ie�h�����e�����p�L
�2��P��)��.�M�6����MT:��R��1��y������;�h��8�/o��;&Z>{�Ke���d�����o�����n��4�y���E(e�<�D������<(K�4Q��������O���k����7�5�\c���k�.�J����_q���H�=��c��`�0�J9rPL�<9�Bq�(�(s�B�������r�w�K�#�2v%��3L��C`�^f��Fi�r��d!1p!	\�"�a��,�d!m��F3�/�03d9���X�;�sF�9]>�ynUwUuU�����������z��������O�Vs���7����6?���n�� ����7������[o�=������ms��E#������g��?���tI������ls�����{���s5�R4�9�y�<��ys�y�O�������spK��x�W�:�hu�
c��k����!�������.�,�����@-d��<�'�����CR4A�b�5�������N�6���8O��wosn�y��y�6���h��u���P������:��6,����[�$�G?��i�yph�&���o�r�JNO������G����yp,�&��[o���um��*��T���k�[�,��`��J��1)��:/������)�d,+]�����������_"<bP�ypnd�������q������Gq��|���k�����^�<���|��_��;���/�����m�5�����m���6����,/I�������������`�r}1p�����:�x������o}� #��W^yeso��u���2����2()��2t����V��|~�������������'�us�i���O6�=}���o���������O����O���l���/_<�|T>��������
���>nj�]����<~�xs�|��s�n�%��&��!��$�x���SVf�&*Wk�������X��:{c�v�[nW�v�8t���]b�o�����m��q�l{~�C�+6���{P��]���5@����>%?�+���)��|� 07E�3��5��
��$��m2N]��y��W�[��������
	B�^<>��M�%���:#(���)�n�����������nnN��1}������ �)}��y�<�"���&�V�ZC'�Q���5����YlK�t�{��<�+O�cS����r}}���}�����6���M��\^�A5p9�������wn���
|��!b���y��{|�>��
��P4A�r������:vJ�����0.K��r��-���d�o�]V�9��X��u������,��u�]��/�����E�C��v��%����sZ���8�u��@p�2�k2���<���	��cC.-VN��z}�D=&���11.�F+Rw�f��u�����cB����"<x�`sk���6�r�e���]������`2j�h���U�y�����^���
Z�^+R��7�;&���1�����.��e��@:�,]re3�)���1}J��M>�F�!�����(��jc.WYSz1Q��u;y����m�}��>������c�<P�����!���p*��}h��~�v�y�<�"�`�MP�<��vi�r�>u���N����gWhP����G�0�s�����py6`-��)��,A��?��5�k�h�����K��{��]bK�N,\\\4����<b��M10�}�u��(D%q�C�r�+,	C��c���Y�Rd��y5�y�f�&����U�{���
b����`3�!�R>���XW��u���}������S���%��������5S4A��\Z�����l1��	|�������Cw=�����J@�s�y�f�&�c�.-VN��N��Dx�A9p,��c;b� ���r���b�����zvmo�ksj�a�p��s��>%�J�C��2���}�����\&�`MM���]Zl��T>x�`s�F�aA$c�U�S�z�0!^'�]�B_���v?�,�nh��O(
C�<��������<X+E��m�+'�S'�1x����	
���������B��s��<P���>�e*8���`.2�J�lt]Zl��T��w����P��$��*���@y����c���`W�|��C�yL#�x���5R4]�+'����2������
���r�C��oe��C��x\�c��������y����J�X2��d��<X#E��ui���bsV��fB6cR=������������7���vy�x����A�)�7/r��2����6��t2�H��.-��������~\�lhx�/;���C+uC��|��R�sm���m�r0��!�9+���O	.S	���<��<��y�6�&���M�9�c�&�1���~����\���"?&�����u��`�7b���v�YC�1�^�!s�����eX
�"��'�G���(��B�`o����	vL�cb�.yR��<���&C�����X�>�����v�]��c����+,���A�qC��?�k�h�<���DO�5���pyyy5�������XwL�����}���]�2����k���7g�c�<n�<�#�`M.>^.���;w�G��M*��o��o���{M���������{�q������o7������6_���{���n�����]��c��m�;���������KC����C�-�Z���������K~m��p�~������_7���?|����O�p�p>��e��?���^���������w6��W�����Yz����������7��';8��f^2�'ue���c��z^�z�����ep�d2�K�(��\N���#�`����	����C�y�y)�?�TI�P%?�Q�������S��g��ZX����������7�N�'?��3����=���98g2I����M�MkTf~����	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*)���h���	�J�&�*]|�\��;w�4�=��T��>h~��m���O}�S���?��������������<���k~��x���w���o��Ovp����d���K�(�������|��_��;
_�����^zis�5����W����/�{�����?��^���{@I��L��!�<���������h���	ny���_|��K�����7��|��_m...n-����������i�����k���>g������u���!����r��G�6y���-�&>S������^2�qd��y����]�&�8������l#����	ny����g�y��K���iO����k�|���_o���;W�������/?�4�}��K�����sNA�u������_�����A�M�
��{�����':k
RX�����W^i�z������rl�B�1��`2�qdO�y#�`�}M�Hq���P1p�A�Q��m���k�]M,%�����K<���NL��vs������[M�����-���n�<N���1d��hF�
b���������K[���AK��`�<���f���r�p�o\t���e�s���1�����Cp�Pc������� ��Xd��<v�y0���!M���,&o����o������t��)*����,!&�$�����0���#0����t�0�)����_��<�b���N����S!��y�<8����y�A��5X�6P��U�����������v���6e8�C����o{�m��|��n>Op>d�����N���.�&`�|Br9�\����nn�8e��Epe(~����r��:���� ���d7dp:duQ4�%�b���N���z���W_}us�x�o.z{�?n�Sh�]����y�ypL2�28-2�z(�`u���������C�����xn�nE`Ueq���-�)r�k��'�*���)�[��a���X���Y�A�^,������!���!�u<�|����k��|I�{������1�M�?��q?����9����?�_�������=�!����]��������j��|���c^�|���k����R���z��R����m�������C�1��d2�!���M���&X�< {���5���=�`9���,m�e3�~�����}'���1��5�\�:��"On�1�h'������~�=&��&����kn����^;��>'��=���mb|��t�slK��Y���^Li���7En�1�u��yp2�'�<n��aW[����c��=�&��!�`N�&X��r9a��1���Vr�x�|��w��i[����L���	^��da�����Q��!�V���v+O�h��k�W�:v����X]��>K������� �Kl���d������_zL9��C��������G�Vc�	��d���6�����@�1���6���h�U�����Sv���`��Tb�� N���nn�V�����~�k����{�	K	C'F�r�N��$m�7b���������Z9��6�z���vl����%?/�"������i�,�+��>/�XO>N��gn�z���6������=��1d�c_<x��w�������Dlk�����1��m�[�m���:��w/]�n#��<8��������>y�$��y�����[��<�mk���)/���h�U�d]�,�I|LEf;@�N+�<P����%8����r=��<����I�����yr�%O�����}rn�� M�u�!���e�f�m�a�xM	P��o�W��x?����_j��nGy�6��"���=d����:6�k�����*�����������gd�����c�5Z����y�T�S�'[>~�x�����c�v�<$32�kC>�2���	V+W����r��;�m���4�>Ap����������n�����ob4���o��?�����+��6��:�e�	N����+O[C��c�#b��M����o��_�K��G;��,�!���ch��tb����o9Di�9��_�x�����a�8G�P�C�?�:2����u���c���%>�2���	V+v���:�}Eg��s�8]s�?]���H��$��o1)��!��StU�w)�kW��k�I�nL�b��k8T��}��_�|N_�P���eNC^7�����M�[��w��!!����h�!���Yj
Y��:�&��dO�y��y�7d�.��7d�K�y���	V+N�e��m��'�CD��|vU����}�0N���O;ih��vM"H���}���w��m�s{�����q�U��A�!B�s�k�r0$)�`�e�c�n���>�&�}���5���3�OH����8�s(_oh��4s�O��<h������Oi���ve,��N�qc�����s�h�U�'���l�Y��/v��O<?K1p)��t��m��'��XbR<tB�'�s�������j��N��%�M��6�v���[�S��f9(���=9�q���l���k���m6�?����9��3����m�v��d6�-�u�����c�>���y�m��G�1Ln��/��f�q�k��%��-o���CS4���r�I��,�<���/5`�t��i�>����r��&i���L�v��$�m��=���V�?������l���m�M\&|��w<���L������%�.��;�@`��=����y����OK�qC��^��d�C��>�&X�]����o\��D�+����O���I�!�����v����WL�:�h�m�H���|N�l�s
�i���?�<�t
�#��
��:��4��	Vo�����;W��e��p�N��95�~��sn��~sXb����2�('�}�q�6`:�iN�)�O�=���=I�q�o;d��gMP�]�*+$su�Rr����b�s�	��o(������bL�����e���V��1�`��wa�7]�.}��L����
C�S�f�c`yK�O��y��)�?2��d�$�8���Q4A�.-4��B���_������`�9������m�P(��l�,�!���O>�v2Y��2t��C������<��������A���+�`�� ��M�qC�1����y���	��;�v�Q����y�xl{�{���W�P�9���$��w�j`���o�3�D����r�_'�~��[�lC~�\�v)'��v�
6��n��|�v��1�g�6G��m�19��%?^�P�9�����_m�V��4�g���	��L�����DtUHv��
@=���~�|�.��9�*�r�B�1AGI�)'ya�Iz�>������/_;�!�_�c�p���!B��[���ws|���S�xl����
�����p���2������<��y��J�q�y�I����zjE��O��.�O�y�����[������@
��b�0D���,�1������r��+����!��xL9��m�s�^�O1F(�����k�����=�$����I�y��c��n��t��X�� �=�b[���T�	R����c�y�&��&�����4�<�O���u�A�
��8A���������N��O���O9Q+'�c��viH�QN�B\�s�,�-SZ2�/�>��}W��{B^O�v��L���q�����~{s�������]�p����s\���24`s�9����cN���O�����c���C_��qEK�1��c2�P4AU��u�QD����SYm'�X�2By�u�������'���2i('{��
:�1y��h�r{�v�S�TZy;D_U������nn�5������;.��~�}?��?L��.�������r��1�x�������d���6��5���d��y�E�U��b3���uuNQ�'�X���;��[j@�i���������}P6w�3�7��)�	:�:>�#�#��l�<�������k��MJ�&X]��*���1������v���cb�q1f���g:��y[c��~�a��e��O����c�y<�kN$��^�T�};�=7��2���=^duP4AU�N�c{������q��������fl�syy9{��+�a�������l�=��.��}��=.vYb�y����S��9>��+D�f�q�>�K��}0��S���s.�7p���dO�y�����14v�e7�9^duP4�-}�Q������K��!����
�V���AK����?����I8��I�����M���v��<�C�?���R~;"�������g�6Xi����Q�[�)S�|�����m��[-�x�h��ce�%�M�s���q��u\�uO]O|Vc;����n�!��S��|���?kS�<���`����}�9�y����	e���G�����m�y�/�Y�1���E�Q����e��s�N�����IE�����_����i��������K�{�������o����;
_����<����c���u1�XrR������[>,`rxw��m?~��w>���n7��ypH2���y-������+M0ZYq�����-�F�e*����y-�G=M0Zy)��<�����v�e*�.q	pnd@�y���sT��>h~��m���O}�S���?��������������<���k~����"4�w���������������)e}�<�L��!�<�E��<���M0Y��~~����x��7�n�K���y@�du(�?��d������;�P�6<�KT
���y@�d�Q4�d�-���~����j�A"�Z�<�N2�:)�`/QmU����O`�"(l/Q�K�k%����<��h������������[���%*���y@=d�R4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MU��x��w��i=z7���?����?����i����m>���l��&����]-����z�j�t��������{����v3/��$���Rf�&*����c�_{ws�4��W^m���_��`M���o6�g_��;
��_k����6����	����C�y�y)�?�T��&*�[���W���u���_���W^y������F��{�]�~��7���T�{��<�6r|������|�b������O���~g�XG(��P�9�|��e��x����J�3���d��� �O�!�8U2�C�(��\~��?���'���;���G�?��w6��/@������;������^{����������w���[o����� ��7����\�X��f�sk��Xy�����������ls�n����i>�����>���"�k������jH��'���O�}�������j�>�m�cz�����@��������k�h�s&��_��d�sk��X�yp(2��d�d�I��Wf~��["<x��g�:��SL�c��-<����9+g�D���s��>� vM��m������E��'�:�r���I����}��� D_��	k��uB�?���>d���5�G���md�����y�M�uQ4E'�=Qq���������6�aF��O��������m�����}����;�����������4Dh��OK�s���y����y@=���}�<�y@]M��9��OL��M���K\��������6���!��7��6���v��r?8=���]r����/#8����t��>�6���v�K��+�"���w��p��8/�<�#���1�,2I��P4��1=}�����R�[�My�����.��r?U~��2�����6��>����
������ur��88�k"����ypH2���	��,W��r%������5��:-�i��@��U�p���ScM�w��p�����m��"���i(C���9���<X�8^d�2�A�uP4#��bb�|b�^�k�������r^��o^�:����|��~j���N���0?/������
w�=��8O2�H�� ��XdP������q������Gq��|�;�����n&����l�z�������g?i>������4��W�/~��6����J���/����T~~�������\�AS�F������!��������h;�L��Y9P���=����������.}���_8���;^����<��G0��������+�{M����m���S�{���O?j~��O6�����k�^����4��6��x��s��,��.�����(��c�3�uz����8C��k|���0�����n�|�#L�o����������7��';8��f^2�n����C�!�8���;^���G��!�<������y���:=���u�!��y����L�h�bk
B�����:�<��;�)�4d ��]v�k�u:�,�'����}���<���m��h��tz���j����������Z��D�3��s�����wr���?c�a�D7�y���!���������o�l[����5g����zj�E�3�G�����q�!L��!���
��jW�G��������\W���J�����c��}�ud2�R��ep�����s�Z1xk
0��AI��l*)�����<X<��1�����1�#*D����5X����z,L��!��)��>������������������X�����59V�b���Dx�������W�y~��t��{N������+��x��u�c����l��v(��������d����J��]lw�~�<`92�ad��ce��Q4�j��RL����l*(Q��*;�Xwt����]�S��
}���t��^,�~����[�d4��kr_>/�Rd�����m��
y�!��<X�����xN�{�>~��i����0fp�\�k^��	E�������Py����6��9��d��ij7dR�\������S�wL���An{`>2���c�r?C96�y�<�2�n2��^�p���pu� �����/U�N/:�]�
��kt�������������M1�)'�C/��������k�@c[��������������8�`������Z���	}���v�g���k���s�;C�9o�+�o��N>����Y�������.�/7��]���~b���T��������98g2��v�=�1@����i����)c���'��1f�;�m��;�mB���v�]��g�g��y����b�yk_�|#��!��6�o���������d�K��VM���`�o�,1��K[�G)�`��#�-�GC�'�=d@2f�b�����=����m���1��_���O��C>3�m�����~����q��;�:�,y����x^��=�{�����k9|�2��(������p�o���-9����<n�y��y@�9m��rd7d�\M�jc/�;�<A����~t�Q�nt����!��)�.�xhe��~�
�Jy3�}��;v�����6����6pk�>N���>��ks�;C���)��;������c������D+�]�����sV^����c�r���1~�yvL5d�K�y����y@��w��y<i�����N����	V-O��ub��:	��o�����v
(�8��]��1������}b�����k0u��bt�����C�e�d��js�~����S�1���;�,y������5�%�)��� %�����C�����4�����<��<�2�� ��!���h��+;���0�����m�A+WN�
�9{���ckKn{�b�=c.	�����&#K�7<�-���r2u�<����]�:�O���m�������������?��a�<���[��O�q�:��<��y��(�`�rG����rU�C:�1��S+����.�y��l��mL��6����}�������<x;x,��!����t��-�j�~�4��6�����b����S���ui�{�2�n-���m2�����m��c;����m2��d��R4A��KW�z�
�2�A����]5`��P;H�%~�������\�)������-����d������~K����������!�\��1�s���cy�0��E�1����y��yV��v��,s��2XET���wu�y�W��
ck��S�W/�1`2����&�������&�i�S��T�����������&�s�y�M������`=MP�]�
+aK_��1�v���Y9U�y��j���^^^^�^k�=�}
���t�������X��6W��a�q��%;����)�y�2g��G�qX�4��y� ��'�8�r��,2@��('q��%��u��r�nQ�����\�������8b��ci_���Z�1�������\8��]>��q,�?���UK�����������>OY������ X���kL��<�����d���u����c���v��,2@��(e��a�:�<@���f����mYB9�:<����1`���m�������'6�ts�;}�7�1As�����-����)r�����`5�;���<����'�<���d��y�y�y��y\�y�N���j�R;xs��<@��V��{s���1��x���r��1p(�m��s{�3pk�~��!���>�N����3��'r�z����~+����{W���`��?��y,K��O�u�y��y\�y�N�U)Y1������Q�
�P�1s��
 ���.W�n�=�������y�:f����r���BX�9��,��P��>�N<7�}]���K�{���nn�o��yj�������c����gjH�'�X�1�TK�y��y�~d�d2`MP��2U��M�\X���@�K��s��Ty`����<8��28�
W����Aw���r{�3���m����R��j�~'��8�D4����KE���!�����i�9��]�����D�5t�����K��C�O`~2��d�d�.2�2�p�MP��-;��U��#�f��9h(+m�z�������D�f�����n�4��o�������8��j�9+����90����)������XG�������uN}}�������m}��v��C}S�u���� ��������"��!��&����	���i�����`�\EGABt��D���I<'O$��7x�S~c�b�b?K�t
�vi�&�r �WB�l��x���j��y[����i�.���6��9��Oy�z�k5G�S��v8�����~������='�K���s>'�m�����]����k���K}��O��nk�}���}0��~���9�B�1^y��<`2�]�Dh���9�]�~���91l;/�<n���mm�O�,��u��x��w��i=z7�������|�f ��o����g���;��~��������{M��_y����mso�a��:sJg�l9(�&:�%��r_��G�����m�����S�-1�����g�~����nG�����m�+<��g��1�z`�o�����W6���3/=�<���6����~�����l�5�����/�����e����|�9�gu������&��>�4�?]����F�)��_W1����9�����{�y������d����K�qmj��m���35el!���n��1�!�F����<��3���G����a�kt�����e�����&�Rt����Q�5W1v�tL�;F����o3���6k�`$��K�K�m}b��������]�v�U�1����0�mb�?�m�1��m��9��O�>�����~'���c|V��w����X��}����/�>���5v���k���
S�9��>�90]_�<��m���<d�n�3%��y���
S�Y��eQW�z�uQ����_���7����V����?j�����w�o]DW^Ji�����,��!&e�������C�3?/�
���6�����v��m�c�M^���Y�,���u��o<���i>�v��������s�8�kl�3D��0�����P����k���S��}�������}{���&8g2�ks��b�a�8i2�a��Gc���m��1�x�yp2�k�|�d���C�������}�^�<�SUfq���d���Sp�am��j��1E9��J�cW�^9@8��F�C�2X�ol���A��L�����K����<�A�C��S��yX����on5��a��rs�e���w��o����\�=���C�C&��:(���N:*�[���`�����q��v���=x�`s��s1�d2`,������9*������o~��|���6�{�W6���DT�\j���K���K��}�j9%���W��wD��w���c���sp�d�b��2I��.������M������������~� ��uS4�
cO��y���d�n�&`��N�����
�/���G�V�B$X'E�)1�d��/����<`�������X����on]��OTK��~��L���[��=��4cO��ys���M�uq�	X���|����n���B�q�C�S��}���]�V��&�Sc��2`.��:�<�ev�h��hX�2;��@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P%E@�MUR4TI�P���������/6/��R�h���������^���s�y�������{�n�����=�p[�y��h`�s-����9�
5���?�#~
�IEND�B`�
figure-3.pngimage/png; name=figure-3.pngDownload
figure-4.pngimage/png; name=figure-4.pngDownload
v5-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v5-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From e45c60ffb51f797c0e8bd4f88ee474fd6fbcaac7 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v5 1/2] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |  13 +-
 src/backend/optimizer/path/equivclass.c   | 833 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 877 insertions(+), 285 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170ce..e8996456be 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,19 +7412,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7455,7 +7459,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7470,19 +7476,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7496,8 +7505,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 60610e3a4b..e036420277 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,9 +430,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index f486d42441..142568b80f 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = list_nth_node(EquivalenceMember, root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5820,7 +5824,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 799bdc91d0..2b04b67b14 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,9 +819,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -729,10 +840,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -764,7 +874,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -794,19 +904,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +983,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +1095,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1063,7 +1182,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1096,7 +1215,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1120,7 +1239,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1145,7 +1264,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,10 +1273,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1172,9 +1291,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1307,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1215,9 +1338,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1227,7 +1350,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1240,7 +1364,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1378,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1290,7 +1418,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1313,11 +1441,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1326,6 +1458,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1346,11 +1479,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1391,9 +1525,9 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1445,7 +1579,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1650,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1559,7 +1693,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1706,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1732,12 +1872,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1893,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1815,9 +1959,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1825,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1835,9 +1983,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1876,7 +2028,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2068,7 +2220,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2087,6 +2240,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2094,6 +2248,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2103,8 +2258,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2275,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2139,9 +2300,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2220,11 +2383,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2251,10 +2415,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2269,7 +2437,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2453,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2332,11 +2502,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2368,21 +2552,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2636,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2507,16 +2703,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2567,7 +2766,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2581,33 +2781,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2653,7 +2841,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2705,7 +2893,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2719,24 +2908,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2745,10 +2929,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2794,7 +2975,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +3038,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +3048,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2879,10 +3062,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2896,14 +3082,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2986,7 +3173,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3000,7 +3187,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3044,7 +3231,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3074,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,12 +3270,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3105,17 +3293,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3199,7 +3385,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3235,3 +3421,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 63a8eef45c..9dc87b3553 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -185,8 +185,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3090,8 +3090,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3108,8 +3108,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3133,9 +3134,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 18f8870098..aeacc88d08 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1436,9 +1436,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1493,18 +1497,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1556,7 +1561,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1967,8 +1972,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1988,19 +1994,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd8a3ef7cb..6d30a24990 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 079bd0bfdf..7f3398572b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41c7066d90..3d322c353f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 71052c841d..6da7f70922 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index edcdd0a360..7fc86aeb0d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -643,6 +649,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -829,6 +838,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 294cfe9c47..bf6389fee4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1285,9 +1312,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d11cdac7f8..f6835f80f5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -161,7 +162,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -187,6 +189,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v5-0002-Revert-one-of-the-optimizations.patchapplication/octet-stream; name=v5-0002-Revert-one-of-the-optimizations.patchDownload
From ac54a3effc0369862cab82b164d28d30e7a2d2fe Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 5 Sep 2022 17:17:13 +0900
Subject: [PATCH v5 2/2] Revert one of the optimizations

This commit reverts one of our optimizations.

This function enumerates EquivalenceMembers in the given
EquivalenceClass and returns true if there is a chance of merging. This
check is accomplished by finding EMs where
"bms_overlap(cur_em->em_relids, relids)" is false. In most cases, we
reach such EMs early in the loop. Therefore, performing our newly
introduced optimization technique is excessive here, and we revert the
change.
---
 src/backend/optimizer/path/equivclass.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 2b04b67b14..5f0d2bafd4 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3261,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
-	Bitmapset  *matching_ems;
 	Relids		relids;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3293,15 +3293,18 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	/* To join, we need a member not in the given rel */
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
+	{
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-	/*
-	 * If there are any other non-child members besides matching_ems then we
-	 * have a chance at merging.
-	 */
-	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
-		return true;
+		Assert(!cur_em->em_is_child);	/* ignore children here */
+
+		if (!bms_overlap(cur_em->em_relids, relids))
+			return true;
+	}
 
 	return false;
 }
-- 
2.35.3.windows.1

v6-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v6-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From e45c60ffb51f797c0e8bd4f88ee474fd6fbcaac7 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v6 1/3] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |  13 +-
 src/backend/optimizer/path/equivclass.c   | 833 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 877 insertions(+), 285 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170ce..e8996456be 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,19 +7412,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7455,7 +7459,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7470,19 +7476,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7496,8 +7505,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 60610e3a4b..e036420277 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,9 +430,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index f486d42441..142568b80f 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = list_nth_node(EquivalenceMember, root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5820,7 +5824,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 799bdc91d0..2b04b67b14 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,9 +819,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -729,10 +840,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -764,7 +874,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -794,19 +904,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +983,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +1095,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1063,7 +1182,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1096,7 +1215,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1120,7 +1239,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1145,7 +1264,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,10 +1273,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1172,9 +1291,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1307,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1215,9 +1338,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1227,7 +1350,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1240,7 +1364,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1378,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1290,7 +1418,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1313,11 +1441,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1326,6 +1458,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1346,11 +1479,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1391,9 +1525,9 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1445,7 +1579,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1650,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1559,7 +1693,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1706,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1732,12 +1872,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1893,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1815,9 +1959,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1825,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1835,9 +1983,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1876,7 +2028,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2068,7 +2220,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2087,6 +2240,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2094,6 +2248,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2103,8 +2258,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2275,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2139,9 +2300,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2220,11 +2383,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2251,10 +2415,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2269,7 +2437,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2453,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2332,11 +2502,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2368,21 +2552,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2636,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2507,16 +2703,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2567,7 +2766,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2581,33 +2781,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2653,7 +2841,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2705,7 +2893,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2719,24 +2908,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2745,10 +2929,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2794,7 +2975,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +3038,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +3048,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2879,10 +3062,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2896,14 +3082,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2986,7 +3173,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3000,7 +3187,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3044,7 +3231,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3074,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,12 +3270,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3105,17 +3293,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3199,7 +3385,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3235,3 +3421,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 63a8eef45c..9dc87b3553 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -185,8 +185,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3090,8 +3090,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3108,8 +3108,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3133,9 +3134,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 18f8870098..aeacc88d08 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1436,9 +1436,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1493,18 +1497,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1556,7 +1561,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1967,8 +1972,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1988,19 +1994,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd8a3ef7cb..6d30a24990 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 079bd0bfdf..7f3398572b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41c7066d90..3d322c353f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 71052c841d..6da7f70922 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index edcdd0a360..7fc86aeb0d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -643,6 +649,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -829,6 +838,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 294cfe9c47..bf6389fee4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1285,9 +1312,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d11cdac7f8..f6835f80f5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -161,7 +162,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -187,6 +189,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v6-0002-Revert-one-of-the-optimizations.patchapplication/octet-stream; name=v6-0002-Revert-one-of-the-optimizations.patchDownload
From ac54a3effc0369862cab82b164d28d30e7a2d2fe Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 5 Sep 2022 17:17:13 +0900
Subject: [PATCH v6 2/3] Revert one of the optimizations

This commit reverts one of our optimizations.

This function enumerates EquivalenceMembers in the given
EquivalenceClass and returns true if there is a chance of merging. This
check is accomplished by finding EMs where
"bms_overlap(cur_em->em_relids, relids)" is false. In most cases, we
reach such EMs early in the loop. Therefore, performing our newly
introduced optimization technique is excessive here, and we revert the
change.
---
 src/backend/optimizer/path/equivclass.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 2b04b67b14..5f0d2bafd4 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3261,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
-	Bitmapset  *matching_ems;
 	Relids		relids;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3293,15 +3293,18 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	/* To join, we need a member not in the given rel */
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
+	{
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-	/*
-	 * If there are any other non-child members besides matching_ems then we
-	 * have a chance at merging.
-	 */
-	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
-		return true;
+		Assert(!cur_em->em_is_child);	/* ignore children here */
+
+		if (!bms_overlap(cur_em->em_relids, relids))
+			return true;
+	}
 
 	return false;
 }
-- 
2.35.3.windows.1

v6-0003-Implement-iterators.patchapplication/octet-stream; name=v6-0003-Implement-iterators.patchDownload
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

v7-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v7-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From e45c60ffb51f797c0e8bd4f88ee474fd6fbcaac7 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v7 1/3] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |  13 +-
 src/backend/optimizer/path/equivclass.c   | 833 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 877 insertions(+), 285 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 16320170ce..e8996456be 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7412,19 +7412,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7455,7 +7459,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7470,19 +7476,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7496,8 +7505,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 60610e3a4b..e036420277 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -430,9 +430,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index f486d42441..142568b80f 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1986,12 +1986,14 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
 		double		nGroups,
 					correctedNGroups;
 		Cost		funcCost = 1.0;
+		int			first_em;
 
 		/*
 		 * We believe that equivalence members aren't very different, so, to
 		 * estimate cost we consider just the first member.
 		 */
-		em = (EquivalenceMember *) linitial(pathkey->pk_eclass->ec_members);
+		first_em = bms_next_member(pathkey->pk_eclass->ec_member_indexes, -1);
+		em = list_nth_node(EquivalenceMember, root->eq_members, first_em);
 
 		if (em->em_datatype != InvalidOid)
 		{
@@ -2326,8 +2328,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5820,7 +5824,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 799bdc91d0..2b04b67b14 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -710,9 +819,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -729,10 +840,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -764,7 +874,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -794,19 +904,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -865,11 +983,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -976,7 +1095,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1063,7 +1182,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1096,7 +1215,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1120,7 +1239,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1145,7 +1264,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1154,10 +1273,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1172,9 +1291,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1186,9 +1307,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1215,9 +1338,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1227,7 +1350,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1240,7 +1364,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1253,9 +1378,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1290,7 +1418,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1313,11 +1441,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1326,6 +1458,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1346,11 +1479,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1391,9 +1525,9 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1445,7 +1579,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1516,7 +1650,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1559,7 +1693,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1706,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1732,12 +1872,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1893,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1815,9 +1959,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1825,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1835,9 +1983,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1876,7 +2028,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2068,7 +2220,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2087,6 +2240,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2094,6 +2248,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2103,8 +2258,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,9 +2275,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2139,9 +2300,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2220,11 +2383,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2251,10 +2415,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2269,7 +2437,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2285,9 +2453,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2332,11 +2502,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2368,21 +2552,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2445,16 +2636,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2507,16 +2703,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2567,7 +2766,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2581,33 +2781,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2653,7 +2841,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2705,7 +2893,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2719,24 +2908,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2745,10 +2929,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2794,7 +2975,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2857,7 +3038,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2866,7 +3048,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2879,10 +3062,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2896,14 +3082,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2986,7 +3173,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3000,7 +3187,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3044,7 +3231,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3074,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3083,12 +3270,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3105,17 +3293,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3199,7 +3385,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3235,3 +3421,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 63a8eef45c..9dc87b3553 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -185,8 +185,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3090,8 +3090,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3108,8 +3108,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3133,9 +3134,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 18f8870098..aeacc88d08 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1436,9 +1436,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -1493,18 +1497,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1556,7 +1561,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1967,8 +1972,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1988,19 +1994,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd8a3ef7cb..6d30a24990 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 079bd0bfdf..7f3398572b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41c7066d90..3d322c353f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 71052c841d..6da7f70922 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index edcdd0a360..7fc86aeb0d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -643,6 +649,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -829,6 +838,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 294cfe9c47..bf6389fee4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1285,9 +1312,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d11cdac7f8..f6835f80f5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -161,7 +162,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -187,6 +189,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v7-0002-Revert-one-of-the-optimizations.patchapplication/octet-stream; name=v7-0002-Revert-one-of-the-optimizations.patchDownload
From ac54a3effc0369862cab82b164d28d30e7a2d2fe Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 5 Sep 2022 17:17:13 +0900
Subject: [PATCH v7 2/3] Revert one of the optimizations

This commit reverts one of our optimizations.

This function enumerates EquivalenceMembers in the given
EquivalenceClass and returns true if there is a chance of merging. This
check is accomplished by finding EMs where
"bms_overlap(cur_em->em_relids, relids)" is false. In most cases, we
reach such EMs early in the loop. Therefore, performing our newly
introduced optimization technique is excessive here, and we revert the
change.
---
 src/backend/optimizer/path/equivclass.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 2b04b67b14..5f0d2bafd4 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3261,8 +3261,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
-	Bitmapset  *matching_ems;
 	Relids		relids;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3293,15 +3293,18 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	/* To join, we need a member not in the given rel */
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
+	{
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-	/*
-	 * If there are any other non-child members besides matching_ems then we
-	 * have a chance at merging.
-	 */
-	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
-		return true;
+		Assert(!cur_em->em_is_child);	/* ignore children here */
+
+		if (!bms_overlap(cur_em->em_relids, relids))
+			return true;
+	}
 
 	return false;
 }
-- 
2.35.3.windows.1

v7-0003-Implement-ECIndexCache.patchapplication/octet-stream; name=v7-0003-Implement-ECIndexCache.patchDownload
From 5c755bfb9b9e218da55af3110d83e2f0c7566ade Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 21 Sep 2022 15:17:20 +0900
Subject: [PATCH v7 3/3] Implement ECIndexCache

---
 contrib/postgres_fdw/postgres_fdw.c       |   6 +-
 src/backend/nodes/gen_node_support.pl     |   4 +
 src/backend/optimizer/path/equivclass.c   | 189 ++++++++++++++++++----
 src/backend/optimizer/path/pathkeys.c     |   4 +-
 src/backend/optimizer/util/appendinfo.c   |   3 +
 src/backend/optimizer/util/relnode.c      |   4 +
 src/backend/optimizer/util/restrictinfo.c |   6 +
 src/include/nodes/pathnodes.h             |  58 +++++++
 src/include/optimizer/paths.h             |  24 ++-
 9 files changed, 255 insertions(+), 43 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e8996456be..3acd4d011a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7415,7 +7415,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 	Bitmapset *matching_ems;
 	int		i = -1;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false,
+										INIT_AND_GET_EC_INDEX_CACHE(root, rel->ec_index_cache));
 
 	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
@@ -7478,7 +7479,7 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 		expr_relids = pull_varnos(root, (Node *) expr);
 		matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids,
-												   false, false);
+												   false, false, NULL);
 		/* Locate an EquivalenceClass member matching this expr, if any */
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -7507,7 +7508,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/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b707a09f56..f0947b53c9 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -168,6 +168,10 @@ my %manual_nodetag_number;
 # EquivalenceClasses are never moved, so just shallow-copy the pointer
 push @scalar_types, qw(EquivalenceClass* EquivalenceMember*);
 
+# ECIndexCache are never moved, so just shallow-copy the pointer
+# TODO: Is this correct?
+push @scalar_types, qw(ECIndexCache*);
+
 # This is a struct, so we can copy it by assignment.  Equal support is
 # currently not required.
 push @scalar_types, qw(QualCost);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 5f0d2bafd4..fc11b94d38 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -765,7 +765,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 			continue;
 
 		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
-												   true, true);
+												   true, true, NULL);
 
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -918,7 +918,8 @@ find_ec_member_matching_expr(PlannerInfo *root,
 		expr = ((RelabelType *) expr)->arg;
 
 	expr_relids = pull_varnos(root, (Node *) expr);
-	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids,
+											   true, true, NULL);
 
 	i = -1;
 	while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -1458,7 +1459,6 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
-	bms_free(matching_ems);
 }
 
 /*
@@ -1706,7 +1706,7 @@ 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);
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false, NULL);
 
 	i = -1;
 	while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -1876,7 +1876,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	List	   *result = NIL;
 	int			i;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids, NULL);
 	i = -1;
 	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
@@ -1970,8 +1970,8 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	matches = bms_intersect(get_ec_source_indexes_strict(root, ec, leftem->em_relids, NULL),
+							get_ec_source_indexes_strict(root, ec, rightem->em_relids, NULL));
 	i = -1;
 	while ((i = bms_next_member(matches, i)) >= 0)
 	{
@@ -1983,8 +1983,8 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+	matches = bms_intersect(get_ec_derive_indexes_strict(root, ec, leftem->em_relids, NULL),
+							get_ec_derive_indexes_strict(root, ec, rightem->em_relids, NULL));
 
 	i = -1;
 	while ((i = bms_next_member(matches, i)) >= 0)
@@ -2223,6 +2223,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	Relids		outer_relids,
 				inner_relids,
 				inner_nullable_relids;
+	ECIndexCache *outer_ec_index_cache;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2242,6 +2243,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		inner_datatype = right_type;
 		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
+		outer_ec_index_cache =
+			INIT_AND_GET_EC_INDEX_CACHE(root, rinfo->left_ec_index_cache);
 	}
 	else
 	{
@@ -2250,6 +2253,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		inner_datatype = left_type;
 		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
+		outer_ec_index_cache =
+			INIT_AND_GET_EC_INDEX_CACHE(root, rinfo->right_ec_index_cache);
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
 										  rinfo->nullable_relids);
@@ -2277,7 +2282,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		match = false;
 		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
 												   outer_relids,
-												   false, true);
+												   false, true,
+												   outer_ec_index_cache);
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
@@ -2417,7 +2423,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 */
 		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
 												   rinfo->clause_relids, true,
-												   false);
+												   false,
+												   INIT_AND_GET_EC_INDEX_CACHE(root, rinfo->clause_ec_index_cache));
 		match = false;
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
@@ -2567,8 +2574,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		if (ec->ec_has_volatile)
 			continue;
 
-		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
-								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		matching_ems = bms_union(get_ecmember_indexes_strict(root, ec, item1_relids, false, true, NULL),
+								 get_ecmember_indexes_strict(root, ec, item2_relids, false, true, NULL));
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
@@ -2643,8 +2650,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
-		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
-								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
+		matching_ems = bms_union(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false, INIT_AND_GET_EC_INDEX_CACHE(root, rel1->ec_index_cache)),
+								 get_ecmember_indexes_strict(root, ec, rel2->relids, false, false, INIT_AND_GET_EC_INDEX_CACHE(root, rel2->ec_index_cache)));
 
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -2784,7 +2791,9 @@ 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);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids,
+											false, false,
+											child_rel->top_parent ? INIT_AND_GET_EC_INDEX_CACHE(root, child_rel->top_parent->ec_index_cache) : NULL);
 
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -2911,7 +2920,9 @@ 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);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids,
+											false, false,
+											child_joinrel->top_parent ? INIT_AND_GET_EC_INDEX_CACHE(root, child_joinrel->top_parent->ec_index_cache) : NULL);
 
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -3062,7 +3073,8 @@ 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);
+		matching_ems = get_ecmember_indexes(root, cur_ec, rel->relids,
+											true, false, INIT_AND_GET_EC_INDEX_CACHE(root, rel->ec_index_cache));
 		cur_em = NULL;
 		j = -1;
 		while ((j = bms_next_member(matching_ems, j)) >= 0)
@@ -3425,6 +3437,57 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	return bms_int_members(rel1ecs, rel2ecs);
 }
 
+/*
+ * Helper macros for caching the result of get_ec**_indexes**().
+ */
+
+/* Call this at the entry of functions */
+#define RETURN_CACHE_IF_AVAILABLE(eclass, cache, data, field)	\
+	do															\
+	{															\
+		Bitmapset  *cached_bitmapset;							\
+																\
+		if ((cache) == NULL)									\
+		{														\
+			(data) = NULL;										\
+			break;												\
+		}														\
+																\
+		/* Find the ECIndexCacheData corresponding to */		\
+		/* the 'eclass' */										\
+		(data) = (cache)->data;									\
+		while (true)											\
+		{														\
+			EquivalenceClass   *pointing_ec;					\
+																\
+			Assert((data) < (cache)->data + (cache)->size);		\
+			pointing_ec = (data)->ec;							\
+																\
+			if (pointing_ec == NULL)							\
+			{													\
+				(data)->ec = (eclass);							\
+				break;											\
+			}													\
+			else if (pointing_ec == (eclass))					\
+				break;											\
+			else												\
+				(data)++;										\
+		}														\
+																\
+		/* Try to read the cached value from ECIndexCacheData */\
+		cached_bitmapset = (data)->field;						\
+		if (cached_bitmapset != NULL)							\
+			return cached_bitmapset;							\
+	} while (false)
+
+/* Call this at the end of functions */
+#define STORE_RESULT_TO_CACHE(value, data, field)				\
+	do															\
+	{															\
+		if ((data) != NULL)										\
+			(data)->field = (value);							\
+	} while (false)
+
 /*
  * get_ecmember_indexes
  *		Returns a Bitmapset with indexes into root->eq_members for all
@@ -3437,12 +3500,22 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
  */
 Bitmapset *
 get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
-					 bool with_children, bool with_norel_members)
+					 bool with_children, bool with_norel_members,
+					 ECIndexCache *cache)
 {
+	ECIndexCacheData *data;
 	Bitmapset  *matching_ems;
-	Bitmapset  *rel_ems = NULL;
+	Bitmapset  *rel_ems;
 	int			i;
 
+	RETURN_CACHE_IF_AVAILABLE(
+		ec,
+		cache,
+		data,
+		ecmember_indexes[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
+	rel_ems = NULL;
+
 	if (!with_children)
 		matching_ems = bms_copy(ec->ec_nonchild_indexes);
 	else
@@ -3474,6 +3547,11 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 	if (with_norel_members)
 		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
 
+	STORE_RESULT_TO_CACHE(
+		matching_ems,
+		data,
+		ecmember_indexes[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
 	return matching_ems;
 }
 
@@ -3488,11 +3566,19 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 Bitmapset *
 get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 							Relids relids, bool with_children,
-							bool with_norel_members)
+							bool with_norel_members,
+							ECIndexCache *cache)
 {
+	ECIndexCacheData *data;
 	Bitmapset  *matching_ems;
 	int			i;
 
+	RETURN_CACHE_IF_AVAILABLE(
+		ec,
+		cache,
+		data,
+		ecmember_indexes_strict[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
 	if (!with_children)
 		matching_ems = bms_copy(ec->ec_nonchild_indexes);
 	else
@@ -3521,6 +3607,11 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	if (with_norel_members)
 		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
 
+	STORE_RESULT_TO_CACHE(
+		matching_ems,
+		data,
+		ecmember_indexes_strict[GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members)]);
+
 	return matching_ems;
 }
 
@@ -3533,10 +3624,16 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_es = NULL;
-	int			i = bms_next_member(relids, -1);
+	ECIndexCacheData *data;
+	Bitmapset  *matching_es;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, data, ec_source_indexes);
+
+	matching_es = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3566,6 +3663,8 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 	/* leave only the sources belonging to this EquivalenceClass */
 	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_es, data, ec_source_indexes);
+
 	return matching_es;
 }
 
@@ -3578,10 +3677,16 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_es = NULL;
-	int			i = bms_next_member(relids, -1);
+	ECIndexCacheData *data;
+	Bitmapset  *matching_es;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, data, ec_source_indexes_strict);
+
+	matching_es = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3611,6 +3716,8 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
 	/* leave only the sources belonging to this EquivalenceClass */
 	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_es, data, ec_source_indexes_strict);
+
 	return matching_es;
 }
 
@@ -3623,10 +3730,16 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_eds = NULL;
-	int			i = bms_next_member(relids, -1);
+	ECIndexCacheData *data;
+	Bitmapset  *matching_eds;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, data, ec_derive_indexes);
+
+	matching_eds = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3656,6 +3769,8 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 	/* leave only the derives belonging to this EquivalenceClass */
 	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_eds, data, ec_derive_indexes);
+
 	return matching_eds;
 }
 
@@ -3668,10 +3783,16 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
  * Returns a newly allocated Bitmapset which the caller is free to modify.
  */
 Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids, ECIndexCache *cache)
 {
-	Bitmapset  *matching_eds = NULL;
-	int			i = bms_next_member(relids, -1);
+	ECIndexCacheData *data;
+	Bitmapset  *matching_eds;
+	int			i;
+
+	RETURN_CACHE_IF_AVAILABLE(ec, cache, data, ec_derive_indexes_strict);
+
+	matching_eds = NULL;
+	i = bms_next_member(relids, -1);
 
 	if (i >= 0)
 	{
@@ -3701,5 +3822,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids rel
 	/* leave only the derives belonging to this EquivalenceClass */
 	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
 
+	STORE_RESULT_TO_CACHE(matching_eds, data, ec_derive_indexes_strict);
+
 	return matching_eds;
 }
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index aeacc88d08..dedbcc9a9c 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1994,7 +1994,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids,
+											false, false,
+											INIT_AND_GET_EC_INDEX_CACHE(root, joinrel->ec_index_cache));
 		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
 		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 62cccf9d87..1728c0c016 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -454,6 +454,9 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->outer_selec = -1;
 		newinfo->left_em = NULL;
 		newinfo->right_em = NULL;
+		newinfo->left_ec_index_cache = NULL;
+		newinfo->right_ec_index_cache = NULL;
+		newinfo->clause_ec_index_cache = NULL;
 		newinfo->scansel_cache = NIL;
 		newinfo->left_bucketsize = -1;
 		newinfo->right_bucketsize = -1;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 7fc86aeb0d..bf0095ab17 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -237,6 +237,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->eclass_member_indexes = NULL;
 	rel->eclass_source_indexes = NULL;
 	rel->eclass_derive_indexes = NULL;
+	rel->ec_index_cache = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -652,6 +653,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->eclass_member_indexes = NULL;
 	joinrel->eclass_source_indexes = NULL;
 	joinrel->eclass_derive_indexes = NULL;
+	joinrel->ec_index_cache = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -841,6 +843,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->eclass_member_indexes = NULL;
 	joinrel->eclass_source_indexes = NULL;
 	joinrel->eclass_derive_indexes = NULL;
+	joinrel->ec_index_cache = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
@@ -1268,6 +1271,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->ec_index_cache = NULL;
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index ef8df3d098..ab49747f83 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -206,6 +206,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->right_ec = NULL;
 	restrictinfo->left_em = NULL;
 	restrictinfo->right_em = NULL;
+	restrictinfo->left_ec_index_cache = NULL;
+	restrictinfo->right_ec_index_cache = NULL;
+	restrictinfo->clause_ec_index_cache = NULL;
 	restrictinfo->scansel_cache = NIL;
 
 	restrictinfo->outer_is_left = false;
@@ -360,6 +363,9 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_ec = rinfo->left_ec;
 	result->left_em = rinfo->right_em;
 	result->right_em = rinfo->left_em;
+	result->left_ec_index_cache = NULL;
+	result->right_ec_index_cache = NULL;
+	result->clause_ec_index_cache = NULL;
 	result->scansel_cache = NIL;	/* not worth updating this */
 	if (rinfo->hashjoinoperator == clause->opno)
 		result->hashjoinoperator = comm_op;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index bf6389fee4..4e6d00bc4f 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -556,6 +556,49 @@ typedef struct PartitionSchemeData
 
 typedef struct PartitionSchemeData *PartitionScheme;
 
+/*
+ * ECIndexCache
+ *
+ * Storage where we cache the result of get_ecmember_indexes and other similar
+ * functions. See comments above the function for details.
+ *
+ * TODO: Is this good place of this struct?
+ */
+
+#define GET_EC_INDEX_CACHE_ARRAY_INDEX(with_children, with_norel_members) \
+	((with_children) << 1 | (with_norel_members))
+
+struct EquivalenceClass;
+
+typedef struct ECIndexCacheData
+{
+	struct EquivalenceClass *ec;
+	Bitmapset  *ecmember_indexes[4];
+	Bitmapset  *ecmember_indexes_strict[4];
+	Bitmapset  *ec_source_indexes;
+	Bitmapset  *ec_source_indexes_strict;
+	Bitmapset  *ec_derive_indexes;
+	Bitmapset  *ec_derive_indexes_strict;
+} ECIndexCacheData;
+
+typedef struct ECIndexCache
+{
+	int					size;
+	ECIndexCacheData	data[FLEXIBLE_ARRAY_MEMBER];
+} ECIndexCache;
+
+static inline ECIndexCache *MakeECIndexCache(int size)
+{
+	ECIndexCache* cache =
+		palloc0(offsetof(ECIndexCache, data) + sizeof(ECIndexCacheData) * size);
+
+	cache->size = size;
+	return cache;
+}
+
+#define INIT_AND_GET_EC_INDEX_CACHE(root, cache) \
+	((cache) ? (cache) : ((cache) = MakeECIndexCache(list_length((root)->eq_classes))))
+
 /*----------
  * RelOptInfo
  *		Per-relation information for planning/optimization
@@ -916,6 +959,12 @@ typedef struct RelOptInfo
 	 */
 	Bitmapset  *eclass_derive_indexes;
 
+	/*
+	 * ECIndexCache corresponding to this rel's relids.
+	 * TODO: The following usage of pg_node_attr is wrong.
+	 */
+	ECIndexCache   *ec_index_cache pg_node_attr(read_write_ignore);
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -2557,6 +2606,15 @@ typedef struct RestrictInfo
 	/* EquivalenceMember for righthand */
 	EquivalenceMember *right_em pg_node_attr(equal_ignore);
 
+	/*
+	 * ECIndexCaches corresponding to this rinfo's left_relids, right_relids,
+	 * and clause_relids.
+	 * TODO: The following usage of pg_node_attr is wrong.
+	 */
+	ECIndexCache *left_ec_index_cache pg_node_attr(read_write_ignore);
+	ECIndexCache *right_ec_index_cache pg_node_attr(read_write_ignore);
+	ECIndexCache *clause_ec_index_cache pg_node_attr(read_write_ignore);
+
 	/*
 	 * List of MergeScanSelCache structs.  Those aren't Nodes, so hard to
 	 * copy; instead replace with NIL.  That has the effect that copying will
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index f6835f80f5..0051cf9dcf 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -189,28 +189,40 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+/*
+ * TODO: Callers of the following function must not modify the returned value
+ * because they are cached. I think we should mark them 'const Bitmapset *'.
+ * Currently, I have not do that because we have to change the declarations of
+ * 'macthing_ems', where we store the returned Bitmapset.
+ */
 extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
 										   EquivalenceClass *ec,
 										   Relids relids,
 										   bool with_children,
-										   bool with_norel_members);
+										   bool with_norel_members,
+										   ECIndexCache *cache);
 extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  EquivalenceClass *ec,
 											  Relids relids,
 											  bool with_children,
-											  bool with_norel_members);
+											  bool with_norel_members,
+											  ECIndexCache *cache);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
+										Relids relids,
+										ECIndexCache *cache);
 extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
 											   EquivalenceClass *ec,
-											   Relids relids);
+											   Relids relids,
+											   ECIndexCache *cache);
 extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
+										Relids relids,
+										ECIndexCache *cache);
 extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
+										Relids relids,
+										ECIndexCache *cache);
 
 /*
  * pathkeys.c
-- 
2.35.3.windows.1

#18Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#17)
3 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Sep 21, 2022 at 6:43 PM Yuya Watari <watari.yuya@gmail.com> wrote:

1.1. Revert one of our optimizations (v5)

As I mentioned in the comment in
v[5|6|7]-0002-Revert-one-of-the-optimizations.patch, I reverted one of
our optimizations. This code tries to find EquivalenceMembers that do
not satisfy the bms_overlap condition. We encounter such members early
in the loop, so the linear search is enough, and our optimization is
too excessive here. As a result of experiments, I found this
optimization was a bottleneck, so I reverted it.

In the previous mail, I proposed a revert of one excessive
optimization. In addition, I found a new bottleneck and attached a new
version of the patch solving it to this email.

The new bottleneck exists in the select_outer_pathkeys_for_merge()
function. At the end of this function, we count EquivalenceMembers
that satisfy the specific condition. To count them, we have used
Bitmapset operations. Through experiments, I concluded that this
optimization is effective for larger cases but leads to some
degradation for the smaller number of partitions. The new patch
switches two algorithms depending on the problem sizes.

1. Experimental result

1.1. Join Order Benchmark

As in the previous email, I used the Join Order Benchmark to evaluate
the patches' performance. The correspondence between each version and
patches is as follows.

v3: v8-0001-*.patch
v5 (v3 with revert): v8-0001-*.patch + v8-0002-*.patch
v8 (v5 with revert): v8-0001-*.patch + v8-0002-*.patch + v8-0003-*.patch

I show the speed-up of each method compared with the master branch in
Table 1. When the number of partitions is 1, performance degradation
is kept to 1.1% in v8, while they are 4.2% and 1.8% in v3 and v5. This
result indicates that a newly introduced revert is effective.

Table 1: Speedup of Join Order Benchmark (higher is better)
(n = the number of partitions)
----------------------------------------------------------
n | v3 | v5 (v3 with revert) | v8 (v5 with revert)
----------------------------------------------------------
2 | 95.8% | 98.2% | 98.9%
4 | 97.2% | 99.7% | 99.3%
8 | 101.4% | 102.5% | 103.4%
16 | 108.7% | 111.4% | 110.2%
32 | 127.1% | 127.6% | 128.8%
64 | 169.5% | 172.1% | 172.4%
128 | 330.1% | 335.2% | 332.3%
256 | 815.1% | 826.4% | 821.8%
----------------------------------------------------------

1.2. pgbench

The following table describes the result of pgbench. The v5 and v8
performed clearly better than the v3 patch. The difference between v5
and v8 is not so significant, but v8's performance is close to the
master branch.

Table 2: The result of pgbench (tps)
-----------------------------------------------------------------
n | Master | v3 | v5 (v3 with revert) | v8 (v5 with revert)
-----------------------------------------------------------------
1 | 7550 | 7422 | 7474 | 7521
2 | 7594 | 7381 | 7536 | 7529
4 | 7518 | 7362 | 7461 | 7524
8 | 7459 | 7340 | 7424 | 7460
-----------------------------------------------------------------
Avg | 7531 | 7377 | 7474 | 7509
-----------------------------------------------------------------

2. Conclusion and future works

The revert in the v8-0003-*.patch is effective in preventing
performance degradation for the smaller number of partitions. However,
I don't think what I have done in the patch is the best or ideal
solution. As I mentioned in the comments in the patch, switching two
algorithms may be ugly because it introduces code duplication. We need
a wiser solution to this problem.

--
Best regards,
Yuya Watari

Attachments:

v8-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v8-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From 6ddc02631a440dd25303e277d10873aec13ee24c Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v8 1/3] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |   9 +-
 src/backend/optimizer/path/equivclass.c   | 833 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 874 insertions(+), 284 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 8d013f5b1a..9a705066ee 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7456,19 +7456,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7499,7 +7503,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7514,19 +7520,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7540,8 +7549,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 64c65f060b..cf620eca0e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -459,9 +459,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 8fc28007f5..9b6a5b08dc 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 4c6b1d1f55..222cf7a729 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2009,8 +2009,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5503,7 +5505,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index f962ff82ad..8c9d4a4c3f 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -699,9 +808,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +829,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +863,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +893,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -854,11 +972,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -965,7 +1084,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1171,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1085,7 +1204,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1109,7 +1228,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1134,7 +1253,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1143,10 +1262,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1280,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1175,9 +1296,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1204,9 +1327,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1339,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1353,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1367,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1407,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1430,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1447,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1468,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,9 +1514,9 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1434,7 +1568,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1505,7 +1639,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1548,7 +1682,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1559,9 +1695,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1721,12 +1861,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1738,12 +1882,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1804,9 +1948,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1814,9 +1959,12 @@ create_join_clause(PlannerInfo *root,
 	 * previously-derived clauses.  The check on opno is probably redundant,
 	 * but be safe ...
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1824,9 +1972,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec &&
@@ -1865,7 +2017,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2057,7 +2209,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2076,6 +2229,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2083,6 +2237,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2092,8 +2247,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2108,9 +2264,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2128,9 +2289,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2209,11 +2372,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2240,10 +2404,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2258,7 +2426,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2274,9 +2442,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2321,11 +2491,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2357,21 +2541,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2434,16 +2625,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2496,16 +2692,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2556,7 +2755,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2570,33 +2770,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2642,7 +2830,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2694,7 +2882,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2708,24 +2897,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2734,10 +2918,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2783,7 +2964,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2846,7 +3027,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2855,7 +3037,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2868,10 +3051,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2885,14 +3071,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2975,7 +3162,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2989,7 +3176,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3033,7 +3220,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3063,8 +3250,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3072,12 +3259,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3094,17 +3282,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3188,7 +3374,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3224,3 +3410,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c31fcc917d..d47be09268 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -185,8 +185,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3090,8 +3090,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3108,8 +3108,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3133,9 +3134,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index a9943cd6e0..be11e1c053 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -898,9 +898,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -955,18 +959,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1018,7 +1023,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1429,8 +1434,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1450,19 +1456,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ab4d8e201d..f0cd2aee5e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5d0fd6e072..899c84ec8f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41c7066d90..3d322c353f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f6046768fb..3015691d3e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index edcdd0a360..7fc86aeb0d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -643,6 +649,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -829,6 +838,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6bda383bea..0e273f68e0 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1285,9 +1312,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 41f765d342..07f53744c2 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v8-0002-Revert-one-of-the-optimizations.patchapplication/octet-stream; name=v8-0002-Revert-one-of-the-optimizations.patchDownload
From 0d44d5960167a824a19c36459c2cf9937e4fa249 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 5 Sep 2022 17:17:13 +0900
Subject: [PATCH v8 2/3] Revert one of the optimizations

This commit reverts one of our optimizations.

This function enumerates EquivalenceMembers in the given
EquivalenceClass and returns true if there is a chance of merging. This
check is accomplished by finding EMs where
"bms_overlap(cur_em->em_relids, relids)" is false. In most cases, we
reach such EMs early in the loop. Therefore, performing our newly
introduced optimization technique is excessive here, and we revert the
change.
---
 src/backend/optimizer/path/equivclass.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8c9d4a4c3f..156a984014 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3250,8 +3250,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
-	Bitmapset  *matching_ems;
 	Relids		relids;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3282,15 +3282,18 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	/* To join, we need a member not in the given rel */
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
+	{
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-	/*
-	 * If there are any other non-child members besides matching_ems then we
-	 * have a chance at merging.
-	 */
-	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
-		return true;
+		Assert(!cur_em->em_is_child);	/* ignore children here */
+
+		if (!bms_overlap(cur_em->em_relids, relids))
+			return true;
+	}
 
 	return false;
 }
-- 
2.35.3.windows.1

v8-0003-Use-conventional-algorithm-for-smaller-size-cases.patchapplication/octet-stream; name=v8-0003-Use-conventional-algorithm-for-smaller-size-cases.patchDownload
From 5b0bc386f1ab60d528c12491640400fa2e1b72fc Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 20 Oct 2022 15:46:00 +0900
Subject: [PATCH v8 3/3] Use conventional algorithm for smaller size cases

---
 src/backend/optimizer/path/pathkeys.c | 58 ++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index be11e1c053..8426d2681a 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1434,9 +1434,6 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		Bitmapset		 *matching_ems;
-		Bitmapset		 *interesting_ems;
-		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1455,14 +1452,57 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		if (j < necs)
 			continue;
 
-		/* compute score */
-		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
-		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
-		interesting_ems = bms_difference(other_parent_ems, matching_ems);
+		/* Compute score */
+		/*
+		 * We use two different algorithms depending on the size of the
+		 * problem. For small sizes, linear search is preferable, while
+		 * optimization based on bitmap sets is faster for larger sizes.
+		 */
+		/*
+		 * TODO: Switching two algorithms is not ideal and may be ugly
+		 * because it introduces code duplication. Is there a better way?
+		 */
+		/*
+		 * TODO: 32 is a magic number. Do we have to declare it as a constant
+		 * macro?
+		 */
+		if (root->simple_rel_array_size < 32)
+		{
+			int			score;
+			int			k;
+
+			/* compute score */
+			score = 0;
+			k = -1;
+			while ((k = bms_next_member(oeclass->ec_member_indexes, k)) >= 0)
+			{
+				EquivalenceMember *em = list_nth_node(EquivalenceMember,
+													  root->eq_members, k);
+
+				/* Potential future join partner? */
+				if (!em->em_is_const && !em->em_is_child &&
+					!bms_overlap(em->em_relids, joinrel->relids))
+					score++;
+			}
+
+			scores[necs] = score;
+		}
+		else
+		{
+			Bitmapset		 *matching_ems;
+			Bitmapset		 *interesting_ems;
+			Bitmapset		 *other_parent_ems;
+
+			/* compute score */
+			matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+			other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+			interesting_ems = bms_difference(other_parent_ems, matching_ems);
+
+			/* record results */
+			scores[necs] = bms_num_members(interesting_ems);
+		}
 
-		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
-- 
2.35.3.windows.1

#19Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#18)
3 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

I noticed that the previous patch does not apply to the current HEAD.
I attached the rebased version to this email.

--
Best regards,
Yuya Watari

Attachments:

v8-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v8-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From 0666a1c8aae7c84df210e9cfcccffe158534acc7 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v8 1/3] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |   9 +-
 src/backend/optimizer/path/equivclass.c   | 837 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 876 insertions(+), 286 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 8d7500abfb..bf79daee58 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7465,19 +7465,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7508,7 +7512,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7523,19 +7529,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7549,8 +7558,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 64c65f060b..cf620eca0e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -459,9 +459,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 4ddaed31a4..4d1fdc2e02 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 4c6b1d1f55..222cf7a729 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2009,8 +2009,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5503,7 +5505,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e65b967b1f..dd3652df70 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -699,9 +808,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +829,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +863,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +893,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -854,11 +972,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -965,7 +1084,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1171,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1085,7 +1204,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1109,7 +1228,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1134,7 +1253,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1143,10 +1262,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1280,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1175,9 +1296,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1204,9 +1327,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1339,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1353,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1367,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1407,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1430,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1447,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1468,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1514,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1436,7 +1570,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1507,7 +1641,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1550,7 +1684,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1561,9 +1697,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1723,12 +1863,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1884,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1951,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1965,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1981,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2029,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2221,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2241,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2249,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2259,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,9 +2276,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2140,9 +2301,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2221,11 +2384,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2416,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2438,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2454,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2503,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2553,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2637,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2704,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2767,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,33 +2782,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2654,7 +2842,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,7 +2894,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2909,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,10 +2930,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2795,7 +2976,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2858,7 +3039,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2867,7 +3049,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2880,10 +3063,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2897,14 +3083,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2987,7 +3174,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3001,7 +3188,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3045,7 +3232,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3075,8 +3262,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3084,12 +3271,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3106,17 +3294,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3200,7 +3386,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3422,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 77f3f81bcb..f0329c09f9 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -185,8 +185,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -998,7 +998,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3090,8 +3090,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3108,8 +3108,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3133,9 +3134,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index a9943cd6e0..be11e1c053 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -898,9 +898,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -955,18 +959,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1018,7 +1023,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1429,8 +1434,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1450,19 +1456,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ac86ce9003..748d750943 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 493a3af0fa..d50b27b7ca 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 41c7066d90..3d322c353f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f6046768fb..3015691d3e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 1786a3dadd..70dcc91ae6 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -231,6 +234,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -643,6 +649,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -829,6 +838,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 09342d128d..bf6b6aa032 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1285,9 +1312,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 41f765d342..07f53744c2 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v8-0002-Revert-one-of-the-optimizations.patchapplication/octet-stream; name=v8-0002-Revert-one-of-the-optimizations.patchDownload
From 1d6c81cbd501a4d0cfa6ef4bdc25eb3f4594f6ae Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 5 Sep 2022 17:17:13 +0900
Subject: [PATCH v8 2/3] Revert one of the optimizations

This commit reverts one of our optimizations.

This function enumerates EquivalenceMembers in the given
EquivalenceClass and returns true if there is a chance of merging. This
check is accomplished by finding EMs where
"bms_overlap(cur_em->em_relids, relids)" is false. In most cases, we
reach such EMs early in the loop. Therefore, performing our newly
introduced optimization technique is excessive here, and we revert the
change.
---
 src/backend/optimizer/path/equivclass.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index dd3652df70..7d2044b5fa 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3262,8 +3262,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
-	Bitmapset  *matching_ems;
 	Relids		relids;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3294,15 +3294,18 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	/* To join, we need a member not in the given rel */
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
+	{
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-	/*
-	 * If there are any other non-child members besides matching_ems then we
-	 * have a chance at merging.
-	 */
-	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
-		return true;
+		Assert(!cur_em->em_is_child);	/* ignore children here */
+
+		if (!bms_overlap(cur_em->em_relids, relids))
+			return true;
+	}
 
 	return false;
 }
-- 
2.35.3.windows.1

v8-0003-Use-conventional-algorithm-for-smaller-size-cases.patchapplication/octet-stream; name=v8-0003-Use-conventional-algorithm-for-smaller-size-cases.patchDownload
From ead62dda612dc42f3d2786ce348015faf8992b3d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 20 Oct 2022 15:46:00 +0900
Subject: [PATCH v8 3/3] Use conventional algorithm for smaller size cases

---
 src/backend/optimizer/path/pathkeys.c | 58 ++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index be11e1c053..8426d2681a 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1434,9 +1434,6 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		Bitmapset		 *matching_ems;
-		Bitmapset		 *interesting_ems;
-		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1455,14 +1452,57 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		if (j < necs)
 			continue;
 
-		/* compute score */
-		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
-		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
-		interesting_ems = bms_difference(other_parent_ems, matching_ems);
+		/* Compute score */
+		/*
+		 * We use two different algorithms depending on the size of the
+		 * problem. For small sizes, linear search is preferable, while
+		 * optimization based on bitmap sets is faster for larger sizes.
+		 */
+		/*
+		 * TODO: Switching two algorithms is not ideal and may be ugly
+		 * because it introduces code duplication. Is there a better way?
+		 */
+		/*
+		 * TODO: 32 is a magic number. Do we have to declare it as a constant
+		 * macro?
+		 */
+		if (root->simple_rel_array_size < 32)
+		{
+			int			score;
+			int			k;
+
+			/* compute score */
+			score = 0;
+			k = -1;
+			while ((k = bms_next_member(oeclass->ec_member_indexes, k)) >= 0)
+			{
+				EquivalenceMember *em = list_nth_node(EquivalenceMember,
+													  root->eq_members, k);
+
+				/* Potential future join partner? */
+				if (!em->em_is_const && !em->em_is_child &&
+					!bms_overlap(em->em_relids, joinrel->relids))
+					score++;
+			}
+
+			scores[necs] = score;
+		}
+		else
+		{
+			Bitmapset		 *matching_ems;
+			Bitmapset		 *interesting_ems;
+			Bitmapset		 *other_parent_ems;
+
+			/* compute score */
+			matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+			other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+			interesting_ems = bms_difference(other_parent_ems, matching_ems);
+
+			/* record results */
+			scores[necs] = bms_num_members(interesting_ems);
+		}
 
-		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
-- 
2.35.3.windows.1

#20Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#19)
Re: [PoC] Reducing planning time when tables have many partitions

On 2/11/2022 15:27, Yuya Watari wrote:

Hello,

I noticed that the previous patch does not apply to the current HEAD.
I attached the rebased version to this email.

I'm still in review of your patch now. At most it seems ok, but are you
really need both eq_sources and eq_derives lists now? As I see,
everywhere access to these lists guides by eclass_source_indexes and
eclass_derive_indexes correspondingly. Maybe to merge them?

--
regards,
Andrey Lepikhov
Postgres Professional

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrey Lepikhov (#20)
Re: [PoC] Reducing planning time when tables have many partitions

Andrey Lepikhov <a.lepikhov@postgrespro.ru> writes:

I'm still in review of your patch now. At most it seems ok, but are you
really need both eq_sources and eq_derives lists now?

Didn't we just have this conversation? eq_sources needs to be kept
separate to support the "broken EC" logic. We don't want to be
regurgitating derived clauses as well as originals in that path.

regards, tom lane

#22Zhang Mingli
zmlpostgres@gmail.com
In reply to: Tom Lane (#21)
Re: [PoC] Reducing planning time when tables have many partitions

HI,

Regards,
Zhang Mingli
On Nov 7, 2022, 14:26 +0800, Tom Lane <tgl@sss.pgh.pa.us>, wrote:

Andrey Lepikhov <a.lepikhov@postgrespro.ru> writes:

I'm still in review of your patch now. At most it seems ok, but are you
really need both eq_sources and eq_derives lists now?

Didn't we just have this conversation? eq_sources needs to be kept
separate to support the "broken EC" logic. We don't want to be
regurgitating derived clauses as well as originals in that path.

Aha, we have that conversation in another thread(Reducing duplicativeness of EquivalenceClass-derived clauses
) : /messages/by-id/644164.1666877342@sss.pgh.pa.us

#23Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#19)
Re: [PoC] Reducing planning time when tables have many partitions

On 2/11/2022 15:27, Yuya Watari wrote:

I noticed that the previous patch does not apply to the current HEAD.
I attached the rebased version to this email.

Looking into find_em_for_rel() changes I see that you replaced
if (bms_is_subset(em->em_relids, rel->relids)
with assertion statement.
According of get_ecmember_indexes(), the em_relids field of returned
equivalence members can contain relids, not mentioned in the relation.
I don't understand, why it works now? For example, we can sort by t1.x,
but have an expression t1.x=t1.y*t2.z. Or I've missed something? If it
is not a mistake, maybe to add a comment why assertion here isn't failed?

--
regards,
Andrey Lepikhov
Postgres Professional

#24Thom Brown
thom@linux.com
In reply to: Zhang Mingli (#22)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, 7 Nov 2022 at 06:33, Zhang Mingli <zmlpostgres@gmail.com> wrote:

HI,

Regards,
Zhang Mingli
On Nov 7, 2022, 14:26 +0800, Tom Lane <tgl@sss.pgh.pa.us>, wrote:

Andrey Lepikhov <a.lepikhov@postgrespro.ru> writes:

I'm still in review of your patch now. At most it seems ok, but are you
really need both eq_sources and eq_derives lists now?

Didn't we just have this conversation? eq_sources needs to be kept
separate to support the "broken EC" logic. We don't want to be
regurgitating derived clauses as well as originals in that path.

Aha, we have that conversation in another thread(Reducing duplicativeness of EquivalenceClass-derived clauses
) : /messages/by-id/644164.1666877342@sss.pgh.pa.us

Once the issue Tom identified has been resolved, I'd like to test
drive newer patches.

Thom

#25Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Thom Brown (#24)
Re: [PoC] Reducing planning time when tables have many partitions

On 2022-Nov-16, Thom Brown wrote:

Once the issue Tom identified has been resolved, I'd like to test
drive newer patches.

What issue? If you mean the one from the thread "Reducing
duplicativeness of EquivalenceClass-derived clauses", that patch is
already applied (commit a5fc46414deb), and Yuya Watari's v8 series
applies fine to current master.

--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
"Having your biases confirmed independently is how scientific progress is
made, and hence made our great society what it is today" (Mary Gardiner)

#26Thom Brown
thom@linux.com
In reply to: Alvaro Herrera (#25)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, 17 Nov 2022 at 09:31, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

On 2022-Nov-16, Thom Brown wrote:

Once the issue Tom identified has been resolved, I'd like to test
drive newer patches.

What issue? If you mean the one from the thread "Reducing
duplicativeness of EquivalenceClass-derived clauses", that patch is
already applied (commit a5fc46414deb), and Yuya Watari's v8 series
applies fine to current master.

Ah, I see.. I'll test the v8 patches.

Thanks

Thom

#27Thom Brown
thom@linux.com
In reply to: Thom Brown (#26)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, 17 Nov 2022 at 11:20, Thom Brown <thom@linux.com> wrote:

On Thu, 17 Nov 2022 at 09:31, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

On 2022-Nov-16, Thom Brown wrote:

Once the issue Tom identified has been resolved, I'd like to test
drive newer patches.

What issue? If you mean the one from the thread "Reducing
duplicativeness of EquivalenceClass-derived clauses", that patch is
already applied (commit a5fc46414deb), and Yuya Watari's v8 series
applies fine to current master.

Ah, I see.. I'll test the v8 patches.

No issues with applying. Created 1024 partitions, each of which is
partitioned into 64 partitions.

I'm getting a generic planning time of 1415ms. Is that considered
reasonable in this situation? Bear in mind that the planning time
prior to this patch was 282311ms, so pretty much a 200x speedup.

Thom

#28Yuya Watari
watari.yuya@gmail.com
In reply to: Thom Brown (#27)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear Andrey and Thom,

Thank you for reviewing and testing the patch. I really apologize for
my late response.

On Tue, Nov 8, 2022 at 8:31 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

Looking into find_em_for_rel() changes I see that you replaced
if (bms_is_subset(em->em_relids, rel->relids)
with assertion statement.
According of get_ecmember_indexes(), the em_relids field of returned
equivalence members can contain relids, not mentioned in the relation.
I don't understand, why it works now? For example, we can sort by t1.x,
but have an expression t1.x=t1.y*t2.z. Or I've missed something? If it
is not a mistake, maybe to add a comment why assertion here isn't failed?

As you pointed out, changing the bms_is_subset() condition to an
assertion is logically incorrect here. Thank you for telling me about
it. I fixed it and attached the modified patch to this email.

On Thu, Nov 17, 2022 at 9:05 PM Thom Brown <thom@linux.com> wrote:

No issues with applying. Created 1024 partitions, each of which is
partitioned into 64 partitions.

I'm getting a generic planning time of 1415ms. Is that considered
reasonable in this situation? Bear in mind that the planning time
prior to this patch was 282311ms, so pretty much a 200x speedup.

Thank you for testing the patch with an actual query. This speedup is
very impressive. When I used an original query with 1024 partitions,
its planning time was about 200ms. Given that each partition is also
partitioned in your workload, I think the result of 1415ms is
reasonable.

--
Best regards,
Yuya Watari

Attachments:

v9-0001-Apply-eclass_member_speedup_v3.patch.patchapplication/octet-stream; name=v9-0001-Apply-eclass_member_speedup_v3.patch.patchDownload
From 52fb0556b3743db212ef62ec99176f0351245941 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 18 Aug 2022 14:26:42 +0900
Subject: [PATCH v9 1/4] Apply eclass_member_speedup_v3.patch

---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   8 +-
 src/backend/nodes/print.c                 |  19 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     |   9 +-
 src/backend/optimizer/path/equivclass.c   | 837 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  42 +-
 src/backend/optimizer/plan/createplan.c   |  82 ++-
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  12 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/nodes/print.h                 |   5 +-
 src/include/optimizer/paths.h             |  28 +-
 src/test/regress/expected/equivclass.out  |   6 +-
 17 files changed, 876 insertions(+), 286 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 8d7500abfb..bf79daee58 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7465,19 +7465,23 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
-		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
-			is_foreign_expr(root, rel, em->em_expr))
+		Assert(bms_is_subset(em->em_relids, rel->relids));
+		Assert(!bms_is_empty(em->em_relids));
+
+		if (is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
@@ -7508,7 +7512,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7523,19 +7529,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7549,8 +7558,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8f150e9a2e..1f5f4dae40 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -462,9 +462,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
-	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a5c44adc6c..8eed1b22d1 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -423,16 +423,17 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(const PlannerInfo *root, const List *pathkeys,
+			   const List *rtable)
 {
-	const ListCell *i;
+	const ListCell *lc;
 
 	printf("(");
-	foreach(i, pathkeys)
+	foreach(lc, pathkeys)
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(i);
+		PathKey    *pathkey = (PathKey *) lfirst(lc);
 		EquivalenceClass *eclass;
-		ListCell   *k;
+		int			i;
 		bool		first = true;
 
 		eclass = pathkey->pk_eclass;
@@ -441,9 +442,11 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			eclass = eclass->ec_merged;
 
 		printf("(");
-		foreach(k, eclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(eclass->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+			EquivalenceMember *mem = list_nth_node(EquivalenceMember,
+												   root->eq_members, i);
 
 			if (first)
 				first = false;
@@ -452,7 +455,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			print_expr((Node *) mem->em_expr, rtable);
 		}
 		printf(")");
-		if (lnext(pathkeys, i))
+		if (lnext(pathkeys, lc))
 			printf(", ");
 	}
 	printf(")\n");
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 4ddaed31a4..4d1fdc2e02 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4539,7 +4539,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		for (i = 0; i < indent; i++)
 			printf("\t");
 		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+		print_pathkeys(root, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 897309d7ec..861fbff71e 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2009,8 +2009,10 @@ cost_incremental_sort(Path *path,
 	foreach(l, pathkeys)
 	{
 		PathKey    *key = (PathKey *) lfirst(l);
-		EquivalenceMember *member = (EquivalenceMember *)
-		linitial(key->pk_eclass->ec_members);
+		int			first_em = bms_next_member(key->pk_eclass->ec_member_indexes, -1);
+		EquivalenceMember *member = list_nth_node(EquivalenceMember,
+												  root->eq_members,
+												  first_em);
 
 		/*
 		 * Check if the expression contains Var with "varno 0" so that we
@@ -5502,7 +5504,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e65b967b1f..dd3652df70 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -139,6 +144,7 @@ process_equivalence(PlannerInfo *root,
 			   *em2;
 	ListCell   *lc1;
 	int			ec2_idx;
+	int			i;
 
 	/* Should not already be marked as having generated an eclass */
 	Assert(restrictinfo->left_ec == NULL);
@@ -265,7 +271,6 @@ process_equivalence(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
 
 		/* Never match to a volatile EC */
 		if (cur_ec->ec_has_volatile)
@@ -286,9 +291,11 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_member_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			Assert(!cur_em->em_is_child);	/* no children yet */
 
@@ -333,7 +340,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +351,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -364,9 +372,16 @@ process_equivalence(PlannerInfo *root,
 		 * PathKeys.  We leave it behind with a link so that the merged EC can
 		 * be found.
 		 */
-		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -378,11 +393,12 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_merged = ec1;
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
-		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -438,9 +458,11 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
-		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +472,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +485,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +562,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +625,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,8 +657,25 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
+	}
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -638,6 +740,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +748,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +764,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -699,9 +808,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
-	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +829,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +863,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +893,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -854,11 +972,12 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -965,7 +1084,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1171,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1085,7 +1204,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * Single-member ECs won't generate any deductions, either here or at
 		 * the join level.
 		 */
-		if (list_length(ec->ec_members) > 1)
+		if (bms_membership(ec->ec_member_indexes) == BMS_MULTIPLE)
 		{
 			if (ec->ec_has_const)
 				generate_base_implied_equalities_const(root, ec);
@@ -1109,7 +1228,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1134,7 +1253,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec)
 {
 	EquivalenceMember *const_em = NULL;
-	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1143,10 +1262,10 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * re-build and re-analyze an equality clause that will be exactly
 	 * equivalent to the old one.
 	 */
-	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+	if (bms_num_members(ec->ec_member_indexes) == 2 &&
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1280,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1175,9 +1296,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	Assert(const_em != NULL);
 
 	/* Generate a derived equality against each other member */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_member_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
@@ -1204,9 +1327,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1339,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1353,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1367,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1407,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1430,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1447,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1468,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1514,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1436,7 +1570,7 @@ generate_join_implied_equalities(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* Sanity check that this eclass overlaps the join */
@@ -1507,7 +1641,7 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root,
 			continue;
 
 		/* Single-member ECs won't generate any deductions */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/* We can quickly ignore any that don't overlap the join, too */
@@ -1550,7 +1684,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1561,9 +1697,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1723,12 +1863,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1884,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1951,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1965,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1981,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2029,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2221,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2241,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2249,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2259,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,9 +2276,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2140,9 +2301,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2221,11 +2384,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2416,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2438,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2454,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2503,25 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2553,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2637,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2704,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2767,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,33 +2782,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2654,7 +2842,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,7 +2894,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2909,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,10 +2930,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2795,7 +2976,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2858,7 +3039,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2867,7 +3049,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		 * Won't generate joinclauses if const or single-member (the latter
 		 * test covers the volatile case too)
 		 */
-		if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
+		if (cur_ec->ec_has_const ||
+			bms_membership(cur_ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -2880,10 +3063,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2897,14 +3083,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -2987,7 +3174,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3001,7 +3188,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3045,7 +3232,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
 		 * Won't generate joinclauses if single-member (this test covers the
 		 * volatile case too)
 		 */
-		if (list_length(ec->ec_members) <= 1)
+		if (bms_membership(ec->ec_member_indexes) != BMS_MULTIPLE)
 			continue;
 
 		/*
@@ -3075,8 +3262,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3084,12 +3271,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3106,17 +3294,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3200,7 +3386,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3422,282 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels */
+	matching_ems = bms_int_members(matching_ems, rel_ems);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	int			i;
+
+	if (!with_children)
+		matching_ems = bms_copy(ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_copy(ec->ec_member_indexes);
+
+	i = -1;
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_add_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_es = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_es = bms_copy(rel->eclass_source_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_es = bms_int_members(matching_es, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the sources belonging to this EquivalenceClass */
+	matching_es = bms_int_members(matching_es, ec->ec_source_indexes);
+
+	return matching_es;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_add_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *matching_eds = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		matching_eds = bms_copy(rel->eclass_derive_indexes);
+	}
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		matching_eds = bms_int_members(matching_eds, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* XXX should we keep this? */
+	i = -1;
+	while ((i = bms_next_member(matching_eds, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* leave only the derives belonging to this EquivalenceClass */
+	matching_eds = bms_int_members(matching_eds, ec->ec_derive_indexes);
+
+	return matching_eds;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 914bfd90bc..4520d6a256 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3070,8 +3070,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3088,8 +3088,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3113,9 +3114,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index a9943cd6e0..be11e1c053 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -898,9 +898,13 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				/* We can represent this sub_pathkey */
 				EquivalenceMember *sub_member;
 				EquivalenceClass *outer_ec;
+				int		first_em;
 
-				Assert(list_length(sub_eclass->ec_members) == 1);
-				sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
+				Assert(bms_membership(sub_eclass->ec_member_indexes) == BMS_SINGLETON);
+				first_em = bms_next_member(sub_eclass->ec_member_indexes, -1);
+				sub_member = list_nth_node(EquivalenceMember,
+										   rel->subroot->eq_members,
+										   first_em);
 
 				/*
 				 * Note: it might look funny to be setting sortref = 0 for a
@@ -955,18 +959,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1018,7 +1023,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 													  sub_pathkey->pk_strategy,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
-					score = list_length(outer_ec->ec_members) - 1;
+					score = bms_num_members(outer_ec->ec_member_indexes) - 1;
 					/* +1 if it matches the proper query_pathkeys item */
 					if (retvallen < outer_query_keys &&
 						list_nth(root->query_pathkeys, retvallen) == outer_pk)
@@ -1429,8 +1434,9 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
+		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1450,19 +1456,13 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_difference(other_parent_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ac86ce9003..748d750943 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1964,7 +1968,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2183,7 +2187,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2207,7 +2211,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2276,7 +2280,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4478,7 +4482,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4492,7 +4496,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6111,7 +6115,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6152,6 +6157,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 		if (ec->ec_has_volatile)
 		{
+			int		first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6161,8 +6168,10 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else if (reqColIdx != NULL)
 		{
@@ -6178,7 +6187,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6218,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6234,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6305,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6315,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6343,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6354,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6706,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6732,6 +6748,8 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
 		if (ec->ec_has_volatile)
 		{
+			int first_em;
+
 			/*
 			 * If the pathkey's EquivalenceClass is volatile, then it must
 			 * have come from an ORDER BY clause, and we have to match it to
@@ -6741,8 +6759,10 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 				elog(ERROR, "volatile EquivalenceClass has no sortref");
 			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
 			Assert(tle);
-			Assert(list_length(ec->ec_members) == 1);
-			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+			Assert(bms_membership(ec->ec_member_indexes) == BMS_SINGLETON);
+			first_em = bms_next_member(ec->ec_member_indexes, -1);
+			pk_datatype = list_nth_node(EquivalenceMember, root->eq_members,
+										first_em)->em_datatype;
 		}
 		else
 		{
@@ -6754,7 +6774,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 493a3af0fa..d50b27b7ca 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -619,6 +619,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index f4cdb879c2..d9c7378828 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f6046768fb..3015691d3e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index d7b4434e7f..1e6c1ff6a2 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK HACK HACK: Used to store ec_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -218,6 +221,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -630,6 +636,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -816,6 +825,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a544b313d3..c329c94747 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -300,6 +300,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -889,6 +898,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1283,9 +1310,17 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index be5e2287f3..526723a187 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -16,6 +16,7 @@
 
 #include "executor/tuptable.h"
 
+struct PlannerInfo;
 
 #define nodeDisplay(x)		pprint(x)
 
@@ -27,7 +28,9 @@ extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
 extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
+extern void print_pathkeys(const struct PlannerInfo *root,
+						   const List *pathkeys,
+						   const List *rtable);
 extern void print_tl(const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 41f765d342..07f53744c2 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 126f7047fe..69eb778627 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -229,12 +229,12 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                        QUERY PLAN                         
+-----------------------------------------------------------
  Nested Loop
    Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1)
    ->  Index Scan using ec1_pkey on ec1
-         Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint))
+         Index Cond: (ff = '42'::bigint)
          Filter: (ff = f1)
    ->  Append
          ->  Index Scan using ec1_expr2 on ec1 ec1_1
-- 
2.35.3.windows.1

v9-0002-Revert-one-of-the-optimizations.patchapplication/octet-stream; name=v9-0002-Revert-one-of-the-optimizations.patchDownload
From c0ff3535e4f589773b627202717bb1ae900f03ca Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 5 Sep 2022 17:17:13 +0900
Subject: [PATCH v9 2/4] Revert one of the optimizations

This commit reverts one of our optimizations.

This function enumerates EquivalenceMembers in the given
EquivalenceClass and returns true if there is a chance of merging. This
check is accomplished by finding EMs where
"bms_overlap(cur_em->em_relids, relids)" is false. In most cases, we
reach such EMs early in the loop. Therefore, performing our newly
introduced optimization technique is excessive here, and we revert the
change.
---
 src/backend/optimizer/path/equivclass.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index dd3652df70..7d2044b5fa 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3262,8 +3262,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
-	Bitmapset  *matching_ems;
 	Relids		relids;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3294,15 +3294,18 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* Find the EquivalenceMember for relids */
-	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
+	/* To join, we need a member not in the given rel */
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
+	{
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-	/*
-	 * If there are any other non-child members besides matching_ems then we
-	 * have a chance at merging.
-	 */
-	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
-		return true;
+		Assert(!cur_em->em_is_child);	/* ignore children here */
+
+		if (!bms_overlap(cur_em->em_relids, relids))
+			return true;
+	}
 
 	return false;
 }
-- 
2.35.3.windows.1

v9-0003-Use-conventional-algorithm-for-smaller-size-cases.patchapplication/octet-stream; name=v9-0003-Use-conventional-algorithm-for-smaller-size-cases.patchDownload
From b85289b511ed8242f08e83d8132070eb0a64d9be Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 20 Oct 2022 15:46:00 +0900
Subject: [PATCH v9 3/4] Use conventional algorithm for smaller size cases

---
 src/backend/optimizer/path/pathkeys.c | 58 ++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 9 deletions(-)

diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index be11e1c053..8426d2681a 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1434,9 +1434,6 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		Bitmapset		 *matching_ems;
-		Bitmapset		 *interesting_ems;
-		Bitmapset		 *other_parent_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1455,14 +1452,57 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		if (j < necs)
 			continue;
 
-		/* compute score */
-		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
-		other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
-		interesting_ems = bms_difference(other_parent_ems, matching_ems);
+		/* Compute score */
+		/*
+		 * We use two different algorithms depending on the size of the
+		 * problem. For small sizes, linear search is preferable, while
+		 * optimization based on bitmap sets is faster for larger sizes.
+		 */
+		/*
+		 * TODO: Switching two algorithms is not ideal and may be ugly
+		 * because it introduces code duplication. Is there a better way?
+		 */
+		/*
+		 * TODO: 32 is a magic number. Do we have to declare it as a constant
+		 * macro?
+		 */
+		if (root->simple_rel_array_size < 32)
+		{
+			int			score;
+			int			k;
+
+			/* compute score */
+			score = 0;
+			k = -1;
+			while ((k = bms_next_member(oeclass->ec_member_indexes, k)) >= 0)
+			{
+				EquivalenceMember *em = list_nth_node(EquivalenceMember,
+													  root->eq_members, k);
+
+				/* Potential future join partner? */
+				if (!em->em_is_const && !em->em_is_child &&
+					!bms_overlap(em->em_relids, joinrel->relids))
+					score++;
+			}
+
+			scores[necs] = score;
+		}
+		else
+		{
+			Bitmapset		 *matching_ems;
+			Bitmapset		 *interesting_ems;
+			Bitmapset		 *other_parent_ems;
+
+			/* compute score */
+			matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+			other_parent_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+			interesting_ems = bms_difference(other_parent_ems, matching_ems);
+
+			/* record results */
+			scores[necs] = bms_num_members(interesting_ems);
+		}
 
-		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
 	}
 
-- 
2.35.3.windows.1

v9-0004-Fix-incorrect-assertion.patchapplication/octet-stream; name=v9-0004-Fix-incorrect-assertion.patchDownload
From 3cba5ee32f2575956a646e2ea4f196042f8fcd43 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 29 Nov 2022 16:55:36 +0900
Subject: [PATCH v9 4/4] Fix incorrect assertion

---
 contrib/postgres_fdw/postgres_fdw.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index bf79daee58..eadd8672d7 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7478,10 +7478,10 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
-		Assert(bms_is_subset(em->em_relids, rel->relids));
 		Assert(!bms_is_empty(em->em_relids));
 
-		if (is_foreign_expr(root, rel, em->em_expr))
+		if (bms_is_subset(em->em_relids, rel->relids) &&
+			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
 
-- 
2.35.3.windows.1

#29David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#28)
3 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, 29 Nov 2022 at 21:59, Yuya Watari <watari.yuya@gmail.com> wrote:

Thank you for testing the patch with an actual query. This speedup is
very impressive. When I used an original query with 1024 partitions,
its planning time was about 200ms. Given that each partition is also
partitioned in your workload, I think the result of 1415ms is
reasonable.

I was looking again at the v9-0001 patch and I think we can do a
little better when building the Bitmapset of matching EMs. For
example, in the v9 patch, the code for get_ecmember_indexes_strict()
is doing:

+ if (!with_children)
+     matching_ems = bms_copy(ec->ec_nonchild_indexes);
+ else
+     matching_ems = bms_copy(ec->ec_member_indexes);
+
+ i = -1;
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+     RelOptInfo *rel = root->simple_rel_array[i];
+
+     matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+ }

It seems reasonable that if there are a large number of partitions
then ec_member_indexes will have a large number of Bitmapwords. When
we do bms_int_members() on that, we're going to probably end up with a
bunch of trailing zero words in the set. In the v10 patch, I've
changed this to become:

+    int            i = bms_next_member(relids, -1);
+
+    if (i >= 0)
+    {
+        RelOptInfo *rel = root->simple_rel_array[i];
+
+        /*
+         * bms_intersect to the first relation to try to keep the resulting
+         * Bitmapset as small as possible.  This saves having to make a
+         * complete bms_copy() of one of them.  One may contain significantly
+         * more words than the other.
+         */
+        if (!with_children)
+            matching_ems = bms_intersect(rel->eclass_member_indexes,
+                                         ec->ec_nonchild_indexes);
+        else
+            matching_ems = bms_intersect(rel->eclass_member_indexes,
+                                         ec->ec_member_indexes);
+
+        while ((i = bms_next_member(relids, i)) >= 0)
+        {
+            rel = root->simple_rel_array[i];
+            matching_ems = bms_int_members(matching_ems,
+                                           rel->eclass_member_indexes);
+        }
+    }

so, effectively we first bms_intersect to the first member of relids
before masking out the bits for the remaining ones. This should mean
we'll have a Bitmapset with fewer words in many complex planning
problems. There's no longer the dilemma of having to decide if we
should start with RelOptInfo's eclass_member_indexes or the
EquivalenceClass's member indexes. When using bms_int_member, we
really want to start with the smallest of those so we get the smallest
resulting set. With bms_intersect(), it will always make a copy of
the smallest set. v10 does that instead of bms_copy()ing the
EquivalenceClass's member's Bitmapset.

I also wondered how much we're losing to the fact that
bms_int_members() zeros the trailing words and does not trim the
Bitmapset down.

The problem there is 2-fold;
1) we have to zero the trailing words on the left input. That'll
pollute the CPU cache a bit as it may have to fetch a bunch of extra
cache lines, and;
2) subsequent bms_int_members() done afterwards may have to mask out
additional words. If we can make the shortest input really short, then
subsequent bms_int_members() are going to be very fast.

You might argue there that setting nwords to the shortest length may
cause us to have to repalloc the Bitmapset if we need to later add
more members again, but if you look at the repalloc() code, it's
effectively a no-op when the allocated size >= the requested size, so
repalloc() should be very fast in this case. So, worst case, there's
an additional "no-op" repalloc() (which should be very fast) followed
by maybe a bms_add_members() which has to zero the words instead of
bms_int_members(). I changed this in the v10-0002 patch. I'm not sure
if we should do this or not.

I also changed v10-0001 so that we still store the EquivalenceClass's
members list. There were a few places where the code just wanted to
get the first member and having to look at the Bitmapset index and
fetch the first match from PlannerInfo seemed convoluted. If the
query is simple, it seems like it's not going to be very expensive to
add a few EquivalenceMembers to this list. When planning more complex
problems, there's probably enough other extra overhead that we're
unlikely to notice the extra lappend()s. This also allows v10-0003 to
work, see below.

In v10-0003, I experimented with the iterator concept that I mentioned
earlier. Since v10-0001 is now storing the EquivalenceMember list in
EquivalenceClass again, it's now quite simple to have the iterator
decide if it should be scanning the index or doing a loop over all
members to find the ones matching the search. We can make this
decision based on list_length(ec->ec_members). This should be a more
reliable check than checking root->simple_rel_array_size as we could
still have classes with just a few members even when there's a large
number of rels in simple_rel_array. I was hoping that v10-0003 would
allow us to maintain the same planner performance for simple queries.
It just does not seem to change the performance much. Perhaps it's not
worth the complexity if there are no performance benefits. It probably
needs more performance testing than what I've done to know if it helps
or hinders, however.

Overall, I'm not quite sure if this is any faster than your v9 patch.
I think more performance testing needs to be done. I think the
v10-0001 + v10-0002 is faster than v9-0001, but perhaps the changes
you've made in v9-0002 and v9-0003 are worth redoing. I didn't test. I
was hoping to keep the logic about which method to use to find the
members in the iterator code and not litter it around the tree.

I did run the test you mentioned in [1]/messages/by-id/CAJ2pMkZNCgoUKSE+_5LthD+KbXKvq6h2hQN8Esxpxd+cxmgomg@mail.gmail.com and I got:

$ echo Master @ 29452de73 && ./partbench.sh | grep -E "^(Testing|latency)"
Master @ 29452de73
Testing with 2 partitions...
latency average = 0.231 ms
Testing with 4 partitions...
latency average = 0.303 ms
Testing with 8 partitions...
latency average = 0.454 ms
Testing with 16 partitions...
latency average = 0.777 ms
Testing with 32 partitions...
latency average = 1.576 ms
Testing with 64 partitions...
latency average = 3.574 ms
Testing with 128 partitions...
latency average = 9.504 ms
Testing with 256 partitions...
latency average = 37.321 ms
Testing with 512 partitions...
latency average = 171.660 ms
Testing with 1024 partitions...
latency average = 1021.990 ms

$ echo Master + v10-0001 && ./partbench.sh | grep -E "^(Testing|latency)"
Master + v10-0001
Testing with 2 partitions...
latency average = 0.239 ms
Testing with 4 partitions...
latency average = 0.315 ms
Testing with 8 partitions...
latency average = 0.463 ms
Testing with 16 partitions...
latency average = 0.757 ms
Testing with 32 partitions...
latency average = 1.481 ms
Testing with 64 partitions...
latency average = 2.563 ms
Testing with 128 partitions...
latency average = 5.618 ms
Testing with 256 partitions...
latency average = 16.229 ms
Testing with 512 partitions...
latency average = 38.855 ms
Testing with 1024 partitions...
latency average = 85.705 ms

$ echo Master + v10-0001 + v10-0002 && ./partbench.sh | grep -E
"^(Testing|latency)"
Master + v10-0001 + v10-0002
Testing with 2 partitions...
latency average = 0.241 ms
Testing with 4 partitions...
latency average = 0.312 ms
Testing with 8 partitions...
latency average = 0.459 ms
Testing with 16 partitions...
latency average = 0.755 ms
Testing with 32 partitions...
latency average = 1.464 ms
Testing with 64 partitions...
latency average = 2.580 ms
Testing with 128 partitions...
latency average = 5.652 ms
Testing with 256 partitions...
latency average = 16.464 ms
Testing with 512 partitions...
latency average = 37.674 ms
Testing with 1024 partitions...
latency average = 84.094 ms

$ echo Master + v10-0001 + v10-0002 + v10-0003 && ./partbench.sh |
grep -E "^(Testing|latency)"
Master + v10-0001 + v10-0002 + v10-0003
Testing with 2 partitions...
latency average = 0.240 ms
Testing with 4 partitions...
latency average = 0.318 ms
Testing with 8 partitions...
latency average = 0.465 ms
Testing with 16 partitions...
latency average = 0.763 ms
Testing with 32 partitions...
latency average = 1.486 ms
Testing with 64 partitions...
latency average = 2.858 ms
Testing with 128 partitions...
latency average = 5.764 ms
Testing with 256 partitions...
latency average = 16.995 ms
Testing with 512 partitions...
latency average = 38.012 ms
Testing with 1024 partitions...
latency average = 88.098 ms

$ echo Master + v9-* && ./partbench.sh | grep -E "^(Testing|latency)"
Master + v9-*
Testing with 2 partitions...
latency average = 0.237 ms
Testing with 4 partitions...
latency average = 0.313 ms
Testing with 8 partitions...
latency average = 0.460 ms
Testing with 16 partitions...
latency average = 0.780 ms
Testing with 32 partitions...
latency average = 1.468 ms
Testing with 64 partitions...
latency average = 2.701 ms
Testing with 128 partitions...
latency average = 5.275 ms
Testing with 256 partitions...
latency average = 17.208 ms
Testing with 512 partitions...
latency average = 37.183 ms
Testing with 1024 partitions...
latency average = 90.595 ms

David

[1]: /messages/by-id/CAJ2pMkZNCgoUKSE+_5LthD+KbXKvq6h2hQN8Esxpxd+cxmgomg@mail.gmail.com

Attachments:

v10-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchtext/plain; charset=US-ASCII; name=v10-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From 1e0edd92daf8bb433b3d5229edefb76c179dcef7 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v10 1/3] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   7 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 807 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  35 +-
 src/backend/optimizer/plan/createplan.c   |  66 +-
 src/backend/optimizer/plan/planagg.c      |   1 +
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  13 +
 src/include/nodes/pathnodes.h             |  75 +-
 src/include/optimizer/paths.h             |  28 +-
 14 files changed, 866 insertions(+), 240 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 20c7b1ad05..ff1ee9af89 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7403,6 +7403,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7461,18 +7465,22 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
@@ -7504,7 +7512,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7519,19 +7529,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7545,8 +7558,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8f150e9a2e..16390be0c1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 897309d7ec..6e747d60e6 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5502,7 +5502,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e65b967b1f..bf6ba3c6ec 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -333,7 +338,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +349,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -365,8 +371,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -379,10 +393,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -439,8 +459,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +473,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +486,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +563,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +626,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,9 +658,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -638,6 +746,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +770,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -700,8 +815,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +836,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +870,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +900,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -965,7 +1090,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1177,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1109,7 +1234,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1135,6 +1260,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1144,9 +1270,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1287,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1204,9 +1332,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1344,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1358,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1372,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1412,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1435,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1452,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1473,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1519,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1550,7 +1689,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1561,9 +1702,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1723,12 +1868,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1889,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1956,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1986,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2034,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2226,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2246,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2254,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2264,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,9 +2281,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2140,9 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2221,11 +2389,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2421,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2443,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2459,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2508,27 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2560,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2644,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2711,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2774,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,33 +2789,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2654,7 +2849,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,7 +2901,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2916,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,10 +2937,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2795,7 +2983,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2858,7 +3046,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2880,10 +3069,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2897,14 +3089,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3001,7 +3194,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3075,8 +3268,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  EquivalenceClass *eclass,
 						  RelOptInfo *rel)
 {
+	Bitmapset  *matching_ems;
 	Relids		relids;
-	ListCell   *lc;
 
 	Assert(!eclass->ec_merged);
 
@@ -3084,12 +3277,13 @@ eclass_useful_for_merging(PlannerInfo *root,
 	 * Won't generate joinclauses if const or single-member (the latter test
 	 * covers the volatile case too)
 	 */
-	if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
+	if (eclass->ec_has_const ||
+		bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE)
 		return false;
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3106,17 +3300,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+	/* Find the EquivalenceMember for relids */
+	matching_ems = get_ecmember_indexes(root, eclass, relids, false, false);
 
-		if (!bms_overlap(cur_em->em_relids, relids))
-			return true;
-	}
+	/*
+	 * If there are any other non-child members besides matching_ems then we
+	 * have a chance at merging.
+	 */
+	if (bms_nonempty_difference(eclass->ec_nonchild_indexes, matching_ems))
+		return true;
 
 	return false;
 }
@@ -3200,7 +3392,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3428,288 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 914bfd90bc..4520d6a256 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3070,8 +3070,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3088,8 +3088,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3113,9 +3114,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index a9943cd6e0..db61aedba5 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -955,18 +955,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1429,8 +1430,8 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
-		int			score;
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		Bitmapset		 *interesting_ems;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1450,20 +1451,18 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
-		score = 0;
-		foreach(lc2, oeclass->ec_members)
-		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
-				score++;
-		}
+		matching_ems = get_ecmember_indexes(root, oeclass, joinrel->relids, false, false);
+		interesting_ems = bms_difference(oeclass->ec_nonchild_indexes, oeclass->ec_norel_indexes);
+		interesting_ems = bms_del_members(interesting_ems, matching_ems);
 
+		/* record results */
 		ecs[necs] = oeclass;
-		scores[necs] = score;
+		scores[necs] = bms_num_members(interesting_ems);
 		necs++;
+
+		/* cleanup */
+		bms_free(matching_ems);
+		bms_free(interesting_ems);
 	}
 
 	/*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 66139928e8..4a6e3e77db 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4483,7 +4487,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4497,7 +4501,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6117,7 +6121,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6184,7 +6189,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6215,7 +6220,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6231,7 +6236,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6302,7 +6307,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6311,7 +6317,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6337,7 +6345,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6347,7 +6356,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6697,7 +6708,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6760,7 +6772,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index ab3f7abba1..61c36d69af 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 799602f5ea..b62dbb964f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -620,6 +620,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 2ea3ca734e..26e3385c27 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -998,6 +998,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f6046768fb..3015691d3e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index d7b4434e7f..0674c13770 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -97,6 +97,10 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK: Used to store eclass_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+	root->simple_rel_array[0]->eclass_member_indexes = NULL;
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -218,6 +222,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -630,6 +637,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -816,6 +826,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index dbaa9bb54d..7bb981b628 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -303,6 +303,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -895,6 +904,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1274,6 +1301,39 @@ typedef struct StatisticExtInfo
  * the included values might be all NULL rather than all the same non-null
  * values.  See src/backend/optimizer/README for more on that point.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1291,9 +1351,18 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 41f765d342..07f53744c2 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
-- 
2.35.1.windows.2

v10-0003-Add-iterators-for-looping-over-EquivalenceMember.patchtext/plain; charset=US-ASCII; name=v10-0003-Add-iterators-for-looping-over-EquivalenceMember.patchDownload
From 332896bd0dafaed00fb1faad615f605121c07883 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sat, 3 Dec 2022 18:02:28 +1300
Subject: [PATCH v10 3/3] Add iterators for looping over EquivalenceMembers

Here we introduce EquivalenceMemberIterator which can be used to search
for EquivalenceMembers matching a given search criteria.  This allows us
to switch between a linear search over all members in an EquivalenceClass
and a bitmap index search over the members.  The latter of these is used
when the number of members in the EquivalenceClass reaches a given
threshold, over which we assume that the index search will be faster.
Currently that threshold is set to 16.  This value may need to be refined
further.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +--
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++++++++++-----
 src/include/optimizer/paths.h           |  39 +++
 3 files changed, 307 insertions(+), 80 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index ff1ee9af89..9806802a6a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7465,15 +7465,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	Bitmapset *matching_ems;
-	int		i = -1;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_next(&iter)) != 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.
@@ -7482,10 +7480,12 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
 		if (bms_is_subset(em->em_relids, rel->relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7512,9 +7512,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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
 		Relids		expr_relids;
-		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7530,14 +7530,12 @@ 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);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = list_nth_node(EquivalenceMember,
-												  root->eq_members, j);
 			Expr	   *em_expr;
 
 			/* don't expect constants */
@@ -7556,11 +7554,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
 		i++;
 		bms_free(expr_relids);
-		bms_free(matching_ems);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index bf6ba3c6ec..aeb8f3324c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -754,8 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
-		int			i;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -770,15 +770,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);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -798,6 +794,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -905,22 +903,20 @@ find_ec_member_matching_expr(PlannerInfo *root,
 							 Expr *expr,
 							 Relids relids)
 {
-	Bitmapset  *matching_ems;
+	EquivalenceMemberIterator iter;
 	Relids		expr_relids;
-	int			i;
+	EquivalenceMember *em;
 
 	/* 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);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = list_nth_node(EquivalenceMember,
-											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -945,10 +941,13 @@ find_ec_member_matching_expr(PlannerInfo *root,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -1685,13 +1684,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	Bitmapset  *matching_ems;
 	ListCell   *lc1;
-	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1702,14 +1701,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);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((cur_em = eclass_member_iterator_next(&iter)) != 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
@@ -1726,6 +1721,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1823,7 +1820,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2264,7 +2261,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
 		int			i;
 
@@ -2281,15 +2279,12 @@ 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)
-		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2297,6 +2292,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2309,11 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		i = -1;
 		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2774,8 +2771,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2789,16 +2786,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2857,6 +2853,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2901,8 +2898,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2916,16 +2913,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2988,6 +2984,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 									 true, cur_em->em_datatype);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -3046,7 +3043,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 iter;
 		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
@@ -3069,19 +3066,19 @@ 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)
-		{
-			cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -3539,6 +3536,195 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	return matching_ems;
 }
 
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
 /*
  * get_ec_source_indexes
  *		Returns a Bitmapset with indexes into root->eq_sources for all
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 07f53744c2..3f6a0173d6 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ *
+ * Callers should assume all of the fields within this struct are internal.
+ *
+ * XXX where should this struct go? Not here.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members;	/* include members with empty em_relids */
+	int			current_index;	/* current iterator position, or -1. */
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;		/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -198,6 +223,20 @@ extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  Relids relids,
 											  bool with_children,
 											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
 										Relids relids);
-- 
2.35.1.windows.2

v10-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchtext/plain; charset=US-ASCII; name=v10-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From 606fcae0b156dbdfed3005eb42627fa8a66e12f4 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v10 2/3] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index b7b274aeff..f287a062fa 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.35.1.windows.2

#30Thom Brown
thom@linux.com
In reply to: David Rowley (#29)
Re: [PoC] Reducing planning time when tables have many partitions

On Sun, 4 Dec 2022 at 00:35, David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 29 Nov 2022 at 21:59, Yuya Watari <watari.yuya@gmail.com> wrote:

Thank you for testing the patch with an actual query. This speedup is
very impressive. When I used an original query with 1024 partitions,
its planning time was about 200ms. Given that each partition is also
partitioned in your workload, I think the result of 1415ms is
reasonable.

I was looking again at the v9-0001 patch and I think we can do a
little better when building the Bitmapset of matching EMs. For
example, in the v9 patch, the code for get_ecmember_indexes_strict()
is doing:

+ if (!with_children)
+     matching_ems = bms_copy(ec->ec_nonchild_indexes);
+ else
+     matching_ems = bms_copy(ec->ec_member_indexes);
+
+ i = -1;
+ while ((i = bms_next_member(relids, i)) >= 0)
+ {
+     RelOptInfo *rel = root->simple_rel_array[i];
+
+     matching_ems = bms_int_members(matching_ems, rel->eclass_member_indexes);
+ }

It seems reasonable that if there are a large number of partitions
then ec_member_indexes will have a large number of Bitmapwords. When
we do bms_int_members() on that, we're going to probably end up with a
bunch of trailing zero words in the set. In the v10 patch, I've
changed this to become:

+    int            i = bms_next_member(relids, -1);
+
+    if (i >= 0)
+    {
+        RelOptInfo *rel = root->simple_rel_array[i];
+
+        /*
+         * bms_intersect to the first relation to try to keep the resulting
+         * Bitmapset as small as possible.  This saves having to make a
+         * complete bms_copy() of one of them.  One may contain significantly
+         * more words than the other.
+         */
+        if (!with_children)
+            matching_ems = bms_intersect(rel->eclass_member_indexes,
+                                         ec->ec_nonchild_indexes);
+        else
+            matching_ems = bms_intersect(rel->eclass_member_indexes,
+                                         ec->ec_member_indexes);
+
+        while ((i = bms_next_member(relids, i)) >= 0)
+        {
+            rel = root->simple_rel_array[i];
+            matching_ems = bms_int_members(matching_ems,
+                                           rel->eclass_member_indexes);
+        }
+    }

so, effectively we first bms_intersect to the first member of relids
before masking out the bits for the remaining ones. This should mean
we'll have a Bitmapset with fewer words in many complex planning
problems. There's no longer the dilemma of having to decide if we
should start with RelOptInfo's eclass_member_indexes or the
EquivalenceClass's member indexes. When using bms_int_member, we
really want to start with the smallest of those so we get the smallest
resulting set. With bms_intersect(), it will always make a copy of
the smallest set. v10 does that instead of bms_copy()ing the
EquivalenceClass's member's Bitmapset.

I also wondered how much we're losing to the fact that
bms_int_members() zeros the trailing words and does not trim the
Bitmapset down.

The problem there is 2-fold;
1) we have to zero the trailing words on the left input. That'll
pollute the CPU cache a bit as it may have to fetch a bunch of extra
cache lines, and;
2) subsequent bms_int_members() done afterwards may have to mask out
additional words. If we can make the shortest input really short, then
subsequent bms_int_members() are going to be very fast.

You might argue there that setting nwords to the shortest length may
cause us to have to repalloc the Bitmapset if we need to later add
more members again, but if you look at the repalloc() code, it's
effectively a no-op when the allocated size >= the requested size, so
repalloc() should be very fast in this case. So, worst case, there's
an additional "no-op" repalloc() (which should be very fast) followed
by maybe a bms_add_members() which has to zero the words instead of
bms_int_members(). I changed this in the v10-0002 patch. I'm not sure
if we should do this or not.

I also changed v10-0001 so that we still store the EquivalenceClass's
members list. There were a few places where the code just wanted to
get the first member and having to look at the Bitmapset index and
fetch the first match from PlannerInfo seemed convoluted. If the
query is simple, it seems like it's not going to be very expensive to
add a few EquivalenceMembers to this list. When planning more complex
problems, there's probably enough other extra overhead that we're
unlikely to notice the extra lappend()s. This also allows v10-0003 to
work, see below.

In v10-0003, I experimented with the iterator concept that I mentioned
earlier. Since v10-0001 is now storing the EquivalenceMember list in
EquivalenceClass again, it's now quite simple to have the iterator
decide if it should be scanning the index or doing a loop over all
members to find the ones matching the search. We can make this
decision based on list_length(ec->ec_members). This should be a more
reliable check than checking root->simple_rel_array_size as we could
still have classes with just a few members even when there's a large
number of rels in simple_rel_array. I was hoping that v10-0003 would
allow us to maintain the same planner performance for simple queries.
It just does not seem to change the performance much. Perhaps it's not
worth the complexity if there are no performance benefits. It probably
needs more performance testing than what I've done to know if it helps
or hinders, however.

Overall, I'm not quite sure if this is any faster than your v9 patch.
I think more performance testing needs to be done. I think the
v10-0001 + v10-0002 is faster than v9-0001, but perhaps the changes
you've made in v9-0002 and v9-0003 are worth redoing. I didn't test. I
was hoping to keep the logic about which method to use to find the
members in the iterator code and not litter it around the tree.

I did run the test you mentioned in [1] and I got:

$ echo Master @ 29452de73 && ./partbench.sh | grep -E "^(Testing|latency)"
Master @ 29452de73
Testing with 2 partitions...
latency average = 0.231 ms
Testing with 4 partitions...
latency average = 0.303 ms
Testing with 8 partitions...
latency average = 0.454 ms
Testing with 16 partitions...
latency average = 0.777 ms
Testing with 32 partitions...
latency average = 1.576 ms
Testing with 64 partitions...
latency average = 3.574 ms
Testing with 128 partitions...
latency average = 9.504 ms
Testing with 256 partitions...
latency average = 37.321 ms
Testing with 512 partitions...
latency average = 171.660 ms
Testing with 1024 partitions...
latency average = 1021.990 ms

$ echo Master + v10-0001 && ./partbench.sh | grep -E "^(Testing|latency)"
Master + v10-0001
Testing with 2 partitions...
latency average = 0.239 ms
Testing with 4 partitions...
latency average = 0.315 ms
Testing with 8 partitions...
latency average = 0.463 ms
Testing with 16 partitions...
latency average = 0.757 ms
Testing with 32 partitions...
latency average = 1.481 ms
Testing with 64 partitions...
latency average = 2.563 ms
Testing with 128 partitions...
latency average = 5.618 ms
Testing with 256 partitions...
latency average = 16.229 ms
Testing with 512 partitions...
latency average = 38.855 ms
Testing with 1024 partitions...
latency average = 85.705 ms

$ echo Master + v10-0001 + v10-0002 && ./partbench.sh | grep -E
"^(Testing|latency)"
Master + v10-0001 + v10-0002
Testing with 2 partitions...
latency average = 0.241 ms
Testing with 4 partitions...
latency average = 0.312 ms
Testing with 8 partitions...
latency average = 0.459 ms
Testing with 16 partitions...
latency average = 0.755 ms
Testing with 32 partitions...
latency average = 1.464 ms
Testing with 64 partitions...
latency average = 2.580 ms
Testing with 128 partitions...
latency average = 5.652 ms
Testing with 256 partitions...
latency average = 16.464 ms
Testing with 512 partitions...
latency average = 37.674 ms
Testing with 1024 partitions...
latency average = 84.094 ms

$ echo Master + v10-0001 + v10-0002 + v10-0003 && ./partbench.sh |
grep -E "^(Testing|latency)"
Master + v10-0001 + v10-0002 + v10-0003
Testing with 2 partitions...
latency average = 0.240 ms
Testing with 4 partitions...
latency average = 0.318 ms
Testing with 8 partitions...
latency average = 0.465 ms
Testing with 16 partitions...
latency average = 0.763 ms
Testing with 32 partitions...
latency average = 1.486 ms
Testing with 64 partitions...
latency average = 2.858 ms
Testing with 128 partitions...
latency average = 5.764 ms
Testing with 256 partitions...
latency average = 16.995 ms
Testing with 512 partitions...
latency average = 38.012 ms
Testing with 1024 partitions...
latency average = 88.098 ms

$ echo Master + v9-* && ./partbench.sh | grep -E "^(Testing|latency)"
Master + v9-*
Testing with 2 partitions...
latency average = 0.237 ms
Testing with 4 partitions...
latency average = 0.313 ms
Testing with 8 partitions...
latency average = 0.460 ms
Testing with 16 partitions...
latency average = 0.780 ms
Testing with 32 partitions...
latency average = 1.468 ms
Testing with 64 partitions...
latency average = 2.701 ms
Testing with 128 partitions...
latency average = 5.275 ms
Testing with 256 partitions...
latency average = 17.208 ms
Testing with 512 partitions...
latency average = 37.183 ms
Testing with 1024 partitions...
latency average = 90.595 ms

Testing your patches with the same 1024 partitions, each with 64
sub-partitions, I get a planning time of 205.020 ms, which is now a
1,377x speedup. This has essentially reduced the planning time from a
catastrophe to a complete non-issue. Huge win!

--
Thom

#31David Rowley
dgrowleyml@gmail.com
In reply to: Thom Brown (#30)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, 6 Dec 2022 at 04:45, Thom Brown <thom@linux.com> wrote:

Testing your patches with the same 1024 partitions, each with 64
sub-partitions, I get a planning time of 205.020 ms, which is now a
1,377x speedup. This has essentially reduced the planning time from a
catastrophe to a complete non-issue. Huge win!

Thanks for testing the v10 patches.

I wouldn't have expected such additional gains from v10. I was mostly
focused on trying to minimise any performance regression for simple
queries that wouldn't benefit from indexing the EquivalenceMembers.
Your query sounds like it does not fit into that category. Perhaps it
is down to the fact that v9-0002 or v9-0003 reverts a couple of the
optimisations that is causing v9 to be slower than v10 for your query.
It's hard to tell without more details of what you're running.

Is this a schema and query you're able to share? Or perhaps mock up a
script of something similar enough to allow us to see why v9 and v10
are so different?

Additionally, it would be interesting to see if patching with v10-0002
alone helps the performance of your query at all. I didn't imagine
that change would give us anything easily measurable, but partition
pruning makes extensive use of Bitmapsets, so perhaps you've found
something. If you have then it might be worth considering v10-0002
independently of the EquivalenceMember indexing work.

David

#32Thom Brown
thom@linux.com
In reply to: David Rowley (#31)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, 5 Dec 2022 at 21:28, David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 6 Dec 2022 at 04:45, Thom Brown <thom@linux.com> wrote:

Testing your patches with the same 1024 partitions, each with 64
sub-partitions, I get a planning time of 205.020 ms, which is now a
1,377x speedup. This has essentially reduced the planning time from a
catastrophe to a complete non-issue. Huge win!

Thanks for testing the v10 patches.

I wouldn't have expected such additional gains from v10. I was mostly
focused on trying to minimise any performance regression for simple
queries that wouldn't benefit from indexing the EquivalenceMembers.
Your query sounds like it does not fit into that category. Perhaps it
is down to the fact that v9-0002 or v9-0003 reverts a couple of the
optimisations that is causing v9 to be slower than v10 for your query.
It's hard to tell without more details of what you're running.

I celebrated prematurely as I neglected to wait for the 6th execution
of the prepared statement, which shows the real result. With the v10
patches, it takes 5632.040 ms, a speedup of 50x.

Testing the v9 patches, the same query takes 3388.173 ms, a speedup of
83x. And re-testing v8, I'm getting roughly the same times. These
are all with a cold cache.

So the result isn't as dramatic as I had initially interpreted it to
have unfortunately.

--
Thom

#33Yuya Watari
watari.yuya@gmail.com
In reply to: Thom Brown (#32)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

Thank you for creating the v10 patches.

On Sun, Dec 4, 2022 at 9:34 AM David Rowley <dgrowleyml@gmail.com> wrote:

Overall, I'm not quite sure if this is any faster than your v9 patch.
I think more performance testing needs to be done. I think the
v10-0001 + v10-0002 is faster than v9-0001, but perhaps the changes
you've made in v9-0002 and v9-0003 are worth redoing. I didn't test. I
was hoping to keep the logic about which method to use to find the
members in the iterator code and not litter it around the tree.

I tested the performance of v9, v10, and v10 + v9-0002 + v9-0003. The
last one is v10 with v9-0002 and v9-0003 applied.

1. Join Order Benchmark

I ran the Join Order Benchmark [1]https://github.com/winkyao/join-order-benchmark and measured its planning times.
The result is shown in Table 1.

Table 1: Speedup of Join Order Benchmark (higher is better)
(n = the number of partitions)
-------------------------------------------------
n | v9 | v10 | v10 + v9-0002 + v9-0003
-------------------------------------------------
2 | 97.2% | 95.7% | 97.5%
4 | 98.0% | 96.7% | 97.3%
8 | 101.2% | 99.6% | 100.3%
16 | 107.0% | 106.7% | 107.5%
32 | 123.1% | 122.0% | 123.7%
64 | 161.9% | 162.0% | 162.6%
128 | 307.0% | 311.7% | 313.4%
256 | 780.1% | 805.5% | 816.4%
-------------------------------------------------

This result indicates that v10 degraded slightly more for the smaller
number of partitions. The performances of v9 and v10 + v9-0002 +
v9-0003 were almost the same, but the latter was faster when the
number of partitions was large.

2. Query A (The query mentioned in [2]/messages/by-id/CAJ2pMkZNCgoUKSE+_5LthD+KbXKvq6h2hQN8Esxpxd+cxmgomg@mail.gmail.com)

I also ran Query A, which I shared in [2]/messages/by-id/CAJ2pMkZNCgoUKSE+_5LthD+KbXKvq6h2hQN8Esxpxd+cxmgomg@mail.gmail.com and you used in
./partbench.sh. The attached figure illustrates the planning times of
Query A. Our patches might have had some degradations, but they were
not so significant.

3. Query B (The query mentioned in [3]/messages/by-id/CAJ2pMka2PBXNNzUfe0-ksFsxVN+gmfKq7aGQ5v35TcpjFG3Ggg@mail.gmail.com)

The following tables show the results of Query B. The results are
close to the one of the Join Order Benchmark; v9 and v10 + v9-0002 +
v9-0003 had fewer degradations than v10.

Table 2: Planning Time of Query B (ms)
--------------------------------------------------------------
n | Master | v9 | v10 | v10 + v9-0002 + v9-0003
--------------------------------------------------------------
1 | 36.056 | 37.730 | 38.546 | 37.782
2 | 35.035 | 37.190 | 37.472 | 36.393
4 | 36.860 | 37.478 | 38.312 | 37.388
8 | 41.099 | 40.152 | 40.705 | 40.268
16 | 52.852 | 44.926 | 45.956 | 45.211
32 | 87.042 | 54.919 | 55.287 | 55.125
64 | 224.750 | 82.125 | 81.323 | 80.567
128 | 901.226 | 136.631 | 136.632 | 132.840
256 | 4166.045 | 263.913 | 260.295 | 258.453
--------------------------------------------------------------

Table 3: Speedup of Query B (higher is better)
---------------------------------------------------
n | v9 | v10 | v10 + v9-0002 + v9-0003
---------------------------------------------------
1 | 95.6% | 93.5% | 95.4%
2 | 94.2% | 93.5% | 96.3%
4 | 98.4% | 96.2% | 98.6%
8 | 102.4% | 101.0% | 102.1%
16 | 117.6% | 115.0% | 116.9%
32 | 158.5% | 157.4% | 157.9%
64 | 273.7% | 276.4% | 279.0%
128 | 659.6% | 659.6% | 678.4%
256 | 1578.6% | 1600.5% | 1611.9%
---------------------------------------------------

======

The above results show that the reverts I have made in v9-0002 and
v9-0003 are very important in avoiding degradation. I think we should
apply these changes again. It is unclear whether v9 or v10 + v9-0002 +
v9-0003 is better, but the latter performed better in my experiments.

[1]: https://github.com/winkyao/join-order-benchmark
[2]: /messages/by-id/CAJ2pMkZNCgoUKSE+_5LthD+KbXKvq6h2hQN8Esxpxd+cxmgomg@mail.gmail.com
[3]: /messages/by-id/CAJ2pMka2PBXNNzUfe0-ksFsxVN+gmfKq7aGQ5v35TcpjFG3Ggg@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

figure.pngimage/png; name=figure.pngDownload
#34David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#33)
3 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Thank you for running all the benchmarks on v10.

On Thu, 8 Dec 2022 at 00:31, Yuya Watari <watari.yuya@gmail.com> wrote:

The above results show that the reverts I have made in v9-0002 and
v9-0003 are very important in avoiding degradation. I think we should
apply these changes again. It is unclear whether v9 or v10 + v9-0002 +
v9-0003 is better, but the latter performed better in my experiments.

I was hoping to keep the logic which decides to loop over ec_members
or use the bitmap indexes all in equivclass.c, ideally in the iterator
code.

I've looked at the v9-0002 patch and I'm thinking maybe it's ok since
it always loops over ec_nonchild_indexes. We process the base
relations first, so all the EquivalenceMember in PlannerInfo for these
will be at the start of the eq_members list and the Bitmapset won't
have many bitmapwords to loop over. Additionally, it's only looping
over the nonchild ones, so a large number of partitions existing has
no effect on the number of loops performed.

For v9-0003, I was really hoping to find some kind of workaround so we
didn't need the "if (root->simple_rel_array_size < 32)". The problem
I have with that is; 1) why is 32 a good choice?, and 2)
simple_rel_array_size is just not a great thing to base the decision
off of. For #1, we only need to look at the EquivalenceMembers
belonging to base relations here and simple_rel_array_size includes
all relations, including partitions, so even if there's just a few
members belonging to base rels, we may still opt to use the Bitmapset
method. Additionally, it does look like this patch should be looping
over ec_nonchild_indexes rather than ec_member_indexes and filtering
out the !em->em_is_const && !em->em_is_child EquivalenceMembers.

Since both the changes made in v9-0002 and v9-0003 can just be made to
loop over ec_nonchild_indexes, which isn't going to get big with large
numbers of partitions, then I wonder if we're ok just to do the loop
in all cases rather than conditionally try to do something more
fanciful with counting bits like I had done in
select_outer_pathkeys_for_merge(). I've made v11 work like what
v9-0003 did and I've used v9-0002. I also found a stray remaining
"bms_membership(eclass->ec_member_indexes) != BMS_MULTIPLE" in
eclass_useful_for_merging() that should have been put back to
"list_length(eclass->ec_members) <= 1".

I've still got a couple of things in mind that I'd like to see done to
this patch.

a) I think the iterator code should have some additional sanity checks
that the results of both methods match when building with
USE_ASSERT_CHECKING. I've got some concerns that we might break
something. The logic about what the em_relids is set to for child
members is a little confusing. See add_eq_member().
b) We still need to think about if adding a RelOptInfo to
PlannerInfo->simple_rel_array[0] is a good idea for solving the append
relation issue. Ideally, we'd have a proper varno for these Vars
instead of setting varno=0 per what's being done in
generate_append_tlist().

I've attached the v11 set of patches.

David

Attachments:

v11-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchtext/plain; charset=US-ASCII; name=v11-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From 3645a8b7dacc6564ab37f803608c86cbddb7a98e Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v11 1/3] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   7 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 796 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  26 +-
 src/backend/optimizer/plan/createplan.c   |  66 +-
 src/backend/optimizer/plan/planagg.c      |   1 +
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  13 +
 src/include/nodes/pathnodes.h             |  75 +-
 src/include/optimizer/paths.h             |  28 +-
 14 files changed, 860 insertions(+), 226 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1ceac2e0cf..b088724c91 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7402,6 +7402,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7460,18 +7464,22 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
@@ -7503,7 +7511,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7518,19 +7528,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7544,8 +7557,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 59b0fdeb62..83fa5dcc69 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 897309d7ec..6e747d60e6 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5502,7 +5502,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e65b967b1f..570b279f5a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -333,7 +338,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +349,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -365,8 +371,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -379,10 +393,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -439,8 +459,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +473,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +486,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +563,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +626,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,9 +658,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -638,6 +746,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +770,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -700,8 +815,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +836,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +870,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +900,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -965,7 +1090,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1177,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1109,7 +1234,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1135,6 +1260,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1144,9 +1270,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1287,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1204,9 +1332,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1344,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1358,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1372,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1412,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1435,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1452,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1473,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1519,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1550,7 +1689,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1561,9 +1702,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1723,12 +1868,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1889,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1956,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1986,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2034,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2226,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2246,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2254,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2264,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,9 +2281,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2140,9 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2221,11 +2389,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2421,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2443,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2459,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2508,27 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2560,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2644,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2711,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2774,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,33 +2789,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2654,7 +2849,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,7 +2901,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2916,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,10 +2937,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2795,7 +2983,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2858,7 +3046,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2880,10 +3069,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2897,14 +3089,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3001,7 +3194,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3076,7 +3269,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3089,7 +3282,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3107,12 +3300,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3200,7 +3395,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3431,288 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 914bfd90bc..4520d6a256 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3070,8 +3070,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3088,8 +3088,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3113,9 +3114,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index a9943cd6e0..b9d7d65c3c 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -955,18 +955,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1430,7 +1431,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1451,13 +1452,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 66139928e8..4a6e3e77db 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4483,7 +4487,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4497,7 +4501,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6117,7 +6121,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6184,7 +6189,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6215,7 +6220,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6231,7 +6236,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6302,7 +6307,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6311,7 +6317,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6337,7 +6345,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6347,7 +6356,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6697,7 +6708,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6760,7 +6772,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index ab3f7abba1..61c36d69af 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5dd4f92720..e564237110 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -624,6 +624,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index c2239d18b4..569c9d9aab 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -991,6 +991,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f6046768fb..3015691d3e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 7085cf3c41..4242526882 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -98,6 +98,10 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK: Used to store eclass_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+	root->simple_rel_array[0]->eclass_member_indexes = NULL;
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -219,6 +223,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -649,6 +656,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -835,6 +845,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 654dba61aa..eed159f9f9 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -306,6 +306,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -898,6 +907,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1277,6 +1304,39 @@ typedef struct StatisticExtInfo
  * the included values might be all NULL rather than all the same non-null
  * values.  See src/backend/optimizer/README for more on that point.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1294,9 +1354,18 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 41f765d342..07f53744c2 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
-- 
2.35.1.windows.2

v11-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchtext/plain; charset=US-ASCII; name=v11-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From bde2644f800febcf08b45c6670ca43e0d1f35a5a Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v11 2/3] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index b7b274aeff..f287a062fa 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.35.1.windows.2

v11-0003-Add-iterators-for-looping-over-EquivalenceMember.patchtext/plain; charset=US-ASCII; name=v11-0003-Add-iterators-for-looping-over-EquivalenceMember.patchDownload
From f545a618e74bc70b6d027c7c5b4c119ce86c6308 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sat, 3 Dec 2022 18:02:28 +1300
Subject: [PATCH v11 3/3] Add iterators for looping over EquivalenceMembers

Here we introduce EquivalenceMemberIterator which can be used to search
for EquivalenceMembers matching a given search criteria.  This allows us
to switch between a linear search over all members in an EquivalenceClass
and a bitmap index search over the members.  The latter of these is used
when the number of members in the EquivalenceClass reaches a given
threshold, over which we assume that the index search will be faster.
Currently that threshold is set to 16.  This value may need to be refined
further.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +--
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++++++++++-----
 src/include/optimizer/paths.h           |  39 +++
 3 files changed, 307 insertions(+), 80 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b088724c91..c5107d35b7 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7464,15 +7464,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	Bitmapset *matching_ems;
-	int		i = -1;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_next(&iter)) != 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.
@@ -7481,10 +7479,12 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
 		if (bms_is_subset(em->em_relids, rel->relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7511,9 +7511,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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
 		Relids		expr_relids;
-		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7529,14 +7529,12 @@ 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);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = list_nth_node(EquivalenceMember,
-												  root->eq_members, j);
 			Expr	   *em_expr;
 
 			/* don't expect constants */
@@ -7555,11 +7553,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
 		i++;
 		bms_free(expr_relids);
-		bms_free(matching_ems);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 570b279f5a..b4184269c3 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -754,8 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
-		int			i;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -770,15 +770,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);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -798,6 +794,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -905,22 +903,20 @@ find_ec_member_matching_expr(PlannerInfo *root,
 							 Expr *expr,
 							 Relids relids)
 {
-	Bitmapset  *matching_ems;
+	EquivalenceMemberIterator iter;
 	Relids		expr_relids;
-	int			i;
+	EquivalenceMember *em;
 
 	/* 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);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = list_nth_node(EquivalenceMember,
-											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -945,10 +941,13 @@ find_ec_member_matching_expr(PlannerInfo *root,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -1685,13 +1684,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	Bitmapset  *matching_ems;
 	ListCell   *lc1;
-	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1702,14 +1701,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);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((cur_em = eclass_member_iterator_next(&iter)) != 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
@@ -1726,6 +1721,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1823,7 +1820,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2264,7 +2261,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
 		int			i;
 
@@ -2281,15 +2279,12 @@ 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)
-		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2297,6 +2292,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2309,11 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		i = -1;
 		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2774,8 +2771,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2789,16 +2786,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2857,6 +2853,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2901,8 +2898,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2916,16 +2913,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2988,6 +2984,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 									 true, cur_em->em_datatype);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -3046,7 +3043,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 iter;
 		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
@@ -3069,19 +3066,19 @@ 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)
-		{
-			cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -3542,6 +3539,195 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	return matching_ems;
 }
 
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
 /*
  * get_ec_source_indexes
  *		Returns a Bitmapset with indexes into root->eq_sources for all
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 07f53744c2..3f6a0173d6 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ *
+ * Callers should assume all of the fields within this struct are internal.
+ *
+ * XXX where should this struct go? Not here.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members;	/* include members with empty em_relids */
+	int			current_index;	/* current iterator position, or -1. */
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;		/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -198,6 +223,20 @@ extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  Relids relids,
 											  bool with_children,
 											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
 										Relids relids);
-- 
2.35.1.windows.2

#35Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#34)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear David,

On Mon, Dec 12, 2022 at 1:50 PM David Rowley <dgrowleyml@gmail.com> wrote:

I've attached the v11 set of patches.

Thanks for creating the v11 version. I think your patches look good to
me. I really apologize for my late reply.

a) I think the iterator code should have some additional sanity checks
that the results of both methods match when building with
USE_ASSERT_CHECKING. I've got some concerns that we might break
something. The logic about what the em_relids is set to for child
members is a little confusing. See add_eq_member().

I added sanity checking code to check that two iteration results are
the same. I have attached a new version of the patch, v12, to this
email.

The implementation of my sanity checking code (v12-0004) is not ideal
and a little ugly. I understand that and will try to improve it.

However, there is more bad news. Unfortunately, some regression tests
are failing in my environment. I'm not sure why, but it could be that
a) my sanity checking code (v12-0004) is wrong, or b) our patches have
some bugs.

I will investigate this issue further, and share the results when found.

--
Best regards,
Yuya Watari

Attachments:

v12-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchapplication/octet-stream; name=v12-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From a06c4255dcb43fe1c10c0c9e9a8c318352c29720 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v12 1/4] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   7 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 796 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  26 +-
 src/backend/optimizer/plan/createplan.c   |  66 +-
 src/backend/optimizer/plan/planagg.c      |   1 +
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  13 +
 src/include/nodes/pathnodes.h             |  75 +-
 src/include/optimizer/paths.h             |  28 +-
 14 files changed, 860 insertions(+), 226 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 50d23f922c..f389f70c16 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7619,6 +7619,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7677,18 +7681,22 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
@@ -7720,7 +7728,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7735,19 +7745,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7761,8 +7774,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6b368b08b2..98becac509 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 29ae32d960..595aaf74f3 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5501,7 +5501,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7d7e6facdf..7fde92315c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -333,7 +338,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +349,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -365,8 +371,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -379,10 +393,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -439,8 +459,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +473,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +486,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +563,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +626,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,9 +658,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -638,6 +746,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +770,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -700,8 +815,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +836,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +870,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +900,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -965,7 +1090,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1177,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1109,7 +1234,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1135,6 +1260,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1144,9 +1270,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1287,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1204,9 +1332,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1344,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1358,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1372,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1412,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1435,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1452,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1473,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1519,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1550,7 +1689,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1561,9 +1702,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1723,12 +1868,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1889,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1956,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1986,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2034,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2226,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2246,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2254,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2264,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,9 +2281,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2140,9 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2221,11 +2389,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2421,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2443,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2459,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2508,27 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2560,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2644,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2711,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2774,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,33 +2789,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2654,7 +2849,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,7 +2901,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2916,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,10 +2937,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2795,7 +2983,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2858,7 +3046,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2880,10 +3069,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2897,14 +3089,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3001,7 +3194,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3076,7 +3269,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3089,7 +3282,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3107,12 +3300,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3200,7 +3395,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3431,288 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index e13c8f1914..e1b736c0c0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3070,8 +3070,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3088,8 +3088,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3113,9 +3114,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index d2e241c983..e715d5135b 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -955,18 +955,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1476,7 +1477,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1497,13 +1498,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd68942af0..d519b97dbf 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4483,7 +4487,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4497,7 +4501,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6117,7 +6121,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6184,7 +6189,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6215,7 +6220,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6231,7 +6236,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6302,7 +6307,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6311,7 +6317,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6337,7 +6345,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6347,7 +6356,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6697,7 +6708,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6760,7 +6772,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index d168620665..9f0fa42045 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 05f44faf6e..f12f7fa83b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -626,6 +626,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 37a7af8c66..b7eaeb6163 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1000,6 +1000,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..31e5ed2c10 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0a5632699d..988d61b0f2 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -98,6 +98,10 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK: Used to store eclass_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+	root->simple_rel_array[0]->eclass_member_indexes = NULL;
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -219,6 +223,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -648,6 +655,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -834,6 +844,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 2d1d8f4bcd..3aeccd3b4a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -306,6 +306,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -926,6 +935,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1305,6 +1332,39 @@ typedef struct StatisticExtInfo
  * the included values might be all NULL rather than all the same non-null
  * values.  See src/backend/optimizer/README for more on that point.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1322,9 +1382,18 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b38627efd..b90c9da573 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
-- 
2.35.3.windows.1

v12-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchapplication/octet-stream; name=v12-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From 80e1aa62c5217eef9cd4c6b6a7c7ef7a5367eac1 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v12 2/4] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 98660524ad..b13b4c378f 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.35.3.windows.1

v12-0003-Add-iterators-for-looping-over-EquivalenceMember.patchapplication/octet-stream; name=v12-0003-Add-iterators-for-looping-over-EquivalenceMember.patchDownload
From 673bf79e9582868c30759d07d473c33ab44ec023 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sat, 3 Dec 2022 18:02:28 +1300
Subject: [PATCH v12 3/4] Add iterators for looping over EquivalenceMembers

Here we introduce EquivalenceMemberIterator which can be used to search
for EquivalenceMembers matching a given search criteria.  This allows us
to switch between a linear search over all members in an EquivalenceClass
and a bitmap index search over the members.  The latter of these is used
when the number of members in the EquivalenceClass reaches a given
threshold, over which we assume that the index search will be faster.
Currently that threshold is set to 16.  This value may need to be refined
further.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +--
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++++++++++-----
 src/include/optimizer/paths.h           |  39 +++
 3 files changed, 307 insertions(+), 80 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f389f70c16..b85c4a071e 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7681,15 +7681,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	Bitmapset *matching_ems;
-	int		i = -1;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_next(&iter)) != 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.
@@ -7698,10 +7696,12 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
 		if (bms_is_subset(em->em_relids, rel->relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7728,9 +7728,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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
 		Relids		expr_relids;
-		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7746,14 +7746,12 @@ 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);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = list_nth_node(EquivalenceMember,
-												  root->eq_members, j);
 			Expr	   *em_expr;
 
 			/* don't expect constants */
@@ -7772,11 +7770,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
 		i++;
 		bms_free(expr_relids);
-		bms_free(matching_ems);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fde92315c..bd9493f659 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -754,8 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
-		int			i;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -770,15 +770,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);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -798,6 +794,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -905,22 +903,20 @@ find_ec_member_matching_expr(PlannerInfo *root,
 							 Expr *expr,
 							 Relids relids)
 {
-	Bitmapset  *matching_ems;
+	EquivalenceMemberIterator iter;
 	Relids		expr_relids;
-	int			i;
+	EquivalenceMember *em;
 
 	/* 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);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = list_nth_node(EquivalenceMember,
-											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -945,10 +941,13 @@ find_ec_member_matching_expr(PlannerInfo *root,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -1685,13 +1684,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	Bitmapset  *matching_ems;
 	ListCell   *lc1;
-	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1702,14 +1701,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);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((cur_em = eclass_member_iterator_next(&iter)) != 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
@@ -1726,6 +1721,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1823,7 +1820,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2264,7 +2261,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
 		int			i;
 
@@ -2281,15 +2279,12 @@ 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)
-		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2297,6 +2292,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2309,11 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		i = -1;
 		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2774,8 +2771,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2789,16 +2786,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2857,6 +2853,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2901,8 +2898,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2916,16 +2913,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2988,6 +2984,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 									 true, cur_em->em_datatype);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -3046,7 +3043,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 iter;
 		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
@@ -3069,19 +3066,19 @@ 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)
-		{
-			cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -3542,6 +3539,195 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	return matching_ems;
 }
 
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
 /*
  * get_ec_source_indexes
  *		Returns a Bitmapset with indexes into root->eq_sources for all
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b90c9da573..df555a87fc 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ *
+ * Callers should assume all of the fields within this struct are internal.
+ *
+ * XXX where should this struct go? Not here.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members;	/* include members with empty em_relids */
+	int			current_index;	/* current iterator position, or -1. */
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;		/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -198,6 +223,20 @@ extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  Relids relids,
 											  bool with_children,
 											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
 										Relids relids);
-- 
2.35.3.windows.1

v12-0004-Add-a-validation-to-check-that-two-iteration-res.patchapplication/octet-stream; name=v12-0004-Add-a-validation-to-check-that-two-iteration-res.patchDownload
From 2de0de6020be4d757db2ed10ff4f57f77fbd5520 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 27 Jan 2023 11:26:13 +0900
Subject: [PATCH v12 4/4] Add a validation to check that two iteration results
 are the same

---
 src/backend/optimizer/path/equivclass.c | 92 +++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index bd9493f659..561f50cafa 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3550,6 +3550,68 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
  */
 #define ECMEMBER_INDEX_THRESHOLD 16
 
+#ifdef USE_ASSERT_CHECKING
+static int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void   *v1 = lfirst(p1);
+	void   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
+/*
+ * validate_eclass_member_iterator
+ *		Assert that the results of the iterations are the same with and without
+ *		indexes. The function iterates in two methods and asserts that the
+ *		results are equal.
+ */
+static void
+validate_eclass_member_iterator(const EquivalenceMemberIterator *iter,
+								EquivalenceMember *(*next)(EquivalenceMemberIterator *iter))
+{
+	/* First, we create two copies of the given iterator */
+	EquivalenceMemberIterator	with_index = *iter;
+	EquivalenceMemberIterator	without_index = *iter;
+
+	/* The results of the two iterations */
+	List					   *result_with_index = NIL;
+	List					   *result_without_index = NIL;
+
+	EquivalenceMember		   *em;
+	ListCell				   *lc1;
+	ListCell				   *lc2;
+
+	/* Set flags */
+	with_index.use_index = true;
+	without_index.use_index = false;
+
+	/* Get the result with index */
+	while ((em = next(&with_index)) != NULL)
+		result_with_index = lappend(result_with_index, em);
+
+	/* Get the result without index */
+	while ((em = next(&without_index)) != NULL)
+		result_without_index = lappend(result_without_index, em);
+
+	/* Sort the two lists to ignore the iteration order */
+	list_sort(result_with_index, list_ptr_cmp);
+	list_sort(result_without_index, list_ptr_cmp);
+
+	/* Validate */
+	Assert(list_length(result_with_index) == list_length(result_without_index));
+
+	forboth(lc1, result_with_index, lc2, result_without_index)
+	{
+		Assert(lfirst(lc1) == lfirst(lc2));
+	}
+}
+#endif
+
 /*
  * setup_eclass_member_iterator
  *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
@@ -3572,14 +3634,29 @@ setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
 	iter->root = root;
 	iter->eclass = ec;
 
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * When assert checking is enabled, we always create indexes to check the
+	 * result of the iteration in validate_eclass_member_iterator().
+	 */
+	iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+											  with_children,
+											  with_norel_members);
+#else
 	if (iter->use_index)
 		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
 												  with_children,
 												  with_norel_members);
 	else
 		iter->matching_ems = NULL;
+#endif
 
 	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+	/* Assert that the results are the same with and without indexes */
+	validate_eclass_member_iterator(iter, eclass_member_iterator_next);
+#endif
 }
 
 /*
@@ -3605,14 +3682,29 @@ setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
 	iter->root = root;
 	iter->eclass = ec;
 
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * When assert checking is enabled, we always create indexes to check the
+	 * result of the iteration in validate_eclass_member_iterator().
+	 */
+	iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+													 with_children,
+													 with_norel_members);
+#else
 	if (iter->use_index)
 		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
 														 with_children,
 														 with_norel_members);
 	else
 		iter->matching_ems = NULL;
+#endif
 
 	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+	/* Assert that the results are the same with and without indexes */
+	validate_eclass_member_iterator(iter, eclass_member_iterator_strict_next);
+#endif
 }
 
 /*
-- 
2.35.3.windows.1

#36Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#35)
7 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Fri, Jan 27, 2023 at 12:48 PM Yuya Watari <watari.yuya@gmail.com> wrote:

However, there is more bad news. Unfortunately, some regression tests
are failing in my environment. I'm not sure why, but it could be that
a) my sanity checking code (v12-0004) is wrong, or b) our patches have
some bugs.

I will investigate this issue further, and share the results when found.

I have investigated this issue and concluded that b) our patches have
some bugs. I have attached the modified patches to this email. This
version passed regression tests in my environment.

1. v13-0005

The first bug is in eclass_member_iterator_strict_next(). As I
mentioned in the commit message, the original code incorrectly missed
EquivalenceMembers with empty em_relids when 'with_norel_members' is
true.

I show my changes as follows:

===
- if (!iter->with_children && em->em_is_child)
- continue;

- if (!iter->with_norel_members && bms_is_empty(em->em_relids))
- continue;

- if (!bms_is_subset(iter->with_relids, em->em_relids))
- continue;

-    iter->current_index = foreach_current_index(lc);
+    if ((iter->with_norel_members && bms_is_empty(em->em_relids))
+        || (bms_is_subset(iter->with_relids, em->em_relids)
+            && (iter->with_children || !em->em_is_child)))
+    {
+        iter->current_index = foreach_current_index(lc);
===

EquivalenceMembers with empty em_relids will pass the second 'if'
condition when 'with_norel_members' is true. These members should be
returned. However, since the empty em_relids can never be superset of
any non-empty relids, the EMs may fail the last condition. Therefore,
the original code missed some members.

2. v13-0006

The second bug exists in get_ecmember_indexes_strict(). As I described
in the comment, if the empty relids is given, this function must
return all members because their em_relids are always superset. I am
concerned that this change may adversely affect performance.
Currently, I have not seen any degradation.

3. v13-0007

The last one is in add_eq_member(). I am not sure why this change is
working, but it is probably related to the concerns David mentioned in
the previous mail. The v13-0007 may be wrong, so it should be
reconsidered.

--
Best regards,
Yuya Watari

Attachments:

v13-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchapplication/octet-stream; name=v13-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From 2c91840542fb9f447e1b15ca4999c8c9c3e83c47 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v13 1/7] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |  42 +-
 src/backend/nodes/outfuncs.c              |   7 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 796 +++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c     |  22 +-
 src/backend/optimizer/path/pathkeys.c     |  26 +-
 src/backend/optimizer/plan/createplan.c   |  66 +-
 src/backend/optimizer/plan/planagg.c      |   1 +
 src/backend/optimizer/plan/planner.c      |   3 +
 src/backend/optimizer/prep/prepjointree.c |   3 +
 src/backend/optimizer/prep/prepunion.c    |   1 +
 src/backend/optimizer/util/relnode.c      |  13 +
 src/include/nodes/pathnodes.h             |  75 +-
 src/include/optimizer/paths.h             |  28 +-
 14 files changed, 860 insertions(+), 226 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 50d23f922c..f389f70c16 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7619,6 +7619,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7677,18 +7681,22 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Bitmapset *matching_ems;
+	int		i = -1;
+
+	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
 
-	foreach(lc, ec->ec_members)
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		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.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
@@ -7720,7 +7728,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		Relids		expr_relids;
+		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7735,19 +7745,22 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		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,
+												   false, false);
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7761,8 +7774,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (is_foreign_expr(root, rel, em->em_expr))
 				return em;
 		}
-
 		i++;
+		bms_free(expr_relids);
+		bms_free(matching_ems);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6b368b08b2..98becac509 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 29ae32d960..595aaf74f3 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5501,7 +5501,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7d7e6facdf..7fde92315c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -333,7 +338,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +349,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -365,8 +371,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -379,10 +393,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -439,8 +459,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +473,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +486,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +563,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int source_idx = list_length(root->eq_sources);
+	int i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int derive_idx = list_length(root->eq_derives);
+	int i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids				expr_relids;
+	int					em_index = list_length(root->eq_members);
+	int					i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +626,20 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,9 +658,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -638,6 +746,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		Bitmapset		 *matching_ems;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,9 +770,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec, expr_relids,
+												   true, true);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 
 			/*
 			 * Ignore child members unless they match the request.
@@ -700,8 +815,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +836,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +870,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +900,27 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	Relids		expr_relids;
+	int			i;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	matching_ems = get_ecmember_indexes_strict(root, ec, expr_relids, true, true);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -965,7 +1090,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1177,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1109,7 +1234,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1135,6 +1260,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int		i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1144,9 +1270,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1287,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1204,9 +1332,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1344,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1358,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset		   *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1372,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1412,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1435,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1452,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1473,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int		i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1519,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1550,7 +1689,9 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
+	Bitmapset  *matching_ems;
 	ListCell   *lc1;
+	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1561,9 +1702,13 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	matching_ems = get_ecmember_indexes(root, ec, join_relids, true, false);
+
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1723,12 +1868,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1889,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1956,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset	 *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1970,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1986,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2034,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2226,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2246,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2254,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2264,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		Bitmapset		 *matching_ems;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,9 +2281,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		matching_ems = get_ecmember_indexes_strict(root, cur_ec,
+												   outer_relids,
+												   false, true);
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			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))
@@ -2140,9 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2221,11 +2389,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset		  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2421,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2443,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2459,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2508,27 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2560,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2644,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2711,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int		i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2774,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,33 +2789,21 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2654,7 +2849,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 														  child_relids);
 				}
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 
@@ -2706,7 +2901,8 @@ 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);
-		int			num_members;
+		Bitmapset		 *matching_ems;
+		int			j;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2916,19 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping over matching_ems means we only loop over existing members,
+		 * not any newly added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		{
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,10 +2937,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
-			{
-				/* Yes, generate transformed child version */
+			{	/* XXX fix indentation */
 				Expr	   *child_expr;
 				Relids		new_relids;
 				Relids		new_nullable_relids;
@@ -2795,7 +2983,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 													   child_joinrel,
 													   child_joinrel->top_parent);
 
-				(void) add_eq_member(cur_ec, child_expr,
+				(void) add_eq_member(root, cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
 									 true, cur_em->em_datatype);
 			}
@@ -2858,7 +3046,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		Bitmapset		  *matching_ems;
+		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2880,10 +3069,13 @@ 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;
-		foreach(lc2, cur_ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			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;
@@ -2897,14 +3089,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3001,7 +3194,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3076,7 +3269,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3089,7 +3282,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3107,12 +3300,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+			list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3200,7 +3395,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3431,288 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo	*rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index e13c8f1914..e1b736c0c0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3070,8 +3070,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3088,8 +3088,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3113,9 +3114,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index d2e241c983..e715d5135b 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -955,18 +955,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1476,7 +1477,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1497,13 +1498,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd68942af0..d519b97dbf 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4483,7 +4487,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4497,7 +4501,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6117,7 +6121,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6184,7 +6189,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6215,7 +6220,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6231,7 +6236,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6302,7 +6307,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6311,7 +6317,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6337,7 +6345,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6347,7 +6356,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6697,7 +6708,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6760,7 +6772,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index d168620665..9f0fa42045 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 05f44faf6e..f12f7fa83b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -626,6 +626,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 37a7af8c66..b7eaeb6163 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1000,6 +1000,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..31e5ed2c10 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0a5632699d..988d61b0f2 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -98,6 +98,10 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	/* HACK: Used to store eclass_member_indexes for varno=0 Exprs */
+	root->simple_rel_array[0] = makeNode(RelOptInfo);
+	root->simple_rel_array[0]->eclass_member_indexes = NULL;
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -219,6 +223,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -648,6 +655,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -834,6 +844,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 2d1d8f4bcd..3aeccd3b4a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -306,6 +306,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -926,6 +935,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1305,6 +1332,39 @@ typedef struct StatisticExtInfo
  * the included values might be all NULL rather than all the same non-null
  * values.  See src/backend/optimizer/README for more on that point.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1322,9 +1382,18 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes; /* Indexes into all PlannerInfos eq_members
+									* for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes; /* Indexes into PlannerInfo's eq_members
+									  * with em_is_child == false */
+	Bitmapset  *ec_norel_indexes; /* Indexes into PlannerInfo's eq_members for
+								   * members where pull_varno on the em_expr
+								   * is an empty set */
+	Bitmapset  *ec_source_indexes; /* indexes into PlannerInfo's eq_sources
+									* list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes; /* indexes into PlannerInfo's eq_derives
+									* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b38627efd..b90c9da573 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -135,7 +135,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +161,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +188,28 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids,
+										   bool with_children,
+										   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
 
 /*
  * pathkeys.c
-- 
2.35.3.windows.1

v13-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchapplication/octet-stream; name=v13-0002-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From 61a63fe9bc3ef7d0b915443764a5f2a7455ceafe Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v13 2/7] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 98660524ad..b13b4c378f 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.35.3.windows.1

v13-0003-Add-iterators-for-looping-over-EquivalenceMember.patchapplication/octet-stream; name=v13-0003-Add-iterators-for-looping-over-EquivalenceMember.patchDownload
From 4512783936a0b94dfe85c6d102f824b473a205e8 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sat, 3 Dec 2022 18:02:28 +1300
Subject: [PATCH v13 3/7] Add iterators for looping over EquivalenceMembers

Here we introduce EquivalenceMemberIterator which can be used to search
for EquivalenceMembers matching a given search criteria.  This allows us
to switch between a linear search over all members in an EquivalenceClass
and a bitmap index search over the members.  The latter of these is used
when the number of members in the EquivalenceClass reaches a given
threshold, over which we assume that the index search will be faster.
Currently that threshold is set to 16.  This value may need to be refined
further.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +--
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++++++++++-----
 src/include/optimizer/paths.h           |  39 +++
 3 files changed, 307 insertions(+), 80 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f389f70c16..b85c4a071e 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7681,15 +7681,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	Bitmapset *matching_ems;
-	int		i = -1;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	matching_ems = get_ecmember_indexes(root, ec, rel->relids, true, false);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_next(&iter)) != 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.
@@ -7698,10 +7696,12 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
 		if (bms_is_subset(em->em_relids, rel->relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7728,9 +7728,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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
 		Relids		expr_relids;
-		int			j;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7746,14 +7746,12 @@ 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);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = list_nth_node(EquivalenceMember,
-												  root->eq_members, j);
 			Expr	   *em_expr;
 
 			/* don't expect constants */
@@ -7772,11 +7770,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
 		i++;
 		bms_free(expr_relids);
-		bms_free(matching_ems);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fde92315c..bd9493f659 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -754,8 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
-		int			i;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -770,15 +770,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);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
-		i = -1;
-		while ((i = bms_next_member(matching_ems, i)) >= 0)
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -798,6 +794,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -905,22 +903,20 @@ find_ec_member_matching_expr(PlannerInfo *root,
 							 Expr *expr,
 							 Relids relids)
 {
-	Bitmapset  *matching_ems;
+	EquivalenceMemberIterator iter;
 	Relids		expr_relids;
-	int			i;
+	EquivalenceMember *em;
 
 	/* 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);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = list_nth_node(EquivalenceMember,
-											  root->eq_members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -945,10 +941,13 @@ find_ec_member_matching_expr(PlannerInfo *root,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -1685,13 +1684,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	Bitmapset  *matching_ems;
 	ListCell   *lc1;
-	int			i;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1702,14 +1701,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);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
-	i = -1;
-	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	while ((cur_em = eclass_member_iterator_next(&iter)) != 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
@@ -1726,6 +1721,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1823,7 +1820,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2264,7 +2261,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		Bitmapset		 *matching_ems;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
 		int			i;
 
@@ -2281,15 +2279,12 @@ 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)
-		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2297,6 +2292,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2309,11 +2306,11 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		i = -1;
 		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
-													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2774,8 +2771,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2789,16 +2786,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2857,6 +2853,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2901,8 +2898,8 @@ 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;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2916,16 +2913,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * Looping over matching_ems means we only loop over existing members,
-		 * not any newly added ones.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		matching_ems = get_ecmember_indexes(root, cur_ec, top_parent_relids, false, false);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-		j = -1;
-		while ((j = bms_next_member(matching_ems, j)) >= 0)
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
-
 			Assert(!cur_em->em_is_const);
 			Assert(!cur_em->em_is_child);
 			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
@@ -2988,6 +2984,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 									 true, cur_em->em_datatype);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -3046,7 +3043,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 iter;
 		int					j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
@@ -3069,19 +3066,19 @@ 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)
-		{
-			cur_em = list_nth_node(EquivalenceMember, root->eq_members, j);
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -3542,6 +3539,195 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 	return matching_ems;
 }
 
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+	if (iter->use_index)
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
+				continue;
+
+			if (!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
 /*
  * get_ec_source_indexes
  *		Returns a Bitmapset with indexes into root->eq_sources for all
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b90c9da573..df555a87fc 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ *
+ * Callers should assume all of the fields within this struct are internal.
+ *
+ * XXX where should this struct go? Not here.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members;	/* include members with empty em_relids */
+	int			current_index;	/* current iterator position, or -1. */
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;		/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -198,6 +223,20 @@ extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
 											  Relids relids,
 											  bool with_children,
 											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
 extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
 										EquivalenceClass *ec,
 										Relids relids);
-- 
2.35.3.windows.1

v13-0004-Add-a-validation-to-check-that-two-iteration-res.patchapplication/octet-stream; name=v13-0004-Add-a-validation-to-check-that-two-iteration-res.patchDownload
From 40fe5da785c3fe11ba561d27873151ef0b0737f6 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 27 Jan 2023 11:26:13 +0900
Subject: [PATCH v13 4/7] Add a validation to check that two iteration results
 are the same

---
 src/backend/optimizer/path/equivclass.c | 92 +++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index bd9493f659..561f50cafa 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3550,6 +3550,68 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
  */
 #define ECMEMBER_INDEX_THRESHOLD 16
 
+#ifdef USE_ASSERT_CHECKING
+static int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void   *v1 = lfirst(p1);
+	void   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
+/*
+ * validate_eclass_member_iterator
+ *		Assert that the results of the iterations are the same with and without
+ *		indexes. The function iterates in two methods and asserts that the
+ *		results are equal.
+ */
+static void
+validate_eclass_member_iterator(const EquivalenceMemberIterator *iter,
+								EquivalenceMember *(*next)(EquivalenceMemberIterator *iter))
+{
+	/* First, we create two copies of the given iterator */
+	EquivalenceMemberIterator	with_index = *iter;
+	EquivalenceMemberIterator	without_index = *iter;
+
+	/* The results of the two iterations */
+	List					   *result_with_index = NIL;
+	List					   *result_without_index = NIL;
+
+	EquivalenceMember		   *em;
+	ListCell				   *lc1;
+	ListCell				   *lc2;
+
+	/* Set flags */
+	with_index.use_index = true;
+	without_index.use_index = false;
+
+	/* Get the result with index */
+	while ((em = next(&with_index)) != NULL)
+		result_with_index = lappend(result_with_index, em);
+
+	/* Get the result without index */
+	while ((em = next(&without_index)) != NULL)
+		result_without_index = lappend(result_without_index, em);
+
+	/* Sort the two lists to ignore the iteration order */
+	list_sort(result_with_index, list_ptr_cmp);
+	list_sort(result_without_index, list_ptr_cmp);
+
+	/* Validate */
+	Assert(list_length(result_with_index) == list_length(result_without_index));
+
+	forboth(lc1, result_with_index, lc2, result_without_index)
+	{
+		Assert(lfirst(lc1) == lfirst(lc2));
+	}
+}
+#endif
+
 /*
  * setup_eclass_member_iterator
  *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
@@ -3572,14 +3634,29 @@ setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
 	iter->root = root;
 	iter->eclass = ec;
 
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * When assert checking is enabled, we always create indexes to check the
+	 * result of the iteration in validate_eclass_member_iterator().
+	 */
+	iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+											  with_children,
+											  with_norel_members);
+#else
 	if (iter->use_index)
 		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
 												  with_children,
 												  with_norel_members);
 	else
 		iter->matching_ems = NULL;
+#endif
 
 	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+	/* Assert that the results are the same with and without indexes */
+	validate_eclass_member_iterator(iter, eclass_member_iterator_next);
+#endif
 }
 
 /*
@@ -3605,14 +3682,29 @@ setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
 	iter->root = root;
 	iter->eclass = ec;
 
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * When assert checking is enabled, we always create indexes to check the
+	 * result of the iteration in validate_eclass_member_iterator().
+	 */
+	iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+													 with_children,
+													 with_norel_members);
+#else
 	if (iter->use_index)
 		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
 														 with_children,
 														 with_norel_members);
 	else
 		iter->matching_ems = NULL;
+#endif
 
 	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+	/* Assert that the results are the same with and without indexes */
+	validate_eclass_member_iterator(iter, eclass_member_iterator_strict_next);
+#endif
 }
 
 /*
-- 
2.35.3.windows.1

v13-0005-Fix-incorrect-filtering-condition-in-iteration.patchapplication/octet-stream; name=v13-0005-Fix-incorrect-filtering-condition-in-iteration.patchDownload
From 401c2ddf9ce72cccbe39e908737d68a6704c7926 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 30 Jan 2023 09:46:56 +0900
Subject: [PATCH v13 5/7] Fix incorrect filtering condition in iteration

The original code incorrectly missed EquivalenceMembers with empty
em_relids when 'with_norel_members' is true in
eclass_member_iterator_strict_next(). Such an EM passes the second if
condition, but fails the last condition because it cannot be superset of
non-empty relids.
---
 src/backend/optimizer/path/equivclass.c | 68 +++++++++++--------------
 1 file changed, 30 insertions(+), 38 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 561f50cafa..83ea0313e1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3733,26 +3733,22 @@ eclass_member_iterator_next(EquivalenceMemberIterator *iter)
 		{
 			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
 
-			if (!iter->with_children && em->em_is_child)
-				continue;
-
-			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
-				continue;
-
-			if (!bms_overlap(em->em_relids, iter->with_relids))
-				continue;
-
-			iter->current_index = foreach_current_index(lc);
+			if ((iter->with_norel_members && bms_is_empty(em->em_relids))
+				|| (bms_overlap(em->em_relids, iter->with_relids)
+					&& (iter->with_children || !em->em_is_child)))
+			{
+				iter->current_index = foreach_current_index(lc);
 
-			/*
-			 * Some users of this iterator will be adding new
-			 * EquivalenceMember during the loop.  We must ensure we don't
-			 * return those, so here we return NULL when the loop index goes
-			 * beyond the original length of the ec_members list.
-			 */
-			if (iter->current_index >= iter->orig_length)
-				return NULL;
-			return em;
+				/*
+				 * Some users of this iterator will be adding new
+				 * EquivalenceMember during the loop.  We must ensure we don't
+				 * return those, so here we return NULL when the loop index goes
+				 * beyond the original length of the ec_members list.
+				 */
+				if (iter->current_index >= iter->orig_length)
+					return NULL;
+				return em;
+			}
 		}
 		return NULL;
 	}
@@ -3784,26 +3780,22 @@ eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
 		{
 			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
 
-			if (!iter->with_children && em->em_is_child)
-				continue;
-
-			if (!iter->with_norel_members && bms_is_empty(em->em_relids))
-				continue;
-
-			if (!bms_is_subset(iter->with_relids, em->em_relids))
-				continue;
-
-			iter->current_index = foreach_current_index(lc);
+			if ((iter->with_norel_members && bms_is_empty(em->em_relids))
+				|| (bms_is_subset(iter->with_relids, em->em_relids)
+					&& (iter->with_children || !em->em_is_child)))
+			{
+				iter->current_index = foreach_current_index(lc);
 
-			/*
-			 * Some users of this iterator will be adding new
-			 * EquivalenceMember during the loop.  We must ensure we don't
-			 * return those, so here we return NULL when the loop index goes
-			 * beyond the original length of the ec_members list.
-			 */
-			if (iter->current_index >= iter->orig_length)
-				return NULL;
-			return em;
+				/*
+				 * Some users of this iterator will be adding new
+				 * EquivalenceMember during the loop.  We must ensure we don't
+				 * return those, so here we return NULL when the loop index goes
+				 * beyond the original length of the ec_members list.
+				 */
+				if (iter->current_index >= iter->orig_length)
+					return NULL;
+				return em;
+			}
 		}
 		return NULL;
 	}
-- 
2.35.3.windows.1

v13-0006-Fix-a-bug-with-get_ecmember_indexes_strict-incor.patchapplication/octet-stream; name=v13-0006-Fix-a-bug-with-get_ecmember_indexes_strict-incor.patchDownload
From b90f94a7d53670fa8334a8b12b05c163e56a3b1e Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 30 Jan 2023 14:48:07 +0900
Subject: [PATCH v13 6/7] Fix a bug with get_ecmember_indexes_strict()
 incorrectly handling empty relids

---
 src/backend/optimizer/path/equivclass.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 83ea0313e1..6e53f1887a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3519,6 +3519,17 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 										   rel->eclass_member_indexes);
 		}
 	}
+	else
+	{
+		/*
+		 * If 'relids' is empty, the relids of any EquivalenceMember can be a
+		 * superset of 'relids'.
+		 */
+		if (!with_children)
+			matching_ems = bms_copy(ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_copy(ec->ec_member_indexes);
+	}
 
 #ifdef USE_ASSERT_CHECKING
 	/* verify the results look sane */
-- 
2.35.3.windows.1

v13-0007-Fix-a-bug.patchapplication/octet-stream; name=v13-0007-Fix-a-bug.patchDownload
From 65ad02290f493eb933a8ad75cb74525ddc40984b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 30 Jan 2023 14:53:26 +0900
Subject: [PATCH v13 7/7] Fix a bug?

I am not sure why this change is working
---
 src/backend/optimizer/path/equivclass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 6e53f1887a..0e6fb2161a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -669,7 +669,7 @@ 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))
+	if (bms_is_empty(relids))
 		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
 
 	if (is_child)
-- 
2.35.3.windows.1

#37David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#36)
2 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

isOn Mon, 30 Jan 2023 at 23:03, Yuya Watari <watari.yuya@gmail.com> wrote:

1. v13-0005

The first bug is in eclass_member_iterator_strict_next(). As I
mentioned in the commit message, the original code incorrectly missed
EquivalenceMembers with empty em_relids when 'with_norel_members' is
true.

Yeah, I was also looking at this today and found the same issues after
adding the verification code that checks we get the same members from
the index and via the looking method. I ended up making some changes
slightly different from what you had but wasn't quite ready to post
them yet.

I'm still a little unhappy with master's comments for the
EquivalenceMember.em_relids field. It claims to be the relids for the
em_expr, but that's not the case for em_is_child members. I've ended
up adding an additional field named em_norel_expr that gets set to
true when em_expr truly contains no Vars. I then adjusted the
conditions in the iterator's loops to properly include members with no
Vars when we ask for those.

2. v13-0006

The second bug exists in get_ecmember_indexes_strict(). As I described
in the comment, if the empty relids is given, this function must
return all members because their em_relids are always superset. I am
concerned that this change may adversely affect performance.
Currently, I have not seen any degradation.

I fixed this by adding a new field to the iterator struct named
relids_empty. It's just set to bms_is_empty(iter->with_relids). The
loop condition then just becomes:

if (iter->relids_empty ||
!bms_is_subset(iter->with_relids, em->em_relids))
continue;

3. v13-0007

The last one is in add_eq_member(). I am not sure why this change is
working, but it is probably related to the concerns David mentioned in
the previous mail. The v13-0007 may be wrong, so it should be
reconsidered.

Unfortunately, we can't fix it that way. At a glance, what you have
would only find var-less child members if you requested that the
iterator also gave you with_norel_members==true. I've not looked,
perhaps all current code locations request with_norel_members, so your
change likely just words by accident.

I've attached what I worked on today. I still want to do more
cross-checking to make sure all code locations which use these new
iterators get the same members as they used to get.

In the attached I also changed the code that added a RelOptInfo to
root->simple_rel_array[0] to allow the varno=0 Vars made in
generate_append_tlist() to be indexed. That's now done via a new
function (setup_append_rel_entry()) which is only called during
plan_set_operations(). This means we're no longer wastefully creating
that entry during the planning of normal queries. We could maybe
consider giving this a more valid varno and expand simple_rel_array to
make more room, but I'm not sure it's worth it or not. I'm happier
that this simple_rel_array[0] entry now only exists when planning set
operations, but I'd probably feel better if there was some other way
that felt less like we're faking up a RelOptInfo to store
EquivalenceMembers in.

I've also included a slightly edited version of your code which checks
that the members match when using and not using the new indexes. All
the cross-checking seems to pass.

David

Attachments:

v15-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchtext/plain; charset=US-ASCII; name=v15-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From 177d013928d2bb5c0c4ee565a433bbf62481b18c Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v15 1/2] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 98660524ad..b13b4c378f 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.39.0.windows.1

v15-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchtext/plain; charset=US-ASCII; name=v15-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From ba89effb12c3547bf843bd0e991b454388cfb88e Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v15 2/2] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |   50 +-
 src/backend/nodes/list.c                  |   16 +
 src/backend/nodes/outfuncs.c              |    7 +-
 src/backend/optimizer/path/costsize.c     |    3 +-
 src/backend/optimizer/path/equivclass.c   | 1295 +++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |   22 +-
 src/backend/optimizer/path/pathkeys.c     |   26 +-
 src/backend/optimizer/plan/createplan.c   |   66 +-
 src/backend/optimizer/plan/planagg.c      |    1 +
 src/backend/optimizer/plan/planner.c      |    3 +
 src/backend/optimizer/prep/prepjointree.c |    3 +
 src/backend/optimizer/prep/prepunion.c    |   25 +-
 src/backend/optimizer/util/relnode.c      |    9 +
 src/include/nodes/pathnodes.h             |   81 +-
 src/include/nodes/pg_list.h               |    1 +
 src/include/optimizer/paths.h             |   67 +-
 src/tools/pgindent/typedefs.list          |    1 +
 17 files changed, 1347 insertions(+), 329 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 50d23f922c..b85c4a071e 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7619,6 +7619,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7677,23 +7681,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
-	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
+	while ((em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7720,7 +7728,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
+		Relids		expr_relids;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7735,19 +7745,20 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
+		expr_relids = pull_varnos(root, (Node *) expr);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7759,10 +7770,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
-
 		i++;
+		bms_free(expr_relids);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index a709d23ef1..b8972bbe5a 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1683,6 +1683,22 @@ list_sort(List *list, list_sort_comparator cmp)
 		qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp);
 }
 
+/*
+ * list_sort comparator for sorting a list into ascending ptr order.
+ */
+int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void	   *v1 = lfirst(p1);
+	void	   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
 /*
  * list_sort comparator for sorting a list into ascending int order.
  */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6b368b08b2..98becac509 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 29ae32d960..595aaf74f3 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5501,7 +5501,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7d7e6facdf..4277548193 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,9 +31,14 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										Relids nullable_relids,
 										bool is_child, Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -333,7 +338,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			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,
 									   restrictinfo->security_level);
@@ -345,6 +349,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -365,8 +371,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -379,10 +393,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		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,
 								   restrictinfo->security_level);
@@ -394,13 +410,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
-							false, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 		ec1->ec_below_outer_join |= below_outer_join;
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,13 +429,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
-							false, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
 		ec2->ec_below_outer_join |= below_outer_join;
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -430,6 +448,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -439,8 +459,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -450,10 +473,10 @@ 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, item1, item1_relids,
+							item1_nullable_relids, false, item1_type);
+		em2 = add_eq_member(root, ec, item2, item2_relids,
+							item2_nullable_relids, false, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -463,6 +486,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -538,14 +563,62 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 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, Expr *expr,
+			  Relids relids, Relids nullable_relids, bool is_child,
+			  Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids		expr_relids;
+	int			em_index = list_length(root->eq_members);
+	int			i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -554,6 +627,23 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_is_child = is_child;
 	em->em_datatype = datatype;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (is_child)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
+	/* record the actual relids from 'expr' */
+	em->em_norel_expr = bms_is_empty(expr_relids);
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -572,9 +662,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!is_child)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (is_child)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -638,6 +750,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Scan through the existing EquivalenceClasses for a match
@@ -645,7 +758,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -660,10 +774,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -683,6 +798,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -700,8 +817,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -718,10 +838,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	/*
 	 * Get the precise set of nullable relids appearing in the expression.
 	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
 	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  nullable_relids, false, opcintype);
 
 	/*
@@ -753,7 +872,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -783,19 +902,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	Relids		expr_relids;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
+
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -820,10 +945,13 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -965,7 +1093,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1052,7 +1180,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1109,7 +1237,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1135,6 +1263,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1144,9 +1273,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
 		{
@@ -1161,9 +1290,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1204,9 +1335,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1216,7 +1347,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1229,7 +1361,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1242,9 +1375,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1279,7 +1415,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1302,11 +1438,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1315,6 +1455,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1335,11 +1476,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1380,11 +1522,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1546,6 +1688,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
@@ -1561,10 +1705,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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
+	while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1581,6 +1725,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1678,7 +1824,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -1723,12 +1869,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1740,12 +1890,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1807,9 +1957,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1820,9 +1971,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1833,9 +1987,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1877,7 +2035,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2069,7 +2227,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
+	Relids		outer_relids,
+				inner_relids,
 				inner_nullable_relids;
 	ListCell   *lc1;
 
@@ -2088,6 +2247,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2255,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 	inner_nullable_relids = bms_intersect(inner_relids,
@@ -2104,8 +2265,10 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2120,10 +2283,12 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2131,6 +2296,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2140,12 +2307,14 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2221,11 +2390,12 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2252,10 +2422,14 @@ 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2270,7 +2444,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2286,9 +2460,11 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 
@@ -2333,11 +2509,28 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2369,21 +2562,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2446,16 +2646,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2508,16 +2713,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2568,7 +2776,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2582,86 +2791,72 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			Expr	   *child_expr;
+			Relids		new_relids;
+			Relids		new_nullable_relids;
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
+
+			if (parent_rel->reloptkind == RELOPT_BASEREL)
+			{
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   1, &appinfo);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_rel,
+													  child_rel->top_parent);
+			}
 
 			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the transformation
+			 * might have substituted a constant, but we don't want the child
+			 * member to be marked as constant.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			/*
+			 * And likewise for nullable_relids.  Note this code assumes
+			 * parent and child relids are singletons.
+			 */
+			new_nullable_relids = cur_em->em_nullable_relids;
+			if (bms_overlap(new_nullable_relids, top_parent_relids))
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-				Relids		new_nullable_relids;
-
-				if (parent_rel->reloptkind == RELOPT_BASEREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   1, &appinfo);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_rel,
-														  child_rel->top_parent);
-				}
-
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
-
-				/*
-				 * And likewise for nullable_relids.  Note this code assumes
-				 * parent and child relids are singletons.
-				 */
-				new_nullable_relids = cur_em->em_nullable_relids;
-				if (bms_overlap(new_nullable_relids, top_parent_relids))
-				{
-					new_nullable_relids = bms_difference(new_nullable_relids,
-														 top_parent_relids);
-					new_nullable_relids = bms_add_members(new_nullable_relids,
-														  child_relids);
-				}
+				new_nullable_relids = bms_difference(new_nullable_relids,
+													 top_parent_relids);
+				new_nullable_relids = bms_add_members(new_nullable_relids,
+													  child_relids);
+			}
 
-				(void) add_eq_member(cur_ec, child_expr,
-									 new_relids, new_nullable_relids,
-									 true, cur_em->em_datatype);
+			(void) add_eq_member(root, cur_ec, child_expr,
+								 new_relids, new_nullable_relids,
+								 true, cur_em->em_datatype);
 
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
-			}
+			/* Record this EC index for the child rel */
+			child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2706,7 +2901,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,24 +2916,22 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
+			Expr	   *child_expr;
+			Relids		new_relids;
+			Relids		new_nullable_relids;
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2746,60 +2940,52 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-				Relids		new_nullable_relids;
-
-				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   nappinfos, appinfos);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_joinrel,
-														  child_joinrel->top_parent);
-				}
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   nappinfos, appinfos);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_joinrel,
+													  child_joinrel->top_parent);
+			}
 
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
+			/*
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the transformation
+			 * might have substituted a constant, but we don't want the child
+			 * member to be marked as constant.
+			 */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-				/*
-				 * For nullable_relids, we must selectively replace parent
-				 * nullable relids with child ones.
-				 */
-				new_nullable_relids = cur_em->em_nullable_relids;
-				if (bms_overlap(new_nullable_relids, top_parent_relids))
-					new_nullable_relids =
-						adjust_child_relids_multilevel(root,
-													   new_nullable_relids,
-													   child_joinrel,
-													   child_joinrel->top_parent);
-
-				(void) add_eq_member(cur_ec, child_expr,
-									 new_relids, new_nullable_relids,
-									 true, cur_em->em_datatype);
-			}
+			/*
+			 * For nullable_relids, we must selectively replace parent
+			 * nullable relids with child ones.
+			 */
+			new_nullable_relids = cur_em->em_nullable_relids;
+			if (bms_overlap(new_nullable_relids, top_parent_relids))
+				new_nullable_relids =
+					adjust_child_relids_multilevel(root,
+												   new_nullable_relids,
+												   child_joinrel,
+												   child_joinrel->top_parent);
+
+			(void) add_eq_member(root, cur_ec, child_expr,
+								 new_relids, new_nullable_relids,
+								 true, cur_em->em_datatype);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -2858,7 +3044,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2880,16 +3067,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -2897,14 +3087,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3001,7 +3192,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3076,7 +3267,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3089,7 +3280,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3107,12 +3298,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+		list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3200,7 +3393,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3236,3 +3429,595 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = false;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = true;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_strict_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_strict_next */
+	Assert(!iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't return members which have no common rels with with_relids
+			 */
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_next */
+	Assert(iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't match members where em_relids that don't contain all rels
+			 * mentioned in with_relids.
+			 */
+			if (iter->relids_empty ||
+				!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index e13c8f1914..e1b736c0c0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3070,8 +3070,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3088,8 +3088,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3113,9 +3114,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index d2e241c983..e715d5135b 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -955,18 +955,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1476,7 +1477,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1497,13 +1498,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cd68942af0..d519b97dbf 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4483,7 +4487,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4497,7 +4501,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6117,7 +6121,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6184,7 +6189,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6215,7 +6220,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6231,7 +6236,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6302,7 +6307,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6311,7 +6317,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6337,7 +6345,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6347,7 +6356,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6697,7 +6708,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6760,7 +6772,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index d168620665..9f0fa42045 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 05f44faf6e..f12f7fa83b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -626,6 +626,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->multiexpr_params = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->all_result_relids =
 		parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 37a7af8c66..b7eaeb6163 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1000,6 +1000,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->multiexpr_params = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->all_result_relids = NULL;
 	subroot->leaf_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..4c7d66311f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -45,7 +45,7 @@
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
 
-
+static void setup_append_rel_entry(PlannerInfo *root);
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  List *colTypes, List *colCollations,
 										  bool junkOK,
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
@@ -149,6 +150,8 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
+	setup_append_rel_entry(root);
+
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -180,6 +183,26 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
+/*
+ * setup_append_rel_entry
+ *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		because generate_append_tlist() makes Vars with varno=0.  In
+ *		add_eq_member() we need to index EquivalenceMembers belonging to this
+ *		relation.
+ */
+static void
+setup_append_rel_entry(PlannerInfo *root)
+{
+	RelOptInfo *rel = makeNode(RelOptInfo);
+
+	memset(rel, 0, sizeof(RelOptInfo));
+	rel->eclass_member_indexes = NULL;
+
+	/* make sure nothing else has made use of this element */
+	Assert(root->simple_rel_array[0] == NULL);
+	root->simple_rel_array[0] = rel;
+}
+
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0a5632699d..ace943c87d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -219,6 +219,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -648,6 +651,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -834,6 +840,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 2d1d8f4bcd..d888cd54d3 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -306,6 +306,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -926,6 +935,24 @@ typedef struct RelOptInfo
 	 * Indexes in PlannerInfo's eq_classes list of ECs that mention this rel
 	 */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1305,6 +1332,39 @@ typedef struct StatisticExtInfo
  * the included values might be all NULL rather than all the same non-null
  * values.  See src/backend/optimizer/README for more on that point.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1322,9 +1382,19 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes;	/* Indexes into all PlannerInfos
+									 * eq_members for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes;	/* Indexes into PlannerInfo's
+										 * eq_members with em_is_child ==
+										 * false */
+	Bitmapset  *ec_norel_indexes;	/* Indexes into PlannerInfo's eq_members
+									 * for members where pull_varno on the
+									 * em_expr is an empty set */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1373,9 +1443,10 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
-	Relids		em_relids;		/* all relids appearing in em_expr */
+	Relids		em_relids;		/* relids for this member */
 	Relids		em_nullable_relids; /* nullable by lower outer joins */
-	bool		em_is_const;	/* expression is pseudoconstant? */
+	bool		em_is_const;	/* is em_relids empty? */
+	bool		em_norel_expr;	/* true if em_expr contains no Vars */
 	bool		em_is_child;	/* derived version for a child relation? */
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
 } EquivalenceMember;
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d28..77f26edf22 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -629,6 +629,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 
+extern int	list_ptr_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b38627efd..4d2221c2d0 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		relids_empty;	/* is with_relids empty? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members; /* include members with empty em_relids */
+#ifdef USE_ASSERT_CHECKING
+	bool		isstrict;		/* ensure the correct next function is used */
+#endif
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	int			current_index;	/* current iterator position, or -1. */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;			/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -135,7 +160,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +186,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +213,42 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+									   EquivalenceClass *ec,
+									   Relids relids,
+									   bool with_children,
+									   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 51484ca7e2..2e9c6c530f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -641,6 +641,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.39.0.windows.1

#38Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#37)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear David,

On Mon, Jan 30, 2023 at 9:14 PM David Rowley <dgrowleyml@gmail.com> wrote:

I've attached what I worked on today.

I really appreciate your quick response and the v15 patches. The bug
fixes in the v15 look good to me.

After receiving your email, I realized that this version does not
apply to the current master. This conflict is caused by commits of
2489d76c49 [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=2489d76c4906f4461a364ca8ad7e0751ead8aa0d and related. I have attached the rebased version, v16,
to this email. Resolving many conflicts was a bit of hard work, so I
may have made some mistakes.

Unfortunately, the rebased version did not pass regression tests. This
failure is due to segmentation faults regarding a null reference to
RelOptInfo. I show the code snippet that leads to the segfault as
follows.

=====
@@ -572,9 +662,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr,
Relids relids,
+    i = -1;
+    while ((i = bms_next_member(expr_relids, i)) >= 0)
+    {
+        RelOptInfo *rel = root->simple_rel_array[i];
+
+        rel->eclass_member_indexes =
bms_add_member(rel->eclass_member_indexes, em_index);
+    }
=====

The segfault occurred because root->simple_rel_array[i] is sometimes
NULL. This issue is similar to the one regarding
root->simple_rel_array[0]. Before the commit of 2489d76c49, we only
had to consider the nullability of root->simple_rel_array[0]. We
overcame this problem by creating the RelOptInfo in the
setup_append_rel_entry() function. However, after the commit,
root->simple_rel_array[i] with non-zero 'i' can also be NULL. I'm not
confident with its cause, but is this because non-base relations
appear in the expr_relids? Seeing the commit, I found the following
change in pull_varnos_walker():

=====
@@ -153,7 +161,11 @@ pull_varnos_walker(Node *node,
pull_varnos_context *context)
Var *var = (Var *) node;

        if (var->varlevelsup == context->sublevels_up)
+       {
            context->varnos = bms_add_member(context->varnos, var->varno);
+           context->varnos = bms_add_members(context->varnos,
+                                             var->varnullingrels);
+       }
        return false;
    }
    if (IsA(node, CurrentOfExpr))
=====

We get the expr_relids by pull_varnos(). This commit adds
var->varnullingrels to its result. From my observations, indices 'i'
such that root->simple_rel_array[i] is null come from
var->varnullingrels. This change is probably related to the segfault.
I don't understand the commit well, so please let me know if I'm
wrong.

To address this problem, in v16-0003, I moved EquivalenceMember
indexes in RelOptInfo to PlannerInfo. This change allows us to store
indexes whose corresponding RelOptInfo is NULL.

I'm happier
that this simple_rel_array[0] entry now only exists when planning set
operations, but I'd probably feel better if there was some other way
that felt less like we're faking up a RelOptInfo to store
EquivalenceMembers in.

Of course, I'm not sure if my approach in v16-0003 is ideal, but it
may help solve your concern above. Since simple_rel_array[0] is no
longer necessary with my patch, I removed the setup_append_rel_entry()
function in v16-0004. However, to work the patch, I needed to change
some assertions in v16-0005. For more details, please see the commit
message of v16-0005. After these works, the attached patches passed
all regression tests in my environment.

Instead of my approach, imitating the following change to
get_eclass_indexes_for_relids() is also a possible solution. Ignoring
NULL RelOptInfos enables us to avoid the segfault, but we have to
adjust EquivalenceMemberIterator to match the result, and I'm not sure
if this idea is correct.

=====
@@ -3204,6 +3268,12 @@ get_eclass_indexes_for_relids(PlannerInfo
*root, Relids relids)
{
RelOptInfo *rel = root->simple_rel_array[i];

+       if (rel == NULL)        /* must be an outer join */
+       {
+           Assert(bms_is_member(i, root->outer_join_rels));
+           continue;
+       }
+
        ec_indexes = bms_add_members(ec_indexes, rel->eclass_indexes);
    }
    return ec_indexes;
=====

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=2489d76c4906f4461a364ca8ad7e0751ead8aa0d

--
Best regards,
Yuya Watari

Attachments:

v16-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchapplication/octet-stream; name=v16-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From 4ff471f5e65955b1ce50d409d5abf8cebc181dda Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v16 1/5] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 98660524ad..b13b4c378f 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.35.3.windows.1

v16-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchapplication/octet-stream; name=v16-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From dac902c47557deff26b56345d9fc5486d7237453 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v16 2/5] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |   50 +-
 src/backend/nodes/list.c                  |   16 +
 src/backend/nodes/outfuncs.c              |    7 +-
 src/backend/optimizer/path/costsize.c     |    3 +-
 src/backend/optimizer/path/equivclass.c   | 1173 +++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |   22 +-
 src/backend/optimizer/path/pathkeys.c     |   26 +-
 src/backend/optimizer/plan/createplan.c   |   66 +-
 src/backend/optimizer/plan/planagg.c      |    1 +
 src/backend/optimizer/plan/planner.c      |    3 +
 src/backend/optimizer/prep/prepjointree.c |    3 +
 src/backend/optimizer/prep/prepunion.c    |   25 +-
 src/backend/optimizer/util/relnode.c      |    9 +
 src/include/nodes/pathnodes.h             |   81 +-
 src/include/nodes/pg_list.h               |    1 +
 src/include/optimizer/paths.h             |   67 +-
 src/tools/pgindent/typedefs.list          |    1 +
 17 files changed, 1285 insertions(+), 269 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f5926ab89d..8c48b7e479 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7617,6 +7617,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7675,23 +7679,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
-	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
+	while ((em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7718,7 +7726,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
+		Relids		expr_relids;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7733,19 +7743,20 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
+		expr_relids = pull_varnos(root, (Node *) expr);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7757,10 +7768,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
-
 		i++;
+		bms_free(expr_relids);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index a709d23ef1..b8972bbe5a 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1683,6 +1683,22 @@ list_sort(List *list, list_sort_comparator cmp)
 		qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp);
 }
 
+/*
+ * list_sort comparator for sorting a list into ascending ptr order.
+ */
+int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void	   *v1 = lfirst(p1);
+	void	   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
 /*
  * list_sort comparator for sorting a list into ascending int order.
  */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ba00b99249..416c972b4e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 7918bb6f0d..361de60cb5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5507,7 +5507,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index de335fdb4d..aad14c25ce 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,8 +32,12 @@
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
@@ -308,7 +312,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -319,6 +322,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -339,8 +344,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -352,10 +365,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -366,13 +381,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
 							jdomain, NULL, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -383,13 +399,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -400,6 +417,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -409,8 +428,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -419,9 +441,9 @@ 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,
+		em1 = add_eq_member(root, ec, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
+		em2 = add_eq_member(root, ec, item2, item2_relids,
 							jdomain, NULL, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
@@ -432,6 +454,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -507,14 +531,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids,
 			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids		expr_relids;
+	int			em_index = list_length(root->eq_members);
+	int			i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -524,6 +595,23 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (parent != NULL)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
+	/* record the actual relids from 'expr' */
+	em->em_norel_expr = bms_is_empty(expr_relids);
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -542,9 +630,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!parent)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (parent != NULL)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -601,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Since SortGroupClause nodes are top-level expressions (GROUP BY, ORDER
@@ -614,7 +725,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -629,10 +741,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -651,6 +764,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -668,8 +783,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -682,12 +800,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	if (newec->ec_has_volatile && sortref == 0) /* should not happen */
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-	/*
-	 * Get the precise set of relids appearing in the expression.
-	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
-
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  jdomain, NULL, opcintype);
 
 	/*
@@ -719,7 +832,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -755,19 +868,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	Relids		expr_relids;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
+
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -792,10 +911,13 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -937,7 +1059,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1024,7 +1146,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1081,7 +1203,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1113,6 +1235,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1122,9 +1245,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1136,9 +1259,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1182,9 +1307,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1194,7 +1319,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1207,7 +1333,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1220,9 +1347,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1260,7 +1390,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1283,11 +1413,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1296,6 +1430,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1316,11 +1451,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1361,11 +1497,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1527,6 +1663,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
@@ -1542,10 +1680,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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
+	while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1562,6 +1700,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1659,7 +1799,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -1704,12 +1844,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1721,12 +1865,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1788,10 +1932,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1802,9 +1947,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1815,9 +1963,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1875,7 +2027,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2075,7 +2227,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids;
+	Relids		outer_relids,
+				inner_relids;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2089,6 +2242,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2096,6 +2250,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 
@@ -2103,8 +2258,10 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2119,10 +2276,12 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2130,6 +2289,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2139,13 +2300,15 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2215,11 +2378,12 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2246,10 +2410,14 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2276,7 +2444,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2292,9 +2460,11 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
@@ -2342,11 +2512,28 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2401,21 +2588,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2478,16 +2672,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2540,16 +2739,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2600,7 +2802,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2614,29 +2817,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			/*
 			 * Consider only members that reference and can be computed at
 			 * child's topmost parent rel.  In particular we want to exclude
@@ -2645,13 +2834,16 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * simple Var; and in any case it wouldn't produce a member that
 			 * has any use in creating plans for the child rel.
 			 */
-			if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
-				!bms_is_empty(cur_em->em_relids))
+			if (bms_is_subset(cur_em->em_relids, top_parent_relids))
 			{
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
 
+				Assert(!cur_em->em_is_const);
+				Assert(!cur_em->em_is_child);
+				Assert(!bms_is_empty(cur_em->em_relids));
+
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
 					/* Simple single-level transformation */
@@ -2680,7 +2872,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
+				(void) add_eq_member(root, cur_ec, child_expr, new_relids,
 									 cur_em->em_jdomain,
 									 cur_em, cur_em->em_datatype);
 
@@ -2688,6 +2880,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2732,7 +2925,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2746,24 +2940,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			Expr	   *child_expr;
+			Relids		new_relids;
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2772,47 +2963,40 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-
-				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   nappinfos, appinfos);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_joinrel,
-														  child_joinrel->top_parent);
-				}
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   nappinfos, appinfos);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_joinrel,
+													  child_joinrel->top_parent);
+			}
 
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
+			/*
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the
+			 * transformation might have substituted a constant, but we
+			 * don't want the child member to be marked as constant.
+			 */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-			}
+			(void) add_eq_member(root, cur_ec, child_expr, new_relids,
+								 cur_em->em_jdomain,
+								 cur_em, cur_em->em_datatype);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -2871,7 +3055,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2893,16 +3078,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -2910,14 +3098,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3014,7 +3203,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3089,7 +3278,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3102,7 +3291,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3120,12 +3309,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+		list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3213,7 +3404,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3255,3 +3446,595 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = false;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = true;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_strict_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_strict_next */
+	Assert(!iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't return members which have no common rels with with_relids
+			 */
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_next */
+	Assert(iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't match members where em_relids that don't contain all rels
+			 * mentioned in with_relids.
+			 */
+			if (iter->relids_empty ||
+				!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 721a075201..5ebf944cb1 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3066,8 +3066,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3084,8 +3084,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3109,9 +3110,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index c4e7f97f68..0596532db4 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -940,18 +940,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1450,7 +1451,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1471,13 +1472,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 134130476e..ee4f3a9cee 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4494,7 +4498,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4508,7 +4512,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6129,7 +6133,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6196,7 +6201,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6224,7 +6229,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6240,7 +6245,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6311,7 +6316,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6320,7 +6326,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6346,7 +6354,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6356,7 +6365,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6706,7 +6717,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6769,7 +6781,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index d168620665..9f0fa42045 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index db5ff6fdca..c27ee6fab1 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -627,6 +627,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 870d84b29d..804ede97b1 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..4c7d66311f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -45,7 +45,7 @@
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
 
-
+static void setup_append_rel_entry(PlannerInfo *root);
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  List *colTypes, List *colCollations,
 										  bool junkOK,
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
@@ -149,6 +150,8 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
+	setup_append_rel_entry(root);
+
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -180,6 +183,26 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
+/*
+ * setup_append_rel_entry
+ *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		because generate_append_tlist() makes Vars with varno=0.  In
+ *		add_eq_member() we need to index EquivalenceMembers belonging to this
+ *		relation.
+ */
+static void
+setup_append_rel_entry(PlannerInfo *root)
+{
+	RelOptInfo *rel = makeNode(RelOptInfo);
+
+	memset(rel, 0, sizeof(RelOptInfo));
+	rel->eclass_member_indexes = NULL;
+
+	/* make sure nothing else has made use of this element */
+	Assert(root->simple_rel_array[0] == NULL);
+	root->simple_rel_array[0] = rel;
+}
+
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ad84cc43e1..dfa374a8cf 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -226,6 +226,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -701,6 +704,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -890,6 +896,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0d4b1ec4e4..cf43dd1385 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -313,6 +313,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -939,6 +948,24 @@ typedef struct RelOptInfo
 	double		allvisfrac;
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1360,6 +1387,39 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1377,9 +1437,19 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes;	/* Indexes into all PlannerInfos
+									 * eq_members for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes;	/* Indexes into PlannerInfo's
+										 * eq_members with em_is_child ==
+										 * false */
+	Bitmapset  *ec_norel_indexes;	/* Indexes into PlannerInfo's eq_members
+									 * for members where pull_varno on the
+									 * em_expr is an empty set */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1427,8 +1497,9 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
-	Relids		em_relids;		/* all relids appearing in em_expr */
-	bool		em_is_const;	/* expression is pseudoconstant? */
+	Relids		em_relids;		/* relids for this member */
+	bool		em_is_const;	/* is em_relids empty? */
+	bool		em_norel_expr;	/* true if em_expr contains no Vars */
 	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 */
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d28..77f26edf22 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -629,6 +629,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 
+extern int	list_ptr_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 736d78ea4c..8b0bcbaf67 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		relids_empty;	/* is with_relids empty? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members; /* include members with empty em_relids */
+#ifdef USE_ASSERT_CHECKING
+	bool		isstrict;		/* ensure the correct next function is used */
+#endif
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	int			current_index;	/* current iterator position, or -1. */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;			/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -134,7 +159,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -159,7 +185,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -185,6 +212,42 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+									   EquivalenceClass *ec,
+									   Relids relids,
+									   bool with_children,
+									   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 07fbb7ccf6..c219eea6dc 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -641,6 +641,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.35.3.windows.1

v16-0003-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchapplication/octet-stream; name=v16-0003-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchDownload
From 8d4db63bb7b2cd9f5c992f635e2f004815d9bc5b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 3 Feb 2023 13:27:07 +0900
Subject: [PATCH v16 3/5] Move EquivalenceMember indexes from RelOptInfo to
 PlannerInfo

---
 src/backend/nodes/gen_node_support.pl   | 10 ++++
 src/backend/optimizer/path/equivclass.c | 65 ++++++++++---------------
 src/backend/optimizer/path/indxpath.c   |  2 +-
 src/backend/optimizer/prep/prepunion.c  |  1 -
 src/backend/optimizer/util/relnode.c    | 15 +++---
 src/include/nodes/pathnodes.h           | 38 ++++++++-------
 6 files changed, 63 insertions(+), 68 deletions(-)

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 19ed29657c..f40f4fec8f 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -862,6 +862,11 @@ _equal${n}(const $n *a, const $n *b)
 			print $cff "\tCOPY_SCALAR_FIELD($f);\n"    unless $copy_ignore;
 			print $eff "\tCOMPARE_SCALAR_FIELD($f);\n" unless $equal_ignore;
 		}
+		elsif ($t eq 'ECIndexes*')
+		{
+			# TODO:
+			# Do nothing
+		}
 		else
 		{
 			die
@@ -1211,6 +1216,11 @@ _read${n}(void)
 	}
 ! unless $no_read;
 		}
+		elsif ($t eq 'ECIndexes*')
+		{
+			# TODO:
+			# Do nothing
+		}
 		else
 		{
 			die
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index aad14c25ce..c2b27e3ae1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -546,10 +546,9 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
-													source_idx);
+		root->eclass_indexes_array[i].source_indexes
+			= bms_add_member(root->eclass_indexes_array[i].source_indexes,
+							 source_idx);
 	}
 }
 
@@ -568,10 +567,9 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
-													derive_idx);
+		root->eclass_indexes_array[i].derive_indexes
+			= bms_add_member(root->eclass_indexes_array[i].derive_indexes,
+							 derive_idx);
 	}
 }
 
@@ -650,9 +648,9 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids
 	i = -1;
 	while ((i = bms_next_member(expr_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+		root->eclass_indexes_array[i].member_indexes
+			= bms_add_member(root->eclass_indexes_array[i].member_indexes,
+							 em_index);
 	}
 
 	return em;
@@ -2528,10 +2526,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 			i = -1;
 			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
 			{
-				RelOptInfo *rel = root->simple_rel_array[i];
-
-				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
-															coal_idx);
+				root->eclass_indexes_array[i].member_indexes
+					= bms_del_member(root->eclass_indexes_array[i].member_indexes,
+									 coal_idx);
 			}
 
 			return true;
@@ -3467,9 +3464,8 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+		rel_ems = bms_add_members(rel_ems,
+								  root->eclass_indexes_array[i].member_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3515,8 +3511,6 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
 		 * Bitmapset as small as possible.  This saves having to make a
@@ -3524,17 +3518,16 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		if (!with_children)
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(root->eclass_indexes_array[i].member_indexes,
 										 ec->ec_nonchild_indexes);
 		else
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(root->eclass_indexes_array[i].member_indexes,
 										 ec->ec_member_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
 			matching_ems = bms_int_members(matching_ems,
-										   rel->eclass_member_indexes);
+										   root->eclass_indexes_array[i].member_indexes);
 		}
 	}
 
@@ -3879,9 +3872,8 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis,
+								   root->eclass_indexes_array[i].source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3917,8 +3909,6 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
 		 * Bitmapset as small as possible.  This saves having to make a
@@ -3926,12 +3916,11 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rel->eclass_source_indexes);
+							 root->eclass_indexes_array[i].source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			esis = bms_int_members(esis, rel->eclass_source_indexes);
+			esis = bms_int_members(esis, root->eclass_indexes_array[i].source_indexes);
 		}
 	}
 
@@ -3968,9 +3957,8 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
-		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis,
+								   root->eclass_indexes_array[i].derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4006,8 +3994,6 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
-
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
 		 * Bitmapset as small as possible.  This saves having to make a
@@ -4015,12 +4001,11 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rel->eclass_derive_indexes);
+							 root->eclass_indexes_array[i].derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+			edis = bms_int_members(edis, root->eclass_indexes_array[i].derive_indexes);
 		}
 	}
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5ebf944cb1..384932727b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3111,7 +3111,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
 		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
-									 index->rel->eclass_member_indexes);
+									 root->eclass_indexes_array[index->rel->relid].member_indexes);
 
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 4c7d66311f..938a1f581c 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -196,7 +196,6 @@ setup_append_rel_entry(PlannerInfo *root)
 	RelOptInfo *rel = makeNode(RelOptInfo);
 
 	memset(rel, 0, sizeof(RelOptInfo));
-	rel->eclass_member_indexes = NULL;
 
 	/* make sure nothing else has made use of this element */
 	Assert(root->simple_rel_array[0] == NULL);
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index dfa374a8cf..2131333297 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -105,6 +105,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->simple_rel_array = (RelOptInfo **)
 		palloc0(size * sizeof(RelOptInfo *));
 
+	root->eclass_indexes_array = (ECIndexes *)
+		palloc0(size * sizeof(ECIndexes));
+
 	/* simple_rte_array is an array equivalent of the rtable list */
 	root->simple_rte_array = (RangeTblEntry **)
 		palloc0(size * sizeof(RangeTblEntry *));
@@ -168,6 +171,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 	root->simple_rel_array =
 		repalloc0_array(root->simple_rel_array, RelOptInfo *, root->simple_rel_array_size, new_size);
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, ECIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rte_array =
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
@@ -226,9 +232,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
-	rel->eclass_member_indexes = NULL;
-	rel->eclass_source_indexes = NULL;
-	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -704,9 +707,6 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -896,9 +896,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index cf43dd1385..0b57a89f94 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -169,6 +169,25 @@ typedef struct PlannerGlobal
 #define planner_subplan_get_plan(root, subplan) \
 	((Plan *) list_nth((root)->glob->subplans, (subplan)->plan_id - 1))
 
+typedef struct ECIndexes
+{
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *derive_indexes;
+} ECIndexes;
 
 /*----------
  * PlannerInfo
@@ -231,6 +250,8 @@ struct PlannerInfo
 	/* allocated size of array */
 	int			simple_rel_array_size;
 
+	ECIndexes  *eclass_indexes_array pg_node_attr(array_size(simple_rel_array_size));
+
 	/*
 	 * simple_rte_array is the same length as simple_rel_array and holds
 	 * pointers to the associated rangetable entries.  Using this is a shade
@@ -949,23 +970,6 @@ typedef struct RelOptInfo
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
 
-	/*
-	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
-	 */
-	Bitmapset  *eclass_member_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_source_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_derive_indexes;
-
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
-- 
2.35.3.windows.1

v16-0004-Remove-setup_append_rel_entry.patchapplication/octet-stream; name=v16-0004-Remove-setup_append_rel_entry.patchDownload
From ae122f049f61a034ef961f3e75275abf20f2699d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 6 Feb 2023 09:25:24 +0900
Subject: [PATCH v16 4/5] Remove setup_append_rel_entry()

---
 src/backend/optimizer/prep/prepunion.c | 23 +----------------------
 1 file changed, 1 insertion(+), 22 deletions(-)

diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 938a1f581c..31e5ed2c10 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -45,7 +45,7 @@
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
 
-static void setup_append_rel_entry(PlannerInfo *root);
+
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  List *colTypes, List *colCollations,
 										  bool junkOK,
@@ -150,8 +150,6 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
-	setup_append_rel_entry(root);
-
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -183,25 +181,6 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
-/*
- * setup_append_rel_entry
- *		Add entry into root's simple_rel_array at element 0.  This is required
- *		because generate_append_tlist() makes Vars with varno=0.  In
- *		add_eq_member() we need to index EquivalenceMembers belonging to this
- *		relation.
- */
-static void
-setup_append_rel_entry(PlannerInfo *root)
-{
-	RelOptInfo *rel = makeNode(RelOptInfo);
-
-	memset(rel, 0, sizeof(RelOptInfo));
-
-	/* make sure nothing else has made use of this element */
-	Assert(root->simple_rel_array[0] == NULL);
-	root->simple_rel_array[0] = rel;
-}
-
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
-- 
2.35.3.windows.1

v16-0005-Fix-an-assertion.patchapplication/octet-stream; name=v16-0005-Fix-an-assertion.patchDownload
From 6438018135d46e8b32a295f017eaf649501e1996 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 6 Feb 2023 09:25:35 +0900
Subject: [PATCH v16 5/5] Fix an assertion

Fix an assertion to deal with root->simple_rel_array[0].

This fix is required because the while condition for looping through
relids has been changed in our patches as follows:

- while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+ while ((i = bms_next_member(newec->ec_relids, i)) >= 0)

Originally, we stopped iterating when we we reached the
root->simple_rel_array[0] member. However, we do not do so with our
patches, so the assertion of Assert(bms_is_member(i,
root->outer_join_rels)) may fail. To solve this problem, this commit
changes its condition.

TODO: Do we need to change the other occurances?
---
 src/backend/optimizer/path/equivclass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index c2b27e3ae1..0ab017788d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -836,7 +836,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
 			if (rel == NULL)	/* must be an outer join */
 			{
-				Assert(bms_is_member(i, root->outer_join_rels));
+				Assert(i == 0 || bms_is_member(i, root->outer_join_rels));
 				continue;
 			}
 
-- 
2.35.3.windows.1

#39Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#38)
Re: [PoC] Reducing planning time when tables have many partitions

On 2/6/23 06:47, Yuya Watari wrote:

Of course, I'm not sure if my approach in v16-0003 is ideal, but it
may help solve your concern above. Since simple_rel_array[0] is no
longer necessary with my patch, I removed the setup_append_rel_entry()
function in v16-0004. However, to work the patch, I needed to change
some assertions in v16-0005. For more details, please see the commit
message of v16-0005. After these works, the attached patches passed
all regression tests in my environment.

Instead of my approach, imitating the following change to
get_eclass_indexes_for_relids() is also a possible solution. Ignoring
NULL RelOptInfos enables us to avoid the segfault, but we have to
adjust EquivalenceMemberIterator to match the result, and I'm not sure
if this idea is correct.

As I see, You moved the indexes from RelOptInfo to PlannerInfo. May be
better to move them into RangeTblEntry instead?

--
Regards
Andrey Lepikhov
Postgres Professional

#40Yuya Watari
watari.yuya@gmail.com
In reply to: Andrey Lepikhov (#39)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Dear Andrey,

On Tue, Feb 14, 2023 at 7:01 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

As I see, You moved the indexes from RelOptInfo to PlannerInfo. May be
better to move them into RangeTblEntry instead?

I really appreciate your kind advice. I think your idea is very good.
I have implemented it as the v17 patches, which are attached to this
email. The v17 has passed all regression tests in my environment.

--
Best regards,
Yuya Watari

Attachments:

v17-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchapplication/octet-stream; name=v17-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From 2608679be912df0b470e23acf81ccf04c92b67b6 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v17 1/4] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 98660524ad..b13b4c378f 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -920,8 +920,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	return a;
 }
 
-- 
2.35.3.windows.1

v17-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchapplication/octet-stream; name=v17-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From 3c2fc01219f67422273598debbebb86494beb5c2 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v17 2/4] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |   50 +-
 src/backend/nodes/list.c                  |   16 +
 src/backend/nodes/outfuncs.c              |    7 +-
 src/backend/optimizer/path/costsize.c     |    3 +-
 src/backend/optimizer/path/equivclass.c   | 1173 +++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |   22 +-
 src/backend/optimizer/path/pathkeys.c     |   26 +-
 src/backend/optimizer/plan/createplan.c   |   66 +-
 src/backend/optimizer/plan/planagg.c      |    1 +
 src/backend/optimizer/plan/planner.c      |    3 +
 src/backend/optimizer/prep/prepjointree.c |    3 +
 src/backend/optimizer/prep/prepunion.c    |   25 +-
 src/backend/optimizer/util/relnode.c      |    9 +
 src/include/nodes/pathnodes.h             |   81 +-
 src/include/nodes/pg_list.h               |    1 +
 src/include/optimizer/paths.h             |   67 +-
 src/tools/pgindent/typedefs.list          |    1 +
 17 files changed, 1285 insertions(+), 269 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f5926ab89d..8c48b7e479 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7617,6 +7617,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7675,23 +7679,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
-	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
+	while ((em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7718,7 +7726,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
+		Relids		expr_relids;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7733,19 +7743,20 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
+		expr_relids = pull_varnos(root, (Node *) expr);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7757,10 +7768,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
-
 		i++;
+		bms_free(expr_relids);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index a709d23ef1..b8972bbe5a 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1683,6 +1683,22 @@ list_sort(List *list, list_sort_comparator cmp)
 		qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp);
 }
 
+/*
+ * list_sort comparator for sorting a list into ascending ptr order.
+ */
+int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void	   *v1 = lfirst(p1);
+	void	   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
 /*
  * list_sort comparator for sorting a list into ascending int order.
  */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ba00b99249..416c972b4e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 7918bb6f0d..361de60cb5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5507,7 +5507,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9132ce235f..53c8760c03 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,8 +32,12 @@
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
@@ -308,7 +312,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -319,6 +322,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -339,8 +344,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -352,10 +365,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -366,13 +381,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
 							jdomain, NULL, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -383,13 +399,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -400,6 +417,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -409,8 +428,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -419,9 +441,9 @@ 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,
+		em1 = add_eq_member(root, ec, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
+		em2 = add_eq_member(root, ec, item2, item2_relids,
 							jdomain, NULL, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
@@ -432,6 +454,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -507,14 +531,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids,
 			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids		expr_relids;
+	int			em_index = list_length(root->eq_members);
+	int			i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -524,6 +595,23 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (parent != NULL)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
+	/* record the actual relids from 'expr' */
+	em->em_norel_expr = bms_is_empty(expr_relids);
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -542,9 +630,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!parent)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (parent != NULL)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -601,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Since SortGroupClause nodes are top-level expressions (GROUP BY, ORDER
@@ -614,7 +725,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -629,10 +741,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -651,6 +764,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -668,8 +783,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -682,12 +800,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	if (newec->ec_has_volatile && sortref == 0) /* should not happen */
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-	/*
-	 * Get the precise set of relids appearing in the expression.
-	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
-
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  jdomain, NULL, opcintype);
 
 	/*
@@ -719,7 +832,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -754,19 +867,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	Relids		expr_relids;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
+
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -791,10 +910,13 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -936,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1023,7 +1145,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1080,7 +1202,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1112,6 +1234,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1121,9 +1244,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1135,9 +1258,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1181,9 +1306,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1193,7 +1318,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1206,7 +1332,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1219,9 +1346,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1259,7 +1389,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1282,11 +1412,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1295,6 +1429,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1315,11 +1450,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1360,11 +1496,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
  * We could simplify this function's API by computing it internally, but in
@@ -1526,6 +1662,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
@@ -1541,10 +1679,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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
+	while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1561,6 +1699,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1658,7 +1798,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -1703,12 +1843,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1720,12 +1864,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1787,10 +1931,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1801,9 +1946,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1814,9 +1962,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1874,7 +2026,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2074,7 +2226,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids;
+	Relids		outer_relids,
+				inner_relids;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2088,6 +2241,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2095,6 +2249,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 
@@ -2102,8 +2257,10 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2118,10 +2275,12 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2129,6 +2288,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2138,13 +2299,15 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2214,11 +2377,12 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2245,10 +2409,14 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2275,7 +2443,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2291,9 +2459,11 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
@@ -2341,11 +2511,28 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2400,21 +2587,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2477,16 +2671,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2539,16 +2738,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2599,7 +2801,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2613,29 +2816,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			/*
 			 * Consider only members that reference and can be computed at
 			 * child's topmost parent rel.  In particular we want to exclude
@@ -2644,13 +2833,16 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * simple Var; and in any case it wouldn't produce a member that
 			 * has any use in creating plans for the child rel.
 			 */
-			if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
-				!bms_is_empty(cur_em->em_relids))
+			if (bms_is_subset(cur_em->em_relids, top_parent_relids))
 			{
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
 
+				Assert(!cur_em->em_is_const);
+				Assert(!cur_em->em_is_child);
+				Assert(!bms_is_empty(cur_em->em_relids));
+
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
 					/* Simple single-level transformation */
@@ -2679,7 +2871,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
+				(void) add_eq_member(root, cur_ec, child_expr, new_relids,
 									 cur_em->em_jdomain,
 									 cur_em, cur_em->em_datatype);
 
@@ -2687,6 +2879,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2731,7 +2924,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2745,24 +2939,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			Expr	   *child_expr;
+			Relids		new_relids;
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2771,47 +2962,40 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-
-				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   nappinfos, appinfos);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_joinrel,
-														  child_joinrel->top_parent);
-				}
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   nappinfos, appinfos);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_joinrel,
+													  child_joinrel->top_parent);
+			}
 
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
+			/*
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the
+			 * transformation might have substituted a constant, but we
+			 * don't want the child member to be marked as constant.
+			 */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-			}
+			(void) add_eq_member(root, cur_ec, child_expr, new_relids,
+								 cur_em->em_jdomain,
+								 cur_em, cur_em->em_datatype);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -2870,7 +3054,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2892,16 +3077,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -2909,14 +3097,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3013,7 +3202,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3088,7 +3277,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3101,7 +3290,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3119,12 +3308,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+		list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3212,7 +3403,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3254,3 +3445,595 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = false;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = true;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_strict_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_strict_next */
+	Assert(!iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't return members which have no common rels with with_relids
+			 */
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_next */
+	Assert(iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't match members where em_relids that don't contain all rels
+			 * mentioned in with_relids.
+			 */
+			if (iter->relids_empty ||
+				!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 721a075201..5ebf944cb1 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -978,7 +978,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3066,8 +3066,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3084,8 +3084,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3109,9 +3110,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index c4e7f97f68..0596532db4 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -940,18 +940,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1450,7 +1451,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1471,13 +1472,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 134130476e..ee4f3a9cee 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4494,7 +4498,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4508,7 +4512,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6129,7 +6133,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6196,7 +6201,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6224,7 +6229,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6240,7 +6245,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6311,7 +6316,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6320,7 +6326,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6346,7 +6354,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6356,7 +6365,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6706,7 +6717,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6769,7 +6781,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index d168620665..9f0fa42045 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a1873ce26d..c8275e704a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -627,6 +627,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 870d84b29d..804ede97b1 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..4c7d66311f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -45,7 +45,7 @@
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
 
-
+static void setup_append_rel_entry(PlannerInfo *root);
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  List *colTypes, List *colCollations,
 										  bool junkOK,
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
@@ -149,6 +150,8 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
+	setup_append_rel_entry(root);
+
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -180,6 +183,26 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
+/*
+ * setup_append_rel_entry
+ *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		because generate_append_tlist() makes Vars with varno=0.  In
+ *		add_eq_member() we need to index EquivalenceMembers belonging to this
+ *		relation.
+ */
+static void
+setup_append_rel_entry(PlannerInfo *root)
+{
+	RelOptInfo *rel = makeNode(RelOptInfo);
+
+	memset(rel, 0, sizeof(RelOptInfo));
+	rel->eclass_member_indexes = NULL;
+
+	/* make sure nothing else has made use of this element */
+	Assert(root->simple_rel_array[0] == NULL);
+	root->simple_rel_array[0] = rel;
+}
+
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index a70a16238a..77ab4990e7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -226,6 +226,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -701,6 +704,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -890,6 +896,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index d61a62da19..40c86e6c4b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -313,6 +313,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -935,6 +944,24 @@ typedef struct RelOptInfo
 	double		allvisfrac;
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1356,6 +1383,39 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1373,9 +1433,19 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes;	/* Indexes into all PlannerInfos
+									 * eq_members for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes;	/* Indexes into PlannerInfo's
+										 * eq_members with em_is_child ==
+										 * false */
+	Bitmapset  *ec_norel_indexes;	/* Indexes into PlannerInfo's eq_members
+									 * for members where pull_varno on the
+									 * em_expr is an empty set */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1423,8 +1493,9 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
-	Relids		em_relids;		/* all relids appearing in em_expr */
-	bool		em_is_const;	/* expression is pseudoconstant? */
+	Relids		em_relids;		/* relids for this member */
+	bool		em_is_const;	/* is em_relids empty? */
+	bool		em_norel_expr;	/* true if em_expr contains no Vars */
 	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 */
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d28..77f26edf22 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -629,6 +629,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 
+extern int	list_ptr_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 736d78ea4c..8b0bcbaf67 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		relids_empty;	/* is with_relids empty? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members; /* include members with empty em_relids */
+#ifdef USE_ASSERT_CHECKING
+	bool		isstrict;		/* ensure the correct next function is used */
+#endif
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	int			current_index;	/* current iterator position, or -1. */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;			/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -134,7 +159,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -159,7 +185,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -185,6 +212,42 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+									   EquivalenceClass *ec,
+									   Relids relids,
+									   bool with_children,
+									   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 22ea42c16b..c8b562fbcc 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -642,6 +642,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.35.3.windows.1

v17-0003-Fix-an-assertion.patchapplication/octet-stream; name=v17-0003-Fix-an-assertion.patchDownload
From e9e6024066de13c8f8e6ce175b862d5295cfa41d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 6 Feb 2023 09:25:35 +0900
Subject: [PATCH v17 3/4] Fix an assertion

Fix an assertion to deal with root->simple_rel_array[0].

This fix is required because the while condition for looping through
relids has been changed in our patches as follows:

- while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+ while ((i = bms_next_member(newec->ec_relids, i)) >= 0)

Originally, we stopped iterating when we we reached the
root->simple_rel_array[0] member. However, we do not do so with our
patches, so the assertion of Assert(bms_is_member(i,
root->outer_join_rels)) may fail. To solve this problem, this commit
changes its condition.

TODO: Do we need to change the other occurances?
---
 src/backend/optimizer/path/equivclass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 53c8760c03..f8bd400c42 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -838,7 +838,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
 			if (rel == NULL)	/* must be an outer join */
 			{
-				Assert(bms_is_member(i, root->outer_join_rels));
+				Assert(i == 0 || bms_is_member(i, root->outer_join_rels));
 				continue;
 			}
 
-- 
2.35.3.windows.1

v17-0004-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchapplication/octet-stream; name=v17-0004-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchDownload
From ac89a73ff9e783471e35d9be6059ea303e667697 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 17 Feb 2023 16:56:38 +0900
Subject: [PATCH v17 4/4] Move EquivalenceMember indexes from RelOptInfo to
 RangeTblEntry

---
 src/backend/nodes/outfuncs.c            |  3 ++
 src/backend/nodes/readfuncs.c           |  3 ++
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 src/backend/optimizer/path/indxpath.c   |  2 +-
 src/backend/optimizer/prep/prepunion.c  | 12 +++---
 src/backend/optimizer/util/inherit.c    |  3 +-
 src/backend/optimizer/util/relnode.c    |  9 -----
 src/include/nodes/parsenodes.h          | 17 ++++++++
 src/include/nodes/pathnodes.h           | 17 --------
 9 files changed, 59 insertions(+), 61 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 416c972b4e..7e1ef5995c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -569,6 +569,9 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_member_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f3629cdfd1..0befbbe222 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -543,6 +543,9 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_member_indexes);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index f8bd400c42..329b206e1e 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -546,9 +546,9 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
 													source_idx);
 	}
 }
@@ -568,9 +568,9 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
 													derive_idx);
 	}
 }
@@ -650,9 +650,9 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids
 	i = -1;
 	while ((i = bms_next_member(expr_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+		rte->eclass_member_indexes = bms_add_member(rte->eclass_member_indexes, em_index);
 	}
 
 	return em;
@@ -2527,9 +2527,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 			i = -1;
 			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
 			{
-				RelOptInfo *rel = root->simple_rel_array[i];
+				RangeTblEntry *rte = root->simple_rte_array[i];
 
-				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+				rte->eclass_member_indexes = bms_del_member(rte->eclass_member_indexes,
 															coal_idx);
 			}
 
@@ -3466,9 +3466,9 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+		rel_ems = bms_add_members(rel_ems, rte->eclass_member_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3514,7 +3514,7 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3523,17 +3523,17 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		if (!with_children)
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
 										 ec->ec_nonchild_indexes);
 		else
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
 										 ec->ec_member_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
+			rte = root->simple_rte_array[i];
 			matching_ems = bms_int_members(matching_ems,
-										   rel->eclass_member_indexes);
+										   rte->eclass_member_indexes);
 		}
 	}
 
@@ -3878,9 +3878,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3916,7 +3916,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3925,12 +3925,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rel->eclass_source_indexes);
+							 rte->eclass_source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			esis = bms_int_members(esis, rel->eclass_source_indexes);
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
 		}
 	}
 
@@ -3967,9 +3967,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4005,7 +4005,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4014,12 +4014,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rel->eclass_derive_indexes);
+							 rte->eclass_derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
 		}
 	}
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5ebf944cb1..63343085f3 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3111,7 +3111,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
 		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
-									 index->rel->eclass_member_indexes);
+									 root->simple_rte_array[index->rel->relid]->eclass_member_indexes);
 
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 4c7d66311f..8eb054f8ed 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -185,7 +185,7 @@ plan_set_operations(PlannerInfo *root)
 
 /*
  * setup_append_rel_entry
- *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		Add entry into root's simple_rte_array at element 0.  This is required
  *		because generate_append_tlist() makes Vars with varno=0.  In
  *		add_eq_member() we need to index EquivalenceMembers belonging to this
  *		relation.
@@ -193,14 +193,14 @@ plan_set_operations(PlannerInfo *root)
 static void
 setup_append_rel_entry(PlannerInfo *root)
 {
-	RelOptInfo *rel = makeNode(RelOptInfo);
+	RangeTblEntry *rte = makeNode(RangeTblEntry);
 
-	memset(rel, 0, sizeof(RelOptInfo));
-	rel->eclass_member_indexes = NULL;
+	memset(rte, 0, sizeof(RangeTblEntry));
+	rte->eclass_member_indexes = NULL;
 
 	/* make sure nothing else has made use of this element */
-	Assert(root->simple_rel_array[0] == NULL);
-	root->simple_rel_array[0] = rel;
+	Assert(root->simple_rte_array[0] == NULL);
+	root->simple_rte_array[0] = rte;
 }
 
 /*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index bae9688e46..3d6345b2cd 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -480,7 +480,8 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * below, so a "flat" copy is sufficient to start with.
 	 */
 	childrte = makeNode(RangeTblEntry);
-	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/* TODO: Usage of offsetof */
+	memcpy(childrte, parentrte, offsetof(RangeTblEntry, eclass_member_indexes));
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 77ab4990e7..a70a16238a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -226,9 +226,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
-	rel->eclass_member_indexes = NULL;
-	rel->eclass_source_indexes = NULL;
-	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -704,9 +701,6 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -896,9 +890,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f7d7f10f7d..94a3dd6ae1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1202,6 +1202,23 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 40c86e6c4b..1668a06368 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -945,23 +945,6 @@ typedef struct RelOptInfo
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
 
-	/*
-	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
-	 */
-	Bitmapset  *eclass_member_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_source_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_derive_indexes;
-
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
-- 
2.35.3.windows.1

#41Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Yuya Watari (#40)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Watari-san, this patch does not currently apply. Could you please
rebase?

David, do you intend to continue to be involved in reviewing this one?

Thanks to both,

--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"All rings of power are equal,
But some rings of power are more equal than others."
(George Orwell's The Lord of the Rings)

#42David Rowley
dgrowleyml@gmail.com
In reply to: Alvaro Herrera (#41)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, 9 Mar 2023 at 01:34, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

David, do you intend to continue to be involved in reviewing this one?

Yes. I'm currently trying to make a few Bitmapset improvements which
include the change made in this thread's 0001 patch over on [1]/messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com.

For the main patch, I've been starting to wonder if it should work
completely differently. Instead of adding members for partitioned and
inheritance children, we could just translate the Vars from child to
top-level parent and find the member that way. I wondered if this
method might be even faster as it would forego
add_child_rel_equivalences(). I think we'd still need em_is_child for
UNION ALL children. So far, I've not looked into this in detail. I
was hoping to find an idea that would allow some means to have the
planner realise that a LIST partition which allows a single Datum
could skip pushing base quals which are constantly true. i.e:

create table lp (a int) partition by list(a);
create table lp1 partition of lp for values in(1);
explain select * from lp where a = 1;

Seq Scan on lp1 lp (cost=0.00..41.88 rows=13 width=4)
Filter: (a = 1)

David

[1]: /messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com

#43Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#42)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Mar 8, 2023 at 9:34 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

Hello Watari-san, this patch does not currently apply. Could you please
rebase?

Thank you for pointing it out. I have attached the rebased version to
this email. This version includes an additional change, v18-0005. The
change relates to the Bitmapset operations that David mentioned:

On Thu, Mar 9, 2023 at 6:23 AM David Rowley <dgrowleyml@gmail.com> wrote:

Yes. I'm currently trying to make a few Bitmapset improvements which
include the change made in this thread's 0001 patch over on [1].

As of v18-0005, the redundant loop to check if the result of
bms_intersect() is empty has been removed. This change is almost the
same as David's following idea in the [1]/messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com thread, but slightly
different.

On Fri, Mar 3, 2023 at 10:52 AM David Rowley <dgrowleyml@gmail.com> wrote:

The patch also optimizes sub-optimal newly added code which calls
bms_is_empty_internal() when we have other more optimal means to
determine if the set is empty or not.

I conducted an experiment measuring the planning time of Query B [2]/messages/by-id/CAJ2pMka2PBXNNzUfe0-ksFsxVN+gmfKq7aGQ5v35TcpjFG3Ggg@mail.gmail.com.
In the experiment, I tested the next four versions:

* Master
* (A): v18-0001 + v18-0002 + v18-0003 + v18-0004 (= v17)
* (B): v18-0001 + v18-0002 + v18-0003 + v18-0004 + v18-0005
* (C): v18-0002 + v18-0003 + v18-0004 + David's patches in [1]/messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com
--> Since [1]/messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com includes v18-0001, (C) does not contain v18-0001.

The following tables show the results. These show that when the number
of partitions is large, (B) is faster than (A). This result indicates
that the change in v18-0005 is effective on this workload. In
addition, the patches in [1]/messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com slowed down the performance compared to
(A) and (B). I am not sure of the cause of this degradation. I will
investigate this issue further. I hope these results will help the
discussion of [1]/messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com.

Table 1: Planning time of Query B (ms)
----------------------------------------------
n | Master | (A) | (B) | (C)
----------------------------------------------
1 | 37.780 | 38.836 | 38.354 | 38.187
2 | 36.222 | 37.067 | 37.416 | 37.068
4 | 38.001 | 38.410 | 37.980 | 38.005
8 | 42.384 | 41.159 | 41.601 | 42.218
16 | 53.906 | 47.277 | 47.080 | 59.466
32 | 88.271 | 58.842 | 58.762 | 69.474
64 | 229.445 | 91.675 | 91.194 | 115.348
128 | 896.418 | 166.251 | 161.182 | 335.121
256 | 4220.514 | 371.369 | 350.723 | 923.272
----------------------------------------------

Table 2: Planning time speedup of Query B (higher is better)
--------------------------------------------------------------------------
n | Master / (A) | Master / (B) | Master / (C) | (A) / (B) | (A) / (C)
--------------------------------------------------------------------------
1 | 97.3% | 98.5% | 98.9% | 101.3% | 101.7%
2 | 97.7% | 96.8% | 97.7% | 99.1% | 100.0%
4 | 98.9% | 100.1% | 100.0% | 101.1% | 101.1%
8 | 103.0% | 101.9% | 100.4% | 98.9% | 97.5%
16 | 114.0% | 114.5% | 90.7% | 100.4% | 79.5%
32 | 150.0% | 150.2% | 127.1% | 100.1% | 84.7%
64 | 250.3% | 251.6% | 198.9% | 100.5% | 79.5%
128 | 539.2% | 556.2% | 267.5% | 103.1% | 49.6%
256 | 1136.5% | 1203.4% | 457.1% | 105.9% | 40.2%
--------------------------------------------------------------------------

On Thu, Mar 9, 2023 at 6:23 AM David Rowley <dgrowleyml@gmail.com> wrote:

For the main patch, I've been starting to wonder if it should work
completely differently. Instead of adding members for partitioned and
inheritance children, we could just translate the Vars from child to
top-level parent and find the member that way. I wondered if this
method might be even faster as it would forego
add_child_rel_equivalences(). I think we'd still need em_is_child for
UNION ALL children. So far, I've not looked into this in detail. I
was hoping to find an idea that would allow some means to have the
planner realise that a LIST partition which allows a single Datum
could skip pushing base quals which are constantly true. i.e:

create table lp (a int) partition by list(a);
create table lp1 partition of lp for values in(1);
explain select * from lp where a = 1;

Seq Scan on lp1 lp (cost=0.00..41.88 rows=13 width=4)
Filter: (a = 1)

Thank you for considering this issue. I will look into this as well.

[1]: /messages/by-id/CAApHDvq9eq0W_aFUGrb6ba28ieuQN4zM5Uwqxy7+LMZjJc+VGg@mail.gmail.com
[2]: /messages/by-id/CAJ2pMka2PBXNNzUfe0-ksFsxVN+gmfKq7aGQ5v35TcpjFG3Ggg@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

v18-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchapplication/octet-stream; name=v18-0001-Adjust-bms_int_members-so-that-it-shortens-the-l.patchDownload
From e16329da62a223244dfa527ea81d17f6db114652 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 1 Dec 2022 20:36:10 +1300
Subject: [PATCH v18 1/5] Adjust bms_int_members so that it shortens the left
 input

Prior to this change, if the left input to bms_int_members had fewer
Bitmapwords than the right input, the additional words in the left input
would be zeroed.  Doing things this why only really makes sense if we
expect to add additional words to the resulting bitmap set again later.
Seemingly, most of our use cases don't add to the resulting Bitmapset,
in fact, many cases are performing bms_int_members in a loop which is
likely to even further increase the amount of all zero words in the set.

Leaving these zero trailing words in place can cause inefficiencies in
functions such as bms_is_empty and loops which loop over a set using
bms_next_member when the set is left with a large number of trailing
words.

If there are use cases which need to add additional words again, then,
repalloc has to do very little processing when the requested allocation
size is <= the actual allocation size.  Both AllocSetRealloc and
GenerationRealloc simply just return the input pointer again.  If there
are any cases where we do increase the size of the set again, then this
just shifts the zeroing of the additional words until then.

We could give bms_del_members() and bms_difference similar treatment, but
that would require additional code to record the last non-zero word after
masking out the members being deleted.  That seems less worthwhile as it
will add cost when we're unable to trim down the number of words.
---
 src/backend/nodes/bitmapset.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 7ba3cf635b..af87da15fb 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -942,8 +942,16 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
 		a->words[i] &= b->words[i];
-	for (; i < a->nwords; i++)
-		a->words[i] = 0;
+
+	/*
+	 * We simply shorten the left input to both remove the need to zero the
+	 * trailing words and also to reduce the required processing if this
+	 * function is being called in a loop.  If more words are required later
+	 * then AllocSetRealloc is likely not going to have to work very hard if
+	 * the new requested size is >= the actual size of the allocation.
+	 */
+	a->nwords = shortlen;
+
 	/* If we computed an empty result, we must return NULL */
 	if (bms_is_empty_internal(a))
 	{
-- 
2.39.2.windows.1

v18-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchapplication/octet-stream; name=v18-0002-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From f302c867126d1b29c0e2a5c1562c0d9a63cef55d Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v18 2/5] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |   50 +-
 src/backend/nodes/list.c                  |   16 +
 src/backend/nodes/outfuncs.c              |    7 +-
 src/backend/optimizer/path/costsize.c     |    3 +-
 src/backend/optimizer/path/equivclass.c   | 1173 +++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |   22 +-
 src/backend/optimizer/path/pathkeys.c     |   26 +-
 src/backend/optimizer/plan/createplan.c   |   66 +-
 src/backend/optimizer/plan/planagg.c      |    1 +
 src/backend/optimizer/plan/planner.c      |    3 +
 src/backend/optimizer/prep/prepjointree.c |    3 +
 src/backend/optimizer/prep/prepunion.c    |   25 +-
 src/backend/optimizer/util/relnode.c      |    9 +
 src/include/nodes/pathnodes.h             |   81 +-
 src/include/nodes/pg_list.h               |    1 +
 src/include/optimizer/paths.h             |   67 +-
 src/tools/pgindent/typedefs.list          |    1 +
 17 files changed, 1285 insertions(+), 269 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f5926ab89d..8c48b7e479 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7617,6 +7617,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7675,23 +7679,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
-	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
+	while ((em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7718,7 +7726,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
+		Relids		expr_relids;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7733,19 +7743,20 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
+		expr_relids = pull_varnos(root, (Node *) expr);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7757,10 +7768,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
-
 		i++;
+		bms_free(expr_relids);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index a709d23ef1..b8972bbe5a 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1683,6 +1683,22 @@ list_sort(List *list, list_sort_comparator cmp)
 		qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp);
 }
 
+/*
+ * list_sort comparator for sorting a list into ascending ptr order.
+ */
+int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void	   *v1 = lfirst(p1);
+	void	   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
 /*
  * list_sort comparator for sorting a list into ascending int order.
  */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ba00b99249..416c972b4e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 7918bb6f0d..361de60cb5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5507,7 +5507,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9949d5b1d3..9205e4093f 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,8 +32,12 @@
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
@@ -308,7 +312,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -319,6 +322,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -339,8 +344,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -352,10 +365,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -366,13 +381,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
 							jdomain, NULL, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -383,13 +399,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -400,6 +417,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -409,8 +428,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -419,9 +441,9 @@ 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,
+		em1 = add_eq_member(root, ec, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
+		em2 = add_eq_member(root, ec, item2, item2_relids,
 							jdomain, NULL, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
@@ -432,6 +454,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -507,14 +531,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids,
 			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids		expr_relids;
+	int			em_index = list_length(root->eq_members);
+	int			i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -524,6 +595,23 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (parent != NULL)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
+	/* record the actual relids from 'expr' */
+	em->em_norel_expr = bms_is_empty(expr_relids);
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -542,9 +630,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!parent)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (parent != NULL)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -601,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Since SortGroupClause nodes are top-level expressions (GROUP BY, ORDER
@@ -614,7 +725,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -629,10 +741,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -651,6 +764,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -668,8 +783,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -682,12 +800,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	if (newec->ec_has_volatile && sortref == 0) /* should not happen */
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-	/*
-	 * Get the precise set of relids appearing in the expression.
-	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
-
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  jdomain, NULL, opcintype);
 
 	/*
@@ -719,7 +832,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -754,19 +867,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	Relids		expr_relids;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
+
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -791,10 +910,13 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -936,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1023,7 +1145,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1080,7 +1202,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1112,6 +1234,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1121,9 +1244,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1135,9 +1258,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1181,9 +1306,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1193,7 +1318,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1206,7 +1332,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1219,9 +1346,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1259,7 +1389,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1282,11 +1412,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1295,6 +1429,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1315,11 +1450,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1360,11 +1496,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, ojrelid is the associated OJ relid,
  * otherwise it's zero.
@@ -1549,6 +1685,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
@@ -1564,10 +1702,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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
+	while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1584,6 +1722,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1681,7 +1821,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -1726,12 +1866,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1743,12 +1887,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1810,10 +1954,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1824,9 +1969,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1837,9 +1985,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1897,7 +2049,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2097,7 +2249,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids;
+	Relids		outer_relids,
+				inner_relids;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2111,6 +2264,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2118,6 +2272,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 
@@ -2125,8 +2280,10 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2141,10 +2298,12 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2152,6 +2311,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2161,13 +2322,15 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2237,11 +2400,12 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2268,10 +2432,14 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2298,7 +2466,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2314,9 +2482,11 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
@@ -2364,11 +2534,28 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2423,21 +2610,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2500,16 +2694,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2562,16 +2761,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2622,7 +2824,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2636,29 +2839,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			/*
 			 * Consider only members that reference and can be computed at
 			 * child's topmost parent rel.  In particular we want to exclude
@@ -2667,13 +2856,16 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * simple Var; and in any case it wouldn't produce a member that
 			 * has any use in creating plans for the child rel.
 			 */
-			if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
-				!bms_is_empty(cur_em->em_relids))
+			if (bms_is_subset(cur_em->em_relids, top_parent_relids))
 			{
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
 
+				Assert(!cur_em->em_is_const);
+				Assert(!cur_em->em_is_child);
+				Assert(!bms_is_empty(cur_em->em_relids));
+
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
 					/* Simple single-level transformation */
@@ -2702,7 +2894,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
+				(void) add_eq_member(root, cur_ec, child_expr, new_relids,
 									 cur_em->em_jdomain,
 									 cur_em, cur_em->em_datatype);
 
@@ -2710,6 +2902,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2754,7 +2947,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2768,24 +2962,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			Expr	   *child_expr;
+			Relids		new_relids;
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2794,47 +2985,40 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-
-				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   nappinfos, appinfos);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_joinrel,
-														  child_joinrel->top_parent);
-				}
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   nappinfos, appinfos);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_joinrel,
+													  child_joinrel->top_parent);
+			}
 
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
+			/*
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the
+			 * transformation might have substituted a constant, but we
+			 * don't want the child member to be marked as constant.
+			 */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-			}
+			(void) add_eq_member(root, cur_ec, child_expr, new_relids,
+								 cur_em->em_jdomain,
+								 cur_em, cur_em->em_datatype);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -2893,7 +3077,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2915,16 +3100,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -2932,14 +3120,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3047,7 +3236,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3122,7 +3311,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3135,7 +3324,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3153,12 +3342,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+		list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3246,7 +3437,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3288,3 +3479,595 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = false;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = true;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_strict_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_strict_next */
+	Assert(!iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't return members which have no common rels with with_relids
+			 */
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_next */
+	Assert(iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't match members where em_relids that don't contain all rels
+			 * mentioned in with_relids.
+			 */
+			if (iter->relids_empty ||
+				!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 9f4698f2a2..e63ddad305 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -975,7 +975,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	else if (index->amcanorderbyop && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (orderbyclauses)
@@ -3063,8 +3063,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * one-to-one with the requested pathkeys.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	List	   *orderby_clauses = NIL;
@@ -3081,8 +3081,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 		/*
 		 * Note: for any failure to match, we just return NIL immediately.
@@ -3106,9 +3107,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index c4e7f97f68..0596532db4 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -940,18 +940,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1450,7 +1451,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1471,13 +1472,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index fa09a6103b..7d0fab0354 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1260,7 +1263,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1304,7 +1308,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1445,7 +1449,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1476,7 +1480,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1966,7 +1970,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2185,7 +2189,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2209,7 +2213,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2278,7 +2282,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4494,7 +4498,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4508,7 +4512,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6129,7 +6133,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6196,7 +6201,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6224,7 +6229,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6240,7 +6245,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6311,7 +6316,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6320,7 +6326,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6346,7 +6354,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6356,7 +6365,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6706,7 +6717,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6769,7 +6781,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index d168620665..9f0fa42045 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a1873ce26d..c8275e704a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -627,6 +627,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 870d84b29d..804ede97b1 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..4c7d66311f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -45,7 +45,7 @@
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
 
-
+static void setup_append_rel_entry(PlannerInfo *root);
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  List *colTypes, List *colCollations,
 										  bool junkOK,
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
@@ -149,6 +150,8 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
+	setup_append_rel_entry(root);
+
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -180,6 +183,26 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
+/*
+ * setup_append_rel_entry
+ *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		because generate_append_tlist() makes Vars with varno=0.  In
+ *		add_eq_member() we need to index EquivalenceMembers belonging to this
+ *		relation.
+ */
+static void
+setup_append_rel_entry(PlannerInfo *root)
+{
+	RelOptInfo *rel = makeNode(RelOptInfo);
+
+	memset(rel, 0, sizeof(RelOptInfo));
+	rel->eclass_member_indexes = NULL;
+
+	/* make sure nothing else has made use of this element */
+	Assert(root->simple_rel_array[0] == NULL);
+	root->simple_rel_array[0] = rel;
+}
+
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 68fd033595..f2f9a350ce 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -227,6 +227,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -713,6 +716,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -901,6 +907,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index d61a62da19..40c86e6c4b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -313,6 +313,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -935,6 +944,24 @@ typedef struct RelOptInfo
 	double		allvisfrac;
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1356,6 +1383,39 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1373,9 +1433,19 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes;	/* Indexes into all PlannerInfos
+									 * eq_members for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes;	/* Indexes into PlannerInfo's
+										 * eq_members with em_is_child ==
+										 * false */
+	Bitmapset  *ec_norel_indexes;	/* Indexes into PlannerInfo's eq_members
+									 * for members where pull_varno on the
+									 * em_expr is an empty set */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1423,8 +1493,9 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
-	Relids		em_relids;		/* all relids appearing in em_expr */
-	bool		em_is_const;	/* expression is pseudoconstant? */
+	Relids		em_relids;		/* relids for this member */
+	bool		em_is_const;	/* is em_relids empty? */
+	bool		em_norel_expr;	/* true if em_expr contains no Vars */
 	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 */
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d28..77f26edf22 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -629,6 +629,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 
+extern int	list_ptr_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 5b9db7733d..cc5b8701a7 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -114,6 +114,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		relids_empty;	/* is with_relids empty? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members; /* include members with empty em_relids */
+#ifdef USE_ASSERT_CHECKING
+	bool		isstrict;		/* ensure the correct next function is used */
+#endif
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	int			current_index;	/* current iterator position, or -1. */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;			/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -134,7 +159,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -160,7 +186,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -186,6 +213,42 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+									   EquivalenceClass *ec,
+									   Relids relids,
+									   bool with_children,
+									   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 86a9303bf5..36b65025ed 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -643,6 +643,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.39.2.windows.1

v18-0003-Fix-an-assertion.patchapplication/octet-stream; name=v18-0003-Fix-an-assertion.patchDownload
From a47bf2f223fe1eac2416215c48f7e9e10257b821 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 6 Feb 2023 09:25:35 +0900
Subject: [PATCH v18 3/5] Fix an assertion

Fix an assertion to deal with root->simple_rel_array[0].

This fix is required because the while condition for looping through
relids has been changed in our patches as follows:

- while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+ while ((i = bms_next_member(newec->ec_relids, i)) >= 0)

Originally, we stopped iterating when we we reached the
root->simple_rel_array[0] member. However, we do not do so with our
patches, so the assertion of Assert(bms_is_member(i,
root->outer_join_rels)) may fail. To solve this problem, this commit
changes its condition.

TODO: Do we need to change the other occurances?
---
 src/backend/optimizer/path/equivclass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9205e4093f..101b881b51 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -838,7 +838,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
 			if (rel == NULL)	/* must be an outer join */
 			{
-				Assert(bms_is_member(i, root->outer_join_rels));
+				Assert(i == 0 || bms_is_member(i, root->outer_join_rels));
 				continue;
 			}
 
-- 
2.39.2.windows.1

v18-0004-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchapplication/octet-stream; name=v18-0004-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchDownload
From e1b76bf5fc338023c0ef84d1c817b20c25221837 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 17 Feb 2023 16:56:38 +0900
Subject: [PATCH v18 4/5] Move EquivalenceMember indexes from RelOptInfo to
 RangeTblEntry

---
 src/backend/nodes/outfuncs.c            |  3 ++
 src/backend/nodes/readfuncs.c           |  3 ++
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 src/backend/optimizer/path/indxpath.c   |  6 ++-
 src/backend/optimizer/prep/prepunion.c  | 12 +++---
 src/backend/optimizer/util/inherit.c    |  8 ++++
 src/backend/optimizer/util/relnode.c    |  9 -----
 src/include/nodes/parsenodes.h          |  9 +++++
 src/include/nodes/pathnodes.h           | 17 --------
 9 files changed, 60 insertions(+), 61 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 416c972b4e..7e1ef5995c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -569,6 +569,9 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_member_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f3629cdfd1..0befbbe222 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -543,6 +543,9 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_member_indexes);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 101b881b51..fbba9c812b 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -546,9 +546,9 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
 													source_idx);
 	}
 }
@@ -568,9 +568,9 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
 													derive_idx);
 	}
 }
@@ -650,9 +650,9 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids
 	i = -1;
 	while ((i = bms_next_member(expr_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+		rte->eclass_member_indexes = bms_add_member(rte->eclass_member_indexes, em_index);
 	}
 
 	return em;
@@ -2550,9 +2550,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 			i = -1;
 			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
 			{
-				RelOptInfo *rel = root->simple_rel_array[i];
+				RangeTblEntry *rte = root->simple_rte_array[i];
 
-				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+				rte->eclass_member_indexes = bms_del_member(rte->eclass_member_indexes,
 															coal_idx);
 			}
 
@@ -3500,9 +3500,9 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+		rel_ems = bms_add_members(rel_ems, rte->eclass_member_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3548,7 +3548,7 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3557,17 +3557,17 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		if (!with_children)
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
 										 ec->ec_nonchild_indexes);
 		else
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
 										 ec->ec_member_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
+			rte = root->simple_rte_array[i];
 			matching_ems = bms_int_members(matching_ems,
-										   rel->eclass_member_indexes);
+										   rte->eclass_member_indexes);
 		}
 	}
 
@@ -3912,9 +3912,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3950,7 +3950,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3959,12 +3959,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rel->eclass_source_indexes);
+							 rte->eclass_source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			esis = bms_int_members(esis, rel->eclass_source_indexes);
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
 		}
 	}
 
@@ -4001,9 +4001,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4039,7 +4039,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4048,12 +4048,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rel->eclass_derive_indexes);
+							 rte->eclass_derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
 		}
 	}
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index e63ddad305..93a7d45eb2 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3107,8 +3107,10 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
-									 index->rel->eclass_member_indexes);
+		matching_ems =
+			bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+						  root->simple_rte_array[index->rel->relid]
+							  ->eclass_member_indexes);
 
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 4c7d66311f..8eb054f8ed 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -185,7 +185,7 @@ plan_set_operations(PlannerInfo *root)
 
 /*
  * setup_append_rel_entry
- *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		Add entry into root's simple_rte_array at element 0.  This is required
  *		because generate_append_tlist() makes Vars with varno=0.  In
  *		add_eq_member() we need to index EquivalenceMembers belonging to this
  *		relation.
@@ -193,14 +193,14 @@ plan_set_operations(PlannerInfo *root)
 static void
 setup_append_rel_entry(PlannerInfo *root)
 {
-	RelOptInfo *rel = makeNode(RelOptInfo);
+	RangeTblEntry *rte = makeNode(RangeTblEntry);
 
-	memset(rel, 0, sizeof(RelOptInfo));
-	rel->eclass_member_indexes = NULL;
+	memset(rte, 0, sizeof(RangeTblEntry));
+	rte->eclass_member_indexes = NULL;
 
 	/* make sure nothing else has made use of this element */
-	Assert(root->simple_rel_array[0] == NULL);
-	root->simple_rel_array[0] = rel;
+	Assert(root->simple_rte_array[0] == NULL);
+	root->simple_rte_array[0] = rte;
 }
 
 /*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index bae9688e46..ac11387193 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -481,6 +481,14 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_member_indexes = NULL;
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f2f9a350ce..68fd033595 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -227,9 +227,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
-	rel->eclass_member_indexes = NULL;
-	rel->eclass_source_indexes = NULL;
-	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -716,9 +713,6 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -907,9 +901,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 371aa0ffc5..e6cf18a47f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1201,6 +1201,15 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_member_indexes;	/* Indexes in PlannerInfo's eq_members
+										 * list of EquivalenceMembers that
+										 * mention this relation */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 40c86e6c4b..1668a06368 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -945,23 +945,6 @@ typedef struct RelOptInfo
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
 
-	/*
-	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
-	 */
-	Bitmapset  *eclass_member_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_source_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_derive_indexes;
-
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
-- 
2.39.2.windows.1

v18-0005-Reduce-bms_is_empty_internal-function-call.patchapplication/octet-stream; name=v18-0005-Reduce-bms_is_empty_internal-function-call.patchDownload
From c748ed6c858873812e7a5bc7d9a846bb2c2673ac Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 10 Mar 2023 15:01:26 +0900
Subject: [PATCH v18 5/5] Reduce bms_is_empty_internal() function call

Originally, we checked whether the result was empty or not by calling
bms_is_empty_internal() function at the end of bms_int_members().
However, this leads to redundant looping over the bitmapset. This
commit reduces this call and improves the performance.
---
 src/backend/nodes/bitmapset.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index af87da15fb..b8f4f85361 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -929,6 +929,7 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 {
 	int			shortlen;
 	int			i;
+	bool		result_is_empty = true;
 
 	/* Handle cases where either input is NULL */
 	if (a == NULL)
@@ -941,7 +942,12 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	/* Intersect b into a; we need never copy */
 	shortlen = Min(a->nwords, b->nwords);
 	for (i = 0; i < shortlen; i++)
-		a->words[i] &= b->words[i];
+	{
+		bitmapword	w = (a->words[i] &= b->words[i]);
+
+		if (w != 0)
+			result_is_empty = false;
+	}
 
 	/*
 	 * We simply shorten the left input to both remove the need to zero the
@@ -953,7 +959,7 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
 	a->nwords = shortlen;
 
 	/* If we computed an empty result, we must return NULL */
-	if (bms_is_empty_internal(a))
+	if (result_is_empty)
 	{
 		pfree(a);
 		return NULL;
-- 
2.39.2.windows.1

#44Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#43)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Fri, Mar 10, 2023 at 5:38 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Thank you for pointing it out. I have attached the rebased version to
this email.

Recent commits, such as a8c09daa8b [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=a8c09daa8bb1d741bb8b3d31a12752448eb6fb7c, have caused conflicts and
compilation errors in these patches. I have attached the fixed version
to this email.

The v19-0004 adds an 'em_index' field representing the index within
root->eq_members of the EquivalenceMember. This field is needed to
delete EquivalenceMembers when iterating them using the ec_members
list instead of the ec_member_indexes.

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=a8c09daa8bb1d741bb8b3d31a12752448eb6fb7c

--
Best regards,
Yuya Watari

Attachments:

v19-0002-Fix-an-assertion.patchapplication/octet-stream; name=v19-0002-Fix-an-assertion.patchDownload
From 68aba7d769333d4ef70f4da71c72497ff9402fc8 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 6 Feb 2023 09:25:35 +0900
Subject: [PATCH v19 2/4] Fix an assertion

Fix an assertion to deal with root->simple_rel_array[0].

This fix is required because the while condition for looping through
relids has been changed in our patches as follows:

- while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+ while ((i = bms_next_member(newec->ec_relids, i)) >= 0)

Originally, we stopped iterating when we we reached the
root->simple_rel_array[0] member. However, we do not do so with our
patches, so the assertion of Assert(bms_is_member(i,
root->outer_join_rels)) may fail. To solve this problem, this commit
changes its condition.

TODO: Do we need to change the other occurances?
---
 src/backend/optimizer/path/equivclass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 91855b4bb0..530f1e9984 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -841,7 +841,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
 			if (rel == NULL)	/* must be an outer join */
 			{
-				Assert(bms_is_member(i, root->outer_join_rels));
+				Assert(i == 0 || bms_is_member(i, root->outer_join_rels));
 				continue;
 			}
 
-- 
2.41.0.windows.1

v19-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchapplication/octet-stream; name=v19-0001-Add-Bitmapset-indexes-for-faster-lookup-of-Equiv.patchDownload
From 420510658e1bd9d427d4d845d04965e18f3c8459 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sun, 4 Dec 2022 11:09:21 +1300
Subject: [PATCH v19 1/4] Add Bitmapset indexes for faster lookup of
 EquivalenceMembers

For queries containing a large number of joins or for queries to
partitioned tables, the looking up of EquivalenceMembers in an
EquivalenceClass can become slow due to the large number of members.
Until now, the searching for EquivalenceMembers has been done using a
linear search over the EquivalenceClass's list of members. Here we
effectively add an index to allow faster lookups of these members.  This
is done by way of adding all members to all classes into a List in
PlannerInfo, then we have a Bitmapset in each EquivalenceClass with
members for each index in PlannerInfo's list which belong to this class.
Additionally, each RelOptInfo also maintains another Bitmapset which
stores each index in PlannerInfo's list of EquivalenceMember for all
members, in all classes, which mention this relation.  This allows us to
quickly find all EquivalenceMembers for a given RelOptInfo and a given
EquivalenceClass by intersecting these two lists.

When searching for members belonging to join rels, we must either
intersect or union all base RelOptInfo's member indexes then intersect the
result to the EquivalenceClass's indexes.  Whether we intersect or union
depends on if we need members that mention all relations or members that
mention any of the base relations making up the join relation.

This method of indexing can significantly speed up the planner when
querying a partitioned table with a large number of partitions when the
query contains a few joins.  Tests have shown around a x10 increase in
performance with 1000 partitions.
---
 contrib/postgres_fdw/postgres_fdw.c       |   50 +-
 src/backend/nodes/list.c                  |   16 +
 src/backend/nodes/outfuncs.c              |    7 +-
 src/backend/optimizer/path/costsize.c     |    3 +-
 src/backend/optimizer/path/equivclass.c   | 1173 +++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |   22 +-
 src/backend/optimizer/path/pathkeys.c     |   26 +-
 src/backend/optimizer/plan/createplan.c   |   66 +-
 src/backend/optimizer/plan/planagg.c      |    1 +
 src/backend/optimizer/plan/planner.c      |    3 +
 src/backend/optimizer/prep/prepjointree.c |    3 +
 src/backend/optimizer/prep/prepunion.c    |   25 +-
 src/backend/optimizer/util/relnode.c      |    9 +
 src/include/nodes/pathnodes.h             |   81 +-
 src/include/nodes/pg_list.h               |    1 +
 src/include/optimizer/paths.h             |   67 +-
 src/tools/pgindent/typedefs.list          |    1 +
 17 files changed, 1285 insertions(+), 269 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..a8b99351b4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7619,6 +7619,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7677,23 +7681,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
-	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
+	while ((em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7720,7 +7728,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
+		Relids		expr_relids;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7735,19 +7745,20 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
+		expr_relids = pull_varnos(root, (Node *) expr);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7759,10 +7770,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
-
 		i++;
+		bms_free(expr_relids);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index 750ee5a7e5..15b74812a3 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1683,6 +1683,22 @@ list_sort(List *list, list_sort_comparator cmp)
 		qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp);
 }
 
+/*
+ * list_sort comparator for sorting a list into ascending ptr order.
+ */
+int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void	   *v1 = lfirst(p1);
+	void	   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
 /*
  * list_sort comparator for sorting a list into ascending int order.
  */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 955286513d..6276fcb816 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ef475d95a1..e728b83a6c 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5514,7 +5514,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e2..91855b4bb0 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,8 +32,12 @@
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
@@ -311,7 +315,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -322,6 +325,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -342,8 +347,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -355,10 +368,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -369,13 +384,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
 							jdomain, NULL, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -386,13 +402,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -403,6 +420,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -412,8 +431,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -422,9 +444,9 @@ 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,
+		em1 = add_eq_member(root, ec, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
+		em2 = add_eq_member(root, ec, item2, item2_relids,
 							jdomain, NULL, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
@@ -435,6 +457,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -510,14 +534,61 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids,
 			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids		expr_relids;
+	int			em_index = list_length(root->eq_members);
+	int			i;
 
 	em->em_expr = expr;
 	em->em_relids = relids;
@@ -527,6 +598,23 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (parent != NULL)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
+	/* record the actual relids from 'expr' */
+	em->em_norel_expr = bms_is_empty(expr_relids);
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -545,9 +633,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!parent)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (parent != NULL)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -604,6 +714,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Since SortGroupClause nodes are top-level expressions (GROUP BY, ORDER
@@ -617,7 +728,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,10 +744,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -654,6 +767,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -671,8 +786,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -685,12 +803,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	if (newec->ec_has_volatile && sortref == 0) /* should not happen */
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-	/*
-	 * Get the precise set of relids appearing in the expression.
-	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
-
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  jdomain, NULL, opcintype);
 
 	/*
@@ -722,7 +835,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -757,19 +870,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	Relids		expr_relids;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
+
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -794,10 +913,13 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -939,7 +1061,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1026,7 +1148,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1083,7 +1205,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1115,6 +1237,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1124,9 +1247,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1138,9 +1261,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1184,9 +1309,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1196,7 +1321,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1209,7 +1335,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1222,9 +1349,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1262,7 +1392,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1285,11 +1415,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * For the moment we force all the Vars to be available at all join nodes
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
-	 * what happened in the pre-8.3 code.
+	 * what happened in the pre-8.3 code.  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1298,6 +1432,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1318,11 +1453,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1363,11 +1499,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1555,6 +1691,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
@@ -1570,10 +1708,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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
+	while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1590,6 +1728,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1687,7 +1827,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -1732,12 +1872,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1893,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1816,10 +1960,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1830,9 +1975,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1843,9 +1991,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1903,7 +2055,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2113,7 +2265,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids;
+	Relids		outer_relids,
+				inner_relids;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2127,6 +2280,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2134,6 +2288,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 
@@ -2141,8 +2296,10 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2157,10 +2314,12 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2168,6 +2327,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2177,13 +2338,15 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2253,11 +2416,12 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2284,10 +2448,14 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2314,7 +2482,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2330,9 +2498,11 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
@@ -2380,11 +2550,28 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RelOptInfo *rel = root->simple_rel_array[i];
+
+				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
+															coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2439,21 +2626,28 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2516,16 +2710,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2578,16 +2777,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2638,7 +2840,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2652,29 +2855,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			/*
 			 * Consider only members that reference and can be computed at
 			 * child's topmost parent rel.  In particular we want to exclude
@@ -2683,13 +2872,16 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * simple Var; and in any case it wouldn't produce a member that
 			 * has any use in creating plans for the child rel.
 			 */
-			if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
-				!bms_is_empty(cur_em->em_relids))
+			if (bms_is_subset(cur_em->em_relids, top_parent_relids))
 			{
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
 
+				Assert(!cur_em->em_is_const);
+				Assert(!cur_em->em_is_child);
+				Assert(!bms_is_empty(cur_em->em_relids));
+
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
 					/* Simple single-level transformation */
@@ -2718,7 +2910,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
+				(void) add_eq_member(root, cur_ec, child_expr, new_relids,
 									 cur_em->em_jdomain,
 									 cur_em, cur_em->em_datatype);
 
@@ -2726,6 +2918,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2770,7 +2963,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2784,24 +2978,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			Expr	   *child_expr;
+			Relids		new_relids;
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2810,47 +3001,40 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-
-				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   nappinfos, appinfos);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_joinrel,
-														  child_joinrel->top_parent);
-				}
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   nappinfos, appinfos);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_joinrel,
+													  child_joinrel->top_parent);
+			}
 
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
+			/*
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the
+			 * transformation might have substituted a constant, but we
+			 * don't want the child member to be marked as constant.
+			 */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-			}
+			(void) add_eq_member(root, cur_ec, child_expr, new_relids,
+								 cur_em->em_jdomain,
+								 cur_em, cur_em->em_datatype);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -2909,7 +3093,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,16 +3116,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -2948,14 +3136,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3063,7 +3252,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3138,7 +3327,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3151,7 +3340,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3169,12 +3358,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+		list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3262,7 +3453,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3304,3 +3495,595 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rel->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rel->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = false;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = true;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_strict_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_strict_next */
+	Assert(!iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't return members which have no common rels with with_relids
+			 */
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_next */
+	Assert(iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't match members where em_relids that don't contain all rels
+			 * mentioned in with_relids.
+			 */
+			if (iter->relids_empty ||
+				!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rel->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RelOptInfo *rel = root->simple_rel_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rel->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6a93d767a5..0b61bda7a7 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,8 +184,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,8 +3071,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	ListCell   *lc1;
@@ -3087,8 +3087,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,9 +3109,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+									 index->rel->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index e53ea84224..12abd18d86 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -940,18 +940,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1452,7 +1453,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1473,13 +1474,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ec73789bc2..39204e1c1d 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -269,10 +269,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -293,7 +296,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1261,7 +1264,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1305,7 +1309,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1446,7 +1450,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1477,7 +1481,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1956,7 +1960,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2175,7 +2179,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2199,7 +2203,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2268,7 +2272,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4479,7 +4483,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4493,7 +4497,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6114,7 +6118,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6181,7 +6186,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6209,7 +6214,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6225,7 +6230,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6296,7 +6301,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6305,7 +6311,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6331,7 +6339,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6341,7 +6350,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6691,7 +6702,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6754,7 +6766,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index a6090167f5..46f82e9681 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index eaee9eda77..a2be6805a6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -624,6 +624,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 73ff40721c..4e169ccae4 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0c68ec011b..4c7d66311f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -45,7 +45,7 @@
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
 
-
+static void setup_append_rel_entry(PlannerInfo *root);
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  List *colTypes, List *colCollations,
 										  bool junkOK,
@@ -128,6 +128,7 @@ plan_set_operations(PlannerInfo *root)
 	 * so that pathkeys.c won't complain.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
@@ -149,6 +150,8 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
+	setup_append_rel_entry(root);
+
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -180,6 +183,26 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
+/*
+ * setup_append_rel_entry
+ *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		because generate_append_tlist() makes Vars with varno=0.  In
+ *		add_eq_member() we need to index EquivalenceMembers belonging to this
+ *		relation.
+ */
+static void
+setup_append_rel_entry(PlannerInfo *root)
+{
+	RelOptInfo *rel = makeNode(RelOptInfo);
+
+	memset(rel, 0, sizeof(RelOptInfo));
+	rel->eclass_member_indexes = NULL;
+
+	/* make sure nothing else has made use of this element */
+	Assert(root->simple_rel_array[0] == NULL);
+	root->simple_rel_array[0] = rel;
+}
+
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 15e3910b79..c298b8ff7a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -228,6 +228,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_member_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -716,6 +719,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -904,6 +910,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_member_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c17b53f7ad..6e94acb1a7 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -310,6 +310,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -929,6 +938,24 @@ typedef struct RelOptInfo
 	double		allvisfrac;
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
+	 */
+	Bitmapset  *eclass_member_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1350,6 +1377,39 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1367,9 +1427,19 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes;	/* Indexes into all PlannerInfos
+									 * eq_members for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes;	/* Indexes into PlannerInfo's
+										 * eq_members with em_is_child ==
+										 * false */
+	Bitmapset  *ec_norel_indexes;	/* Indexes into PlannerInfo's eq_members
+									 * for members where pull_varno on the
+									 * em_expr is an empty set */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1417,8 +1487,9 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
-	Relids		em_relids;		/* all relids appearing in em_expr */
-	bool		em_is_const;	/* expression is pseudoconstant? */
+	Relids		em_relids;		/* relids for this member */
+	bool		em_is_const;	/* is em_relids empty? */
+	bool		em_norel_expr;	/* true if em_expr contains no Vars */
 	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 */
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d28..77f26edf22 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -629,6 +629,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 
+extern int	list_ptr_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 50bc3b503a..efb72b7a95 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -117,6 +117,31 @@ extern void mark_dummy_rel(RelOptInfo *rel);
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		relids_empty;	/* is with_relids empty? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members; /* include members with empty em_relids */
+#ifdef USE_ASSERT_CHECKING
+	bool		isstrict;		/* ensure the correct next function is used */
+#endif
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	int			current_index;	/* current iterator position, or -1. */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;			/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -137,7 +162,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -163,7 +189,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -189,6 +216,42 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+									   EquivalenceClass *ec,
+									   Relids relids,
+									   bool with_children,
+									   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e941fb6c82..050de96fbd 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -649,6 +649,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.41.0.windows.1

v19-0003-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchapplication/octet-stream; name=v19-0003-Move-EquivalenceMember-indexes-from-RelOptInfo-t.patchDownload
From bd98fa9add02793efb71f60fc0aa10108d1cb09e Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 17 Feb 2023 16:56:38 +0900
Subject: [PATCH v19 3/4] Move EquivalenceMember indexes from RelOptInfo to
 RangeTblEntry

---
 src/backend/nodes/outfuncs.c            |  3 ++
 src/backend/nodes/readfuncs.c           |  3 ++
 src/backend/optimizer/path/equivclass.c | 57 +++++++++++++------------
 src/backend/optimizer/path/indxpath.c   |  6 ++-
 src/backend/optimizer/prep/prepunion.c  | 12 +++---
 src/backend/optimizer/util/inherit.c    |  8 ++++
 src/backend/optimizer/util/relnode.c    |  9 ----
 src/include/nodes/parsenodes.h          |  9 ++++
 src/include/nodes/pathnodes.h           | 17 --------
 9 files changed, 62 insertions(+), 62 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6276fcb816..ff2d577366 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -570,6 +570,9 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_member_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 97e43cbb49..030ed3bc83 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -567,6 +567,9 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_member_indexes);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 530f1e9984..bda89ce833 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -549,9 +549,9 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
 													source_idx);
 	}
 }
@@ -571,9 +571,9 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
 													derive_idx);
 	}
 }
@@ -653,9 +653,10 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids
 	i = -1;
 	while ((i = bms_next_member(expr_relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel->eclass_member_indexes = bms_add_member(rel->eclass_member_indexes, em_index);
+		rte->eclass_member_indexes =
+			bms_add_member(rte->eclass_member_indexes, em_index);
 	}
 
 	return em;
@@ -2566,10 +2567,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 			i = -1;
 			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
 			{
-				RelOptInfo *rel = root->simple_rel_array[i];
+				RangeTblEntry *rte = root->simple_rte_array[i];
 
-				rel->eclass_member_indexes = bms_del_member(rel->eclass_member_indexes,
-															coal_idx);
+				rte->eclass_member_indexes =
+					bms_del_member(rte->eclass_member_indexes, coal_idx);
 			}
 
 			return true;
@@ -3516,9 +3517,9 @@ get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_ems = bms_add_members(rel_ems, rel->eclass_member_indexes);
+		rel_ems = bms_add_members(rel_ems, rte->eclass_member_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3564,7 +3565,7 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3573,17 +3574,17 @@ get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		if (!with_children)
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
 										 ec->ec_nonchild_indexes);
 		else
-			matching_ems = bms_intersect(rel->eclass_member_indexes,
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
 										 ec->ec_member_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
+			rte = root->simple_rte_array[i];
 			matching_ems = bms_int_members(matching_ems,
-										   rel->eclass_member_indexes);
+										   rte->eclass_member_indexes);
 		}
 	}
 
@@ -3928,9 +3929,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3966,7 +3967,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3975,12 +3976,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rel->eclass_source_indexes);
+							 rte->eclass_source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			esis = bms_int_members(esis, rel->eclass_source_indexes);
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
 		}
 	}
 
@@ -4017,9 +4018,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4055,7 +4056,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RelOptInfo *rel = root->simple_rel_array[i];
+		RangeTblEntry *rte = root->simple_rte_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4064,12 +4065,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rel->eclass_derive_indexes);
+							 rte->eclass_derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rel = root->simple_rel_array[i];
-			edis = bms_int_members(edis, rel->eclass_derive_indexes);
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
 		}
 	}
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 0b61bda7a7..f0564b4a03 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3109,8 +3109,10 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		matching_ems = bms_intersect(pathkey->pk_eclass->ec_member_indexes,
-									 index->rel->eclass_member_indexes);
+		matching_ems =
+			bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+						  root->simple_rte_array[index->rel->relid]
+							  ->eclass_member_indexes);
 
 		i = -1;
 		while ((i = bms_next_member(matching_ems, i)) >= 0)
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 4c7d66311f..8eb054f8ed 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -185,7 +185,7 @@ plan_set_operations(PlannerInfo *root)
 
 /*
  * setup_append_rel_entry
- *		Add entry into root's simple_rel_array at element 0.  This is required
+ *		Add entry into root's simple_rte_array at element 0.  This is required
  *		because generate_append_tlist() makes Vars with varno=0.  In
  *		add_eq_member() we need to index EquivalenceMembers belonging to this
  *		relation.
@@ -193,14 +193,14 @@ plan_set_operations(PlannerInfo *root)
 static void
 setup_append_rel_entry(PlannerInfo *root)
 {
-	RelOptInfo *rel = makeNode(RelOptInfo);
+	RangeTblEntry *rte = makeNode(RangeTblEntry);
 
-	memset(rel, 0, sizeof(RelOptInfo));
-	rel->eclass_member_indexes = NULL;
+	memset(rte, 0, sizeof(RangeTblEntry));
+	rte->eclass_member_indexes = NULL;
 
 	/* make sure nothing else has made use of this element */
-	Assert(root->simple_rel_array[0] == NULL);
-	root->simple_rel_array[0] = rel;
+	Assert(root->simple_rte_array[0] == NULL);
+	root->simple_rte_array[0] = rte;
 }
 
 /*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 94de855a22..d4d50f342d 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -481,6 +481,14 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_member_indexes = NULL;
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index c298b8ff7a..15e3910b79 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -228,9 +228,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
-	rel->eclass_member_indexes = NULL;
-	rel->eclass_source_indexes = NULL;
-	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -719,9 +716,6 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -910,9 +904,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
-	joinrel->eclass_member_indexes = NULL;
-	joinrel->eclass_source_indexes = NULL;
-	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 88b03cc472..fb2baa45c3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1202,6 +1202,15 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_member_indexes;	/* Indexes in PlannerInfo's eq_members
+										 * list of EquivalenceMembers that
+										 * mention this relation */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6e94acb1a7..3a45bcc3f4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -939,23 +939,6 @@ typedef struct RelOptInfo
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
 
-	/*
-	 * Indexes in PlannerInfo's eq_members list of EMs that mention this rel
-	 */
-	Bitmapset  *eclass_member_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_source_indexes;
-
-	/*
-	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
-	 * this relation.
-	 */
-	Bitmapset  *eclass_derive_indexes;
-
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
-- 
2.41.0.windows.1

v19-0004-Fix-compilation-errors-in-remove_rel_from_eclass.patchapplication/octet-stream; name=v19-0004-Fix-compilation-errors-in-remove_rel_from_eclass.patchDownload
From fa8ee2700409182b564f2754de14e81b5acaa977 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 30 Jun 2023 14:22:52 +0900
Subject: [PATCH v19 4/4] Fix compilation errors in remove_rel_from_eclass()

---
 src/backend/optimizer/path/equivclass.c   |  1 +
 src/backend/optimizer/plan/analyzejoins.c | 37 +++++++++++++++++++----
 src/include/nodes/pathnodes.h             |  1 +
 3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index bda89ce833..7dc9b92a15 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -591,6 +591,7 @@ add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids
 	int			i;
 
 	em->em_expr = expr;
+	em->em_index = em_index;
 	em->em_relids = relids;
 	em->em_is_const = false;
 	em->em_is_child = (parent != NULL);
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 5f3cce873a..11b8514c18 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -39,7 +39,7 @@ static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -522,7 +522,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -607,9 +607,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -627,18 +629,41 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 		if (bms_is_member(relid, cur_em->em_relids) ||
 			bms_is_member(ojrelid, cur_em->em_relids))
 		{
+			RangeTblEntry  *rte;
+
 			Assert(!cur_em->em_is_const);
+
+			/* Delete 'relid' from em_relids */
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
+			rte = root->simple_rte_array[relid];
+			rte->eclass_member_indexes =
+				bms_del_member(rte->eclass_member_indexes, cur_em->em_index);
+
+			/* Delete 'ojrelid' from em_relids */
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
+			rte = root->simple_rte_array[ojrelid];
+			rte->eclass_member_indexes =
+				bms_del_member(rte->eclass_member_indexes, cur_em->em_index);
+
 			if (bms_is_empty(cur_em->em_relids))
+			{
+				/* Delete this EquivalenceMember with updating indexes */
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				ec->ec_member_indexes =
+					bms_del_member(ec->ec_member_indexes, cur_em->em_index);
+				ec->ec_nonchild_indexes =
+					bms_del_member(ec->ec_nonchild_indexes, cur_em->em_index);
+				ec->ec_norel_indexes =
+					bms_del_member(ec->ec_norel_indexes, cur_em->em_index);
+			}
 		}
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -648,7 +673,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 3a45bcc3f4..3c680f9c2e 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1470,6 +1470,7 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
+	int			em_index;		/* index in root->eq_members */
 	Relids		em_relids;		/* relids for this member */
 	bool		em_is_const;	/* is em_relids empty? */
 	bool		em_norel_expr;	/* true if em_expr contains no Vars */
-- 
2.41.0.windows.1

#45Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#44)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On 5/7/2023 16:57, Yuya Watari wrote:

Hello,

On Fri, Mar 10, 2023 at 5:38 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Thank you for pointing it out. I have attached the rebased version to
this email.

Recent commits, such as a8c09daa8b [1], have caused conflicts and
compilation errors in these patches. I have attached the fixed version
to this email.

The v19-0004 adds an 'em_index' field representing the index within
root->eq_members of the EquivalenceMember. This field is needed to
delete EquivalenceMembers when iterating them using the ec_members
list instead of the ec_member_indexes.

[1] https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=a8c09daa8bb1d741bb8b3d31a12752448eb6fb7c

Discovering quality of partition pruning at the stage of execution
initialization and using your set of patches I have found some dubious
results with performance degradation. Look into the test case in attachment.
Here is three queries. Execution times:
1 - 8s; 2 - 30s; 3 - 131s (with your patch set).
1 - 5s; 2 - 10s; 3 - 33s (current master).

Maybe it is a false alarm, but on my laptop I see this degradation at
every launch.

--
regards,
Andrey Lepikhov
Postgres Professional

Attachments:

parts-problem.sqlapplication/sql; name=parts-problem.sqlDownload
#46Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Andrey Lepikhov (#45)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On 27/7/2023 14:58, Andrey Lepikhov wrote:

On 5/7/2023 16:57, Yuya Watari wrote:

Hello,

On Fri, Mar 10, 2023 at 5:38 PM Yuya Watari <watari.yuya@gmail.com>
wrote:

Thank you for pointing it out. I have attached the rebased version to
this email.

Recent commits, such as a8c09daa8b [1], have caused conflicts and
compilation errors in these patches. I have attached the fixed version
to this email.

The v19-0004 adds an 'em_index' field representing the index within
root->eq_members of the EquivalenceMember. This field is needed to
delete EquivalenceMembers when iterating them using the ec_members
list instead of the ec_member_indexes.

[1]
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=a8c09daa8bb1d741bb8b3d31a12752448eb6fb7c

Discovering quality of partition pruning at the stage of execution
initialization and using your set of patches I have found some dubious
results with performance degradation. Look into the test case in
attachment.
Here is three queries. Execution times:
1 - 8s; 2 - 30s; 3 - 131s (with your patch set).
1 - 5s; 2 - 10s; 3 - 33s (current master).

Maybe it is a false alarm, but on my laptop I see this degradation at
every launch.

Sorry for this. It was definitely a false alarm. In this patch,
assertion checking adds much overhead. After switching it off, I found
out that this feature solves my problem with a quick pass through the
members of an equivalence class. Planning time results for the queries
from the previous letter:
1 - 0.4s, 2 - 1.3s, 3 - 1.3s; (with the patches applied)
1 - 5s; 2 - 8.7s; 3 - 22s; (current master).

I have attached flamegraph that shows query 2 planning process after
applying this set of patches. As you can see, overhead at the
equivalence class routines has gone.

--
regards,
Andrey Lepikhov
Postgres Professional

Attachments:

2-Yuya-kernel.svgimage/svg+xml; name=2-Yuya-kernel.svgDownload
#47Yuya Watari
watari.yuya@gmail.com
In reply to: Andrey Lepikhov (#46)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Fri, Jul 28, 2023 at 1:27 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

Sorry for this. It was definitely a false alarm. In this patch,
assertion checking adds much overhead. After switching it off, I found
out that this feature solves my problem with a quick pass through the
members of an equivalence class. Planning time results for the queries
from the previous letter:
1 - 0.4s, 2 - 1.3s, 3 - 1.3s; (with the patches applied)
1 - 5s; 2 - 8.7s; 3 - 22s; (current master).

I have attached flamegraph that shows query 2 planning process after
applying this set of patches. As you can see, overhead at the
equivalence class routines has gone.

I really appreciate testing the patches and sharing your results. The
results are interesting because they show that our optimization
effectively reduces planning time for your workload containing
different queries than I have used in my benchmarks.

Thank you again for reviewing this.

--
Best regards,
Yuya Watari

#48Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Andrey Lepikhov (#46)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hi Yuya, Andrey,

On Fri, Jul 28, 2023 at 9:58 AM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

Discovering quality of partition pruning at the stage of execution
initialization and using your set of patches I have found some dubious
results with performance degradation. Look into the test case in
attachment.
Here is three queries. Execution times:
1 - 8s; 2 - 30s; 3 - 131s (with your patch set).
1 - 5s; 2 - 10s; 3 - 33s (current master).

Maybe it is a false alarm, but on my laptop I see this degradation at
every launch.

Sorry for this. It was definitely a false alarm. In this patch,
assertion checking adds much overhead. After switching it off, I found
out that this feature solves my problem with a quick pass through the
members of an equivalence class. Planning time results for the queries
from the previous letter:
1 - 0.4s, 2 - 1.3s, 3 - 1.3s; (with the patches applied)
1 - 5s; 2 - 8.7s; 3 - 22s; (current master).

I measured planning time using my scripts setup.sql and queries.sql
attached to [1]/messages/by-id/CAExHW5stmOUobE55pMt83r8UxvfCph+Pvo5dNpdrVCsBgXEzDQ@mail.gmail.com with and without assert build using your patch. The
timings are recorded in the attached spreadsheet. I have following
observations

1. The patchset improves the planning time of queries involving
partitioned tables by an integral factor. Both in case of
partitionwise join and without it. The speedup is 5x to 21x in my
experiment. That's huge.
2. There's slight degradation in planning time of queries involving
unpartitioned tables. But I have seen that much variance usually.
3. assert and debug enabled build shows degradation in planning time
in all the cases.
4. There is substantial memory increase in all the cases. It's
percentage wise predominant when the partitionwise join is not used.

Given that most of the developers run assert enabled builds it would
be good to bring down the degradation there while keeping the
excellent speedup in non-assert builds.
Queries on partitioned tables eat a lot of memory anyways, increasing
that further should be avoided.

I have not studied the patches. But I think the memory increase has to
do with our Bitmapset structure. It's space inefficient when there are
thousands of partitions involved. See my comment at [2]/messages/by-id/CAExHW5s4EqY43oB=ne6B2=-xLgrs9ZGeTr1NXwkGFt2j-OmaQQ@mail.gmail.com

[1]: /messages/by-id/CAExHW5stmOUobE55pMt83r8UxvfCph+Pvo5dNpdrVCsBgXEzDQ@mail.gmail.com
[2]: /messages/by-id/CAExHW5s4EqY43oB=ne6B2=-xLgrs9ZGeTr1NXwkGFt2j-OmaQQ@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

Attachments:

planning time measurement.odsapplication/vnd.oasis.opendocument.spreadsheet; name="planning time measurement.ods"Download
#49Yuya Watari
watari.yuya@gmail.com
In reply to: Ashutosh Bapat (#48)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

I really appreciate sharing very useful scripts and benchmarking results.

On Fri, Jul 28, 2023 at 6:51 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

Given that most of the developers run assert enabled builds it would
be good to bring down the degradation there while keeping the
excellent speedup in non-assert builds.

From my observation, this degradation in assert enabled build is
caused by verifying the iteration results of EquivalenceMembers. My
patch uses Bitmapset-based indexes to speed up the iteration. When
assertions are enabled, we verify that the result of the iteration is
the same with and without the indexes. This verification results in
executing a similar loop three times, which causes the degradation. I
measured planning time by using your script without this verification.
The results are as follows:

Master: 144.55 ms
Patched (v19): 529.85 ms
Patched (v19) without verification: 78.84 ms
(*) All runs are with assertions.

As seen from the above, verifying iteration results was the cause of
the performance degradation. I agree that we should avoid such
degradation because it negatively affects the development of
PostgreSQL. Removing the verification when committing this patch is
one possible option.

Queries on partitioned tables eat a lot of memory anyways, increasing
that further should be avoided.

I have not studied the patches. But I think the memory increase has to
do with our Bitmapset structure. It's space inefficient when there are
thousands of partitions involved. See my comment at [2]

Thank you for pointing this out. I have never considered the memory
usage impact of this patch. As you say, the Bitmapset structure caused
this increase. I will try to look into this further.

--
Best regards,
Yuya Watari

#50Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#49)
Re: [PoC] Reducing planning time when tables have many partitions

On 2/8/2023 13:40, Yuya Watari wrote:

As seen from the above, verifying iteration results was the cause of
the performance degradation. I agree that we should avoid such
degradation because it negatively affects the development of
PostgreSQL. Removing the verification when committing this patch is
one possible option.

You introduced list_ptr_cmp as an extern function of a List, but use it
the only under USE_ASSERT_CHECKING ifdef.
Maybe you hide it under USE_ASSERT_CHECKING or remove all the stuff?

--
regards,
Andrey Lepikhov
Postgres Professional

#51Yuya Watari
watari.yuya@gmail.com
In reply to: Andrey Lepikhov (#50)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Aug 2, 2023 at 6:43 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

You introduced list_ptr_cmp as an extern function of a List, but use it
the only under USE_ASSERT_CHECKING ifdef.
Maybe you hide it under USE_ASSERT_CHECKING or remove all the stuff?

Thank you for your quick reply and for pointing that out. If we remove
the verification code when committing this patch, we should also
remove the list_ptr_cmp() function because nobody will use it. If we
don't remove the verification, whether to hide it by
USE_ASSERT_CHECKING is a difficult question. The list_ptr_cmp() can be
used for generic use and is helpful even without assertions, so not
hiding it is one option. However, I understand that it is not pretty
to have the function compiled even though it is not referenced from
anywhere when assertions are disabled. As you say, I think hiding it
by USE_ASSERT_CHECKING is also a possible solution.

--
Best regards,
Yuya Watari

#52Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Yuya Watari (#49)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, Aug 2, 2023 at 12:11 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Hello,

I really appreciate sharing very useful scripts and benchmarking results.

On Fri, Jul 28, 2023 at 6:51 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

Given that most of the developers run assert enabled builds it would
be good to bring down the degradation there while keeping the
excellent speedup in non-assert builds.

From my observation, this degradation in assert enabled build is
caused by verifying the iteration results of EquivalenceMembers. My
patch uses Bitmapset-based indexes to speed up the iteration. When
assertions are enabled, we verify that the result of the iteration is
the same with and without the indexes. This verification results in
executing a similar loop three times, which causes the degradation. I
measured planning time by using your script without this verification.
The results are as follows:

Master: 144.55 ms
Patched (v19): 529.85 ms
Patched (v19) without verification: 78.84 ms
(*) All runs are with assertions.

As seen from the above, verifying iteration results was the cause of
the performance degradation. I agree that we should avoid such
degradation because it negatively affects the development of
PostgreSQL. Removing the verification when committing this patch is
one possible option.

If you think that the verification is important to catch bugs, you may want
to encapsulate it with an #ifdef .. #endif such that the block within is
not compiled by default. See OPTIMIZER_DEBUG for example.

Queries on partitioned tables eat a lot of memory anyways, increasing
that further should be avoided.

I have not studied the patches. But I think the memory increase has to
do with our Bitmapset structure. It's space inefficient when there are
thousands of partitions involved. See my comment at [2]

Thank you for pointing this out. I have never considered the memory
usage impact of this patch. As you say, the Bitmapset structure caused
this increase. I will try to look into this further.

Do you think that the memory measurement patch I have shared in those
threads is useful in itself? If so, I will start another proposal to
address it.

--
Best Wishes,
Ashutosh Bapat

#53Yuya Watari
watari.yuya@gmail.com
In reply to: Ashutosh Bapat (#52)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

Thank you for your reply.

On Thu, Aug 3, 2023 at 10:29 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

If you think that the verification is important to catch bugs, you may want to encapsulate it with an #ifdef .. #endif such that the block within is not compiled by default. See OPTIMIZER_DEBUG for example.

In my opinion, verifying the iteration results is only necessary to
avoid introducing bugs while developing this patch. The verification
is too excessive for regular development of PostgreSQL. I agree that
we should avoid a significant degradation in assert enabled builds, so
I will consider removing it.

Do you think that the memory measurement patch I have shared in those threads is useful in itself? If so, I will start another proposal to address it.

For me, who is developing the planner in this thread, the memory
measurement patch is useful. However, most users do not care about
memory usage, so there is room for consideration. For example, making
the metrics optional in EXPLAIN ANALYZE outputs might be better.

--
Best regards,
Yuya Watari

#54Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#53)
Re: [PoC] Reducing planning time when tables have many partitions

On 7/8/2023 15:19, Yuya Watari wrote:

Hello,

Thank you for your reply.

On Thu, Aug 3, 2023 at 10:29 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

If you think that the verification is important to catch bugs, you may want to encapsulate it with an #ifdef .. #endif such that the block within is not compiled by default. See OPTIMIZER_DEBUG for example.

In my opinion, verifying the iteration results is only necessary to
avoid introducing bugs while developing this patch. The verification
is too excessive for regular development of PostgreSQL. I agree that
we should avoid a significant degradation in assert enabled builds, so
I will consider removing it.

I should admit, these checks has helped me during backpatching this
feature to pg v.13 (users crave speed up of query planning a lot). Maybe
it is a sign of a lack of tests, but in-fact, it already has helped.

One more thing: I think, you should add comments to
add_child_rel_equivalences() and add_child_join_rel_equivalences()
on replacing of:

if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
!bms_is_empty(cur_em->em_relids))
and
if (bms_overlap(cur_em->em_relids, top_parent_relids))

with different logic. What was changed? It will be better to help future
developers realize this part of the code more easily by adding some
comments.

Do you think that the memory measurement patch I have shared in those threads is useful in itself? If so, I will start another proposal to address it.

For me, who is developing the planner in this thread, the memory
measurement patch is useful. However, most users do not care about
memory usage, so there is room for consideration. For example, making
the metrics optional in EXPLAIN ANALYZE outputs might be better.

+1. Any memory-related info in the output of EXPLAIN ANALYZE makes tests
more complex because of architecture dependency.

--
regards,
Andrey Lepikhov
Postgres Professional

#55Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Andrey Lepikhov (#54)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, Aug 7, 2023 at 2:21 PM Andrey Lepikhov <a.lepikhov@postgrespro.ru>
wrote:

Do you think that the memory measurement patch I have shared in those

threads is useful in itself? If so, I will start another proposal to
address it.

For me, who is developing the planner in this thread, the memory
measurement patch is useful. However, most users do not care about
memory usage, so there is room for consideration. For example, making
the metrics optional in EXPLAIN ANALYZE outputs might be better.

+1. Any memory-related info in the output of EXPLAIN ANALYZE makes tests
more complex because of architecture dependency.

As far as the tests go, the same is the case with planning time and
execution time. They change even without changing the architecture. But we
have tests which mask the actual values. Something similar will be done to
the planning memory.

I will propose it as a separate patch in the next commitfest and will seek
opinions from other hackers.

--
Best Wishes,
Ashutosh Bapat

#56Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Ashutosh Bapat (#55)
Re: [PoC] Reducing planning time when tables have many partitions

On 7/8/2023 19:15, Ashutosh Bapat wrote:

On Mon, Aug 7, 2023 at 2:21 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru <mailto:a.lepikhov@postgrespro.ru>> wrote:

Do you think that the memory measurement patch I have shared in

those threads is useful in itself? If so, I will start another
proposal to address it.

For me, who is developing the planner in this thread, the memory
measurement patch is useful. However, most users do not care about
memory usage, so there is room for consideration. For example, making
the metrics optional in EXPLAIN ANALYZE outputs might be better.

+1. Any memory-related info in the output of EXPLAIN ANALYZE makes
tests
more complex because of architecture dependency.

As far as the tests go, the same is the case with planning time and
execution time. They change even without changing the architecture. But
we have tests which mask the actual values. Something similar will be
done to the planning memory.

It is a positive thing to access some planner internals from the
console, of course. My point is dedicated to the structuration of an
EXPLAIN output and is caused by two reasons:
1. I use the EXPLAIN command daily to identify performance issues and
the optimiser's weak points. According to the experience, when you have
an 'explain analyze' containing more than 100 strings, you try removing
unnecessary information to improve observability. It would be better to
have the possibility to see an EXPLAIN with different levels of the
output details. Flexibility here reduces a lot of manual work, sometimes.
2. Writing extensions and having an explain analyze in the regression
test, we must create masking functions just to make the test more
stable. That additional work can be avoided with another option, like
MEMUSAGE ON/OFF.

So, in my opinion, it would be better to introduce this new output data
guarded by additional option.

I will propose it as a separate patch in the next commitfest and will
seek opinions from other hackers.

Cool, good news.

--
regards,
Andrey Lepikhov
Postgres Professional

#57Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Andrey Lepikhov (#56)
Re: [PoC] Reducing planning time when tables have many partitions

Hi Andrey,

On Tue, Aug 8, 2023 at 8:52 AM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

It is a positive thing to access some planner internals from the
console, of course. My point is dedicated to the structuration of an
EXPLAIN output and is caused by two reasons:
1. I use the EXPLAIN command daily to identify performance issues and
the optimiser's weak points. According to the experience, when you have
an 'explain analyze' containing more than 100 strings, you try removing
unnecessary information to improve observability. It would be better to
have the possibility to see an EXPLAIN with different levels of the
output details. Flexibility here reduces a lot of manual work, sometimes.

I use the json output format to extract the interesting parts of
EXPLAIN output. See my SQL scripts attached upthread. That way I can
ignore new additions like this.

2. Writing extensions and having an explain analyze in the regression
test, we must create masking functions just to make the test more
stable. That additional work can be avoided with another option, like
MEMUSAGE ON/OFF.

We already have a masking function in-place. See changes to
explain.out in my proposed patch at [1]/messages/by-id/CAExHW5sZA=5LJ_ZPpRO-w09ck8z9p7eaYAqq3Ks9GDfhrxeWBw@mail.gmail.com

I will propose it as a separate patch in the next commitfest and will
seek opinions from other hackers.

Cool, good news.

Done. Commitfest entry https://commitfest.postgresql.org/44/4492/

[1]: /messages/by-id/CAExHW5sZA=5LJ_ZPpRO-w09ck8z9p7eaYAqq3Ks9GDfhrxeWBw@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

#58Yuya Watari
watari.yuya@gmail.com
In reply to: Ashutosh Bapat (#57)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Andrey, Ashutosh, and David,

Thank you for your reply and for reviewing the patch.

On Mon, Aug 7, 2023 at 5:51 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

One more thing: I think, you should add comments to
add_child_rel_equivalences() and add_child_join_rel_equivalences()
on replacing of:

if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
!bms_is_empty(cur_em->em_relids))
and
if (bms_overlap(cur_em->em_relids, top_parent_relids))

with different logic. What was changed? It will be better to help future
developers realize this part of the code more easily by adding some
comments.

The following change in add_child_join_rel_equivalences():

- /* Does this member reference child's topmost parent rel? */
- if (bms_overlap(cur_em->em_relids, top_parent_relids))

is correct because EquivalenceMemberIterator guarantees that these two
Relids always overlap for the iterated results. The following code
does this iteration. As seen from the below code, the iteration
eliminates not overlapping Relids, so we do not need to check
bms_overlap() for the iterated results.

=====
/*
* eclass_member_iterator_next
* Fetch the next EquivalenceMember from an EquivalenceMemberIterator
* which was set up by setup_eclass_member_iterator(). Returns NULL when
* there are no more matching EquivalenceMembers.
*/
EquivalenceMember *
eclass_member_iterator_next(EquivalenceMemberIterator *iter)
{
...
ListCell *lc;

for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
{
EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
...
/*
* Don't return members which have no common rels with with_relids
*/
if (!bms_overlap(em->em_relids, iter->with_relids))
continue;

return em;
}
return NULL;
...
}
=====

I agree with your opinion that my patch lacks some explanations, so I
will consider adding more comments. However, I received the following
message from David in March.

On Thu, Mar 9, 2023 at 6:23 AM David Rowley <dgrowleyml@gmail.com> wrote:

For the main patch, I've been starting to wonder if it should work
completely differently. Instead of adding members for partitioned and
inheritance children, we could just translate the Vars from child to
top-level parent and find the member that way. I wondered if this
method might be even faster as it would forego
add_child_rel_equivalences(). I think we'd still need em_is_child for
UNION ALL children. So far, I've not looked into this in detail. I
was hoping to find an idea that would allow some means to have the
planner realise that a LIST partition which allows a single Datum
could skip pushing base quals which are constantly true. i.e:

create table lp (a int) partition by list(a);
create table lp1 partition of lp for values in(1);
explain select * from lp where a = 1;

Seq Scan on lp1 lp (cost=0.00..41.88 rows=13 width=4)
Filter: (a = 1)

I am concerned that fixing the current patch will conflict with
David's idea. Of course, I am now trying to experiment with the above
idea, but I should avoid the conflict if he is working on this. David,
what do you think about this? Is it OK to post a new patch to address
the review comments? I am looking forward to your reply.

--
Best regards,
Yuya Watari

#59David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#44)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 5 Jul 2023 at 21:58, Yuya Watari <watari.yuya@gmail.com> wrote:

Hello,

On Fri, Mar 10, 2023 at 5:38 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Thank you for pointing it out. I have attached the rebased version to
this email.

Recent commits, such as a8c09daa8b [1], have caused conflicts and
compilation errors in these patches. I have attached the fixed version
to this email.

The v19-0004 adds an 'em_index' field representing the index within
root->eq_members of the EquivalenceMember. This field is needed to
delete EquivalenceMembers when iterating them using the ec_members
list instead of the ec_member_indexes.

If 0004 is adding an em_index to mark the index into
PlannerInfo->eq_members, can't you use that in
setup_eclass_member[_strict]_iterator to loop to verify that the two
methods yield the same result?

i.e:

+ Bitmapset *matching_ems = NULL;
+ memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+ memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+ idx_iter.use_index = true;
+ noidx_iter.use_index = false;
+
+ while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+     matching_ems = bms_add_member(matching_ems, em->em_index);
+
+ Assert(bms_equal(matching_ems, iter->matching_ems));

That should void the complaint that the Assert checking is too slow.
You can also delete the list_ptr_cmp function too (also noticed a
complaint about that).

For the 0003 patch. Can you explain why you think these fields should
be in RangeTblEntry rather than RelOptInfo? I can only guess you might
have done this for memory usage so that we don't have to carry those
fields for join rels? I think RelOptInfo is the correct place to
store fields that are only used in the planner. If you put them in
RangeTblEntry they'll end up in pg_rewrite and be stored for all
views. Seems very space inefficient and scary as it limits the scope
for fixing bugs in back branches due to RangeTblEntries being
serialized into the catalogues in various places.

David

#60David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#59)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 9 Aug 2023 at 22:28, David Rowley <dgrowleyml@gmail.com> wrote:

i.e:

+ Bitmapset *matching_ems = NULL;
+ memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+ memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+ idx_iter.use_index = true;
+ noidx_iter.use_index = false;
+
+ while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+     matching_ems = bms_add_member(matching_ems, em->em_index);
+
+ Assert(bms_equal(matching_ems, iter->matching_ems));

Slight correction, you could just get rid of idx_iter completely. I
only added that copy since the Assert code needed to iterate and I
didn't want to change the position of the iterator that's actually
being used. Since the updated code wouldn't be interesting over
"iter", you could just use "iter" directly like I have in the
Assert(bms_equals... code above.

David

#61David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#58)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 9 Aug 2023 at 20:15, Yuya Watari <watari.yuya@gmail.com> wrote:

I agree with your opinion that my patch lacks some explanations, so I
will consider adding more comments. However, I received the following
message from David in March.

On Thu, Mar 9, 2023 at 6:23 AM David Rowley <dgrowleyml@gmail.com> wrote:

For the main patch, I've been starting to wonder if it should work
completely differently. Instead of adding members for partitioned and
inheritance children, we could just translate the Vars from child to
top-level parent and find the member that way. I wondered if this
method might be even faster as it would forego
add_child_rel_equivalences(). I think we'd still need em_is_child for
UNION ALL children. So far, I've not looked into this in detail. I
was hoping to find an idea that would allow some means to have the
planner realise that a LIST partition which allows a single Datum
could skip pushing base quals which are constantly true. i.e:

create table lp (a int) partition by list(a);
create table lp1 partition of lp for values in(1);
explain select * from lp where a = 1;

Seq Scan on lp1 lp (cost=0.00..41.88 rows=13 width=4)
Filter: (a = 1)

I am concerned that fixing the current patch will conflict with
David's idea. Of course, I am now trying to experiment with the above
idea, but I should avoid the conflict if he is working on this. David,
what do you think about this? Is it OK to post a new patch to address
the review comments? I am looking forward to your reply.

So, I have three concerns with this patch.

1) I really dislike the way eclass_member_iterator_next() has to check
bms_overlap() to filter out unwanted EMs. This is required because of
how add_child_rel_equivalences() does not pass the "relids" parameter
in add_eq_member() as equivalent to pull_varnos(expr). See this code
in master:

/*
* Transform em_relids to match. Note we do *not* do
* pull_varnos(child_expr) here, as for example the
* transformation might have substituted a constant, but we
* don't want the child member to be marked as constant.
*/
new_relids = bms_difference(cur_em->em_relids,
top_parent_relids);
new_relids = bms_add_members(new_relids, child_relids);

I understand this is done to support Consts in UNION ALL parents, e.g
the following query prunes the n=2 UNION ALL branch

postgres=# explain select * from (select 1 AS n,* from pg_Class c1
union all select 2 AS n,* from pg_Class c2) where n=1;
QUERY PLAN
----------------------------------------------------------------
Seq Scan on pg_class c1 (cost=0.00..18.13 rows=413 width=277)
(1 row)

... but the following (existing) comment is just a lie:

Relids em_relids; /* all relids appearing in em_expr */

This means that there's some weirdness on which RelOptInfos we set
eclass_member_indexes. Do we just set the EM in the RelOptInfos
mentioned in the em_expr, or should it be the ones in em_relids?

You can see the following code I wrote in the 0001 patch which tries
to work around this problem:

+ /*
+ * We must determine the exact set of relids in the expr for child
+ * EquivalenceMembers as what is given to us in 'relids' may differ from
+ * the relids mentioned in the expression.  See add_child_rel_equivalences
+ */
+ if (parent != NULL)
+ expr_relids = pull_varnos(root, (Node *) expr);
+ else
+ {
+ expr_relids = relids;
+ /* We expect the relids to match for non-child members */
+ Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+ }

So, you can see we go with the relids from the em_expr rather than
what's mentioned in em_relids. I believe this means we need the
following line:

+ /*
+ * Don't return members which have no common rels with with_relids
+ */
+ if (!bms_overlap(em->em_relids, iter->with_relids))
+ continue;

I don't quite recall if the em_expr can mention relids that are not in
em_relids or not or if em_expr's relids always is a subset of
em_relids.

I'm just concerned this adds complexity and the risk of mixing up the
meaning (even more than it is already in master). I'm not sure I'm
confident that all this is correct, and I wrote the 0001 patch.

Maybe this can be fixed by changing master so that em_relids always
matches pull_varnos(em_expr)? I'm unsure if there are any other
complexities other than having to ensure we don't set em_is_const for
child members.

2) The 2nd reason is what I hinted at that you quoted in the email I
sent you in March. I think if it wasn't for UNION ALL and perhaps
table inheritance and we only needed child EMs for partitions of
partitioned tables, then I think we might be able to get away with
just translating Exprs child -> parent before looking up the EM and
likewise when asked to get join quals for child rels, we'd translate
the child relids to their top level parents, find the quals then
translate those back to child form again. EquivalenceClasses would
then only contain a few members and there likely wouldn't be a great
need to do any indexing like we are in the 0001 patch. I'm sure
someone somewhere probably has a query that would go faster with them,
but it's likely going to be rare therefore probably not worth it.

Unfortunately, I'm not smart enough to just tell you this will or will
not work just off hand. The UNION ALL branch pruning adds complexity
that I don't recall the details of. To know, someone would either
need to tell me, or I'd need to go try to make it work myself and then
discover the reason it can't be made to work. I'm happy for you to try
this, but if you don't I'm not sure when I can do it. I think it
would need to be at least explored before I'd ever consider thinking
about committing this patch.

3) I just don't like the way the patch switches between methods of
looking up EMs as it means we could return EMs in a different order
depending on something like how many partitions were pruned or after
the DBA does ATTACH PARTITION. That could start causing weird
problems like plan changes due to a change in which columns were
selected in generate_implied_equalities_for_column(). I don't have
any examples of actual problems, but it's pretty difficult to prove
there aren't any.

Of course, I do recall the complaint about the regression for more
simple queries and that's why I wrote the iterator code to have it use
the linear search when the number of EMs is small, so we can't exactly
just delete the linear search method as we'd end up with that
performance regression again.

I think the best way to move this forward is to explore not putting
partitioned table partitions in EMs and instead see if we can
translate to top-level parent before lookups. This might just be too
complex to translate the Exprs all the time and it may add overhead
unless we can quickly determine somehow that we don't need to attempt
to translate the Expr when the given Expr is already from the
top-level parent. If that can't be made to work, then maybe that shows
the current patch has merit.

David

#62Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#61)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

I really appreciate your quick reply.

On Wed, Aug 9, 2023 at 7:28 PM David Rowley <dgrowleyml@gmail.com> wrote:

If 0004 is adding an em_index to mark the index into
PlannerInfo->eq_members, can't you use that in
setup_eclass_member[_strict]_iterator to loop to verify that the two
methods yield the same result?

i.e:

+ Bitmapset *matching_ems = NULL;
+ memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+ memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+ idx_iter.use_index = true;
+ noidx_iter.use_index = false;
+
+ while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+     matching_ems = bms_add_member(matching_ems, em->em_index);
+
+ Assert(bms_equal(matching_ems, iter->matching_ems));

That should void the complaint that the Assert checking is too slow.
You can also delete the list_ptr_cmp function too (also noticed a
complaint about that).

Thanks for sharing your idea regarding this verification. It looks
good to solve the degradation problem in assert-enabled builds. I will
try it.

For the 0003 patch. Can you explain why you think these fields should
be in RangeTblEntry rather than RelOptInfo? I can only guess you might
have done this for memory usage so that we don't have to carry those
fields for join rels? I think RelOptInfo is the correct place to
store fields that are only used in the planner. If you put them in
RangeTblEntry they'll end up in pg_rewrite and be stored for all
views. Seems very space inefficient and scary as it limits the scope
for fixing bugs in back branches due to RangeTblEntries being
serialized into the catalogues in various places.

This change was not made for performance reasons but to avoid null
reference exceptions. The details are explained in my email [1]/messages/by-id/CAJ2pMkYR_X-=pq+39-W5kc0OG7q9u5YUwDBCHnkPur17DXnxuQ@mail.gmail.com. In
brief, the earlier patch did not work because simple_rel_array[i]
could be NULL for some 'i', and we referenced such a RelOptInfo. For
example, the following code snippet in add_eq_member() does not work.
I inserted "Assert(rel != NULL)" into this code, and then the
assertion failed. So, I moved the indexes to RangeTblEntry to address
this issue, but I don't know if this solution is good. We may have to
solve this in a different way.

=====
@@ -572,9 +662,31 @@ add_eq_member(EquivalenceClass *ec, Expr *expr,
Relids relids,
+    i = -1;
+    while ((i = bms_next_member(expr_relids, i)) >= 0)
+    {
+        RelOptInfo *rel = root->simple_rel_array[i];
+
+        rel->eclass_member_indexes =
bms_add_member(rel->eclass_member_indexes, em_index);
+    }
=====

On Wed, Aug 9, 2023 at 8:54 PM David Rowley <dgrowleyml@gmail.com> wrote:

So, I have three concerns with this patch.

I think the best way to move this forward is to explore not putting
partitioned table partitions in EMs and instead see if we can
translate to top-level parent before lookups. This might just be too
complex to translate the Exprs all the time and it may add overhead
unless we can quickly determine somehow that we don't need to attempt
to translate the Expr when the given Expr is already from the
top-level parent. If that can't be made to work, then maybe that shows
the current patch has merit.

I really appreciate your detailed advice. I am sorry that I will not
be able to respond for a week or two due to my vacation, but I will
explore and work on these issues. Thanks again for your kind reply.

[1]: /messages/by-id/CAJ2pMkYR_X-=pq+39-W5kc0OG7q9u5YUwDBCHnkPur17DXnxuQ@mail.gmail.com

--
Best regards,
Yuya Watari

#63Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#62)
3 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Aug 9, 2023 at 8:54 PM David Rowley <dgrowleyml@gmail.com> wrote:

I think the best way to move this forward is to explore not putting
partitioned table partitions in EMs and instead see if we can
translate to top-level parent before lookups. This might just be too
complex to translate the Exprs all the time and it may add overhead
unless we can quickly determine somehow that we don't need to attempt
to translate the Expr when the given Expr is already from the
top-level parent. If that can't be made to work, then maybe that shows
the current patch has merit.

Based on your suggestion, I have experimented with not putting child
EquivalenceMembers in an EquivalenceClass. I have attached a new
patch, v20, to this email. The following is a summary of v20.

* v20 has been written from scratch.
* In v20, EquivalenceClass->ec_members no longer has any child
members. All of ec_members are now non-child. Instead, the child
EquivalenceMembers are in the RelOptInfos.
* When child EquivalenceMembers are required, 1) we translate the
given Relids to their top-level parents, and 2) if some parent
EquivalenceMembers' Relids match the translated top-level ones, we get
the child members from the RelOptInfo.
* With the above change, ec_members has a few members, which leads to
a significant performance improvement. This is the core part of the
v20 optimization.
* My experimental results show that v20 performs better for both small
and large sizes. For small sizes, v20 is clearly superior to v19. For
large sizes, v20 performs as well as v19.
* At this point, I don't know if we should switch to the v20 method.
v20 is just a new proof of concept with much room for improvement. It
is important to compare two different methods of v19 and v20 and
discuss the best strategy.

1. Key idea of v20

I have attached a patch series consisting of two patches. v20-0001 and
v20-0002 are for optimizations regarding EquivalenceClasses and
RestrictInfos, respectively. v20-0002 is picked up from v19. Most of
my new optimizations are in v20-0001.

As I wrote above, the main change in v20-0001 is that we don't add
child EquivalenceMembers to ec_members. I will describe how v20 works.
First of all, take a look at the code of get_eclass_for_sort_expr().
Its comments are helpful for understanding my idea. Traditionally, we
have searched EquivalenceMembers matching the request as follows. This
was a very slow linear search when there were many members in the
list.

===== Master =====
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);

/*
* Ignore child members unless they match the request.
*/
if (cur_em->em_is_child &&
!bms_equal(cur_em->em_relids, rel))
continue;

/*
* Match constants only within the same JoinDomain (see
* optimizer/README).
*/
if (cur_em->em_is_const && cur_em->em_jdomain != jdomain)
continue;

if (opcintype == cur_em->em_datatype &&
equal(expr, cur_em->em_expr))
return cur_ec; /* Match! */
}
==================

v20 addressed this problem by not adding child members to ec_members.
Since there are few members in the list, we can speed up the search.
Of course, we still need child members. Previously, child members have
been made and added to ec_members in
add_child_[join_]rel_equivalences(). Now, in v20, we add them to
child_[join]rel instead of ec_members. The following is the v20's
change.

===== v20 =====
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
top_parent_relids);
new_relids = bms_add_members(new_relids, child_relids);

-   (void) add_eq_member(cur_ec, child_expr, new_relids,
-                        cur_em->em_jdomain,
-                        cur_em, cur_em->em_datatype);
+   child_em = make_eq_member(cur_ec, child_expr, new_relids,
+                             cur_em->em_jdomain,
+                             cur_em, cur_em->em_datatype);
+   child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+                                             child_em);
+
+   /*
+    * We save the knowledge that 'child_em' can be translated from
+    * 'child_rel'. This knowledge is useful for
+    * add_transformed_child_version() 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);
===============

In many places, we need child EquivalenceMembers that match the given
Relids. To get them, we first find the top-level parents of the given
Relids by calling find_relids_top_parents(). find_relids_top_parents()
replaces all of the Relids as their top-level parents. During looping
over ec_members, we check if the children of an EquivalenceMember can
match the request (top-level parents are needed in this checking). If
the children can match, we get child members from RelOptInfos. These
techniques are the core of the v20 solution. The next change does what
I mentioned now.

===== v20 =====
@@ -599,6 +648,17 @@ 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.
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue;

-        foreach(lc2, cur_ec->ec_members)
+        /*
+         * When we have to see child EquivalenceMembers, we get and add them to
+         * 'members'. We cannot use foreach() because the 'members' may be
+         * modified during iteration.
+         */
+        members = cur_ec->ec_members;
+        modified = false;
+        for (i = 0; i < list_length(members); i++)
         {
-            EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+            EquivalenceMember *cur_em =
list_nth_node(EquivalenceMember, members, i);
+
+            /*
+             * If child EquivalenceMembers may match the request, we add and
+             * iterate over them.
+             */
+            if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+                bms_equal(cur_em->em_relids, top_parent_rel))
+                add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+                                                   &members, &modified);
             /*
              * Ignore child members unless they match the request.
              */
-            if (cur_em->em_is_child &&
-                !bms_equal(cur_em->em_relids, rel))
-                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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));

/*
* Match constants only within the same JoinDomain (see
===============

The main concern was the overhead of getting top-level parents. If the
given Relids are already top-level, such an operation can be a major
bottleneck. I addressed this issue with a simple null check. v20 saves
top-level parent Relids to PlannerInfo's array. If there are no
children, v20 sets this array to null, and find_relids_top_parents()
can quickly conclude that the given Relids are top-level. For more
details, see the find_relids_top_parents() in pathnode.h (partially
quoted below).

===== v20 =====
@@ -323,6 +323,24 @@ extern Relids min_join_parameterization(PlannerInfo *root,
+#define find_relids_top_parents(root, relids) \
+    (likely((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);
===============

2. Experimental results

I conducted experiments to test the performance of v20.

2.1. Small size cases (make installcheck)

When I worked with you on optimizing Bitmapset operations, we used
'make installcheck' to check degradation in planning [1]/messages/by-id/CAApHDvo68m_0JuTHnEHFNsdSJEb2uPphK6BWXStj93u_QEi2rg@mail.gmail.com. I did the
same for v19 and v20. Figure 1 and Tables 1 and 2 are the results.
They show that v20 is clearly superior to v19. The degradation of v20
was only 0.5%, while that of v19 was 2.1%. Figure 1 shows that the
0.5% slowdown is much smaller than its variance.

Table 1: Total Planning Time for installcheck (seconds)
-----------------------------------------
| Mean | Median | Stddev
-----------------------------------------
Master | 2.505161 | 2.503110 | 0.019775
v19 | 2.558466 | 2.558560 | 0.017320
v20 | 2.517806 | 2.516081 | 0.016932
-----------------------------------------

Table 2: Speedup for installcheck (higher is better)
----------------------
| Mean | Median
----------------------
v19 | 97.9% | 97.8%
v20 | 99.5% | 99.5%
----------------------

2.2. Large size cases (queries A and B)

I evaluated v20 with the same queries I have used in this thread. The
queries, Queries A and B, are attached in [2]/messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com. Both queries join
partitioned tables. Figures 2 and 3 and the following tables show the
results. v20 performed as well as v19 for large sizes. v20 achieved a
speedup of about x10. There seems to be some regression for small
sizes.

Table 3: Planning time of Query A
(n: the number of partitions of each table)
(lower is better)
------------------------------------------
n | Master (ms) | v19 (ms) | v20 (ms)
------------------------------------------
1 | 0.713 | 0.730 | 0.737
2 | 0.792 | 0.814 | 0.815
4 | 0.955 | 0.982 | 0.987
8 | 1.291 | 1.299 | 1.335
16 | 1.984 | 1.951 | 1.992
32 | 3.991 | 3.720 | 3.778
64 | 7.701 | 6.003 | 6.891
128 | 21.118 | 13.988 | 12.861
256 | 77.405 | 37.091 | 37.294
384 | 166.122 | 56.748 | 57.130
512 | 316.650 | 79.942 | 78.692
640 | 520.007 | 94.030 | 93.772
768 | 778.314 | 123.494 | 123.207
896 | 1182.477 | 185.422 | 179.266
1024 | 1547.897 | 161.104 | 155.761
------------------------------------------

Table 4: Speedup of Query A (higher is better)
------------------------
n | v19 | v20
------------------------
1 | 97.7% | 96.7%
2 | 97.3% | 97.2%
4 | 97.3% | 96.8%
8 | 99.4% | 96.7%
16 | 101.7% | 99.6%
32 | 107.3% | 105.6%
64 | 128.3% | 111.8%
128 | 151.0% | 164.2%
256 | 208.7% | 207.6%
384 | 292.7% | 290.8%
512 | 396.1% | 402.4%
640 | 553.0% | 554.5%
768 | 630.2% | 631.7%
896 | 637.7% | 659.6%
1024 | 960.8% | 993.8%
------------------------

Table 5: Planning time of Query B
-----------------------------------------
n | Master (ms) | v19 (ms) | v20 (ms)
-----------------------------------------
1 | 37.044 | 38.062 | 37.614
2 | 35.839 | 36.804 | 36.555
4 | 38.202 | 37.864 | 37.977
8 | 42.292 | 41.023 | 41.210
16 | 51.867 | 46.481 | 46.477
32 | 80.003 | 57.329 | 57.363
64 | 185.212 | 87.124 | 88.528
128 | 656.116 | 157.236 | 160.884
256 | 2883.258 | 343.035 | 340.285
-----------------------------------------

Table 6: Speedup of Query B (higher is better)
-----------------------
n | v19 | v20
-----------------------
1 | 97.3% | 98.5%
2 | 97.4% | 98.0%
4 | 100.9% | 100.6%
8 | 103.1% | 102.6%
16 | 111.6% | 111.6%
32 | 139.6% | 139.5%
64 | 212.6% | 209.2%
128 | 417.3% | 407.8%
256 | 840.5% | 847.3%
-----------------------

3. Future works

3.1. Redundant memory allocation of Lists

When we need child EquivalenceMembers in a loop over ec_members, v20
adds them to the list. However, since we cannot modify the ec_members,
v20 always copies it. In most cases, there are only one or two child
members, so this behavior is a waste of memory and time and not a good
idea. I didn't address this problem in v20 because doing so could add
much complexity to the code, but it is one of the major future works.

I suspect that the degradation of Queries A and B is due to this
problem. The difference between 'make installcheck' and Queries A and
B is whether there are partitioned tables. Most of the tests in 'make
installcheck' do not have partitions, so find_relids_top_parents()
could immediately determine the given Relids are already top-level and
keep degradation very small. However, since Queries A and B have
partitions, too frequent allocations of Lists may have caused the
regression. I hope we can reduce the degradation by avoiding these
memory allocations. I will continue to investigate and fix this
problem.

3.2. em_relids and pull_varnos

I'm sorry that v20 did not address your 1st concern regarding
em_relids and pull_varnos. I will try to look into this.

3.3. Indexes for RestrictInfos

Indexes for RestrictInfos are still in RangeTblEntry in v20-0002. I
will also investigate this issue.

3.4. Correctness

v20 has passed all regression tests in my environment, but I'm not so
sure if v20 is correct.

4. Conclusion

I wrote v20 based on a new idea. It may have a lot of problems, but it
has advantages. At least it solves your 3rd concern. Since we iterate
Lists instead of Bitmapsets, we don't have to introduce an iterator
mechanism. My experiment showed that the 'make installcheck'
degradation was very small. For the 2nd concern, v20 no longer adds
child EquivalenceMembers to ec_members. I'm sorry if this is not what
you intended, but it effectively worked. Again, v20 is a new proof of
concept. I hope the v20-based approach will be a good alternative
solution if we can overcome several problems, including what I
mentioned above.

[1]: /messages/by-id/CAApHDvo68m_0JuTHnEHFNsdSJEb2uPphK6BWXStj93u_QEi2rg@mail.gmail.com
[2]: /messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

v20-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v20-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 8925ee908485b3202ba37fc6506d387965d963a0 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v20 1/2] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c       |  28 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 414 ++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |  40 ++-
 src/backend/optimizer/path/pathkeys.c     |   9 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +-
 src/backend/optimizer/plan/createplan.c   |  57 +--
 src/backend/optimizer/util/inherit.c      |  14 +
 src/backend/optimizer/util/relnode.c      |  88 +++++
 src/include/nodes/pathnodes.h             |  26 ++
 src/include/optimizer/pathnode.h          |  18 +
 src/include/optimizer/paths.h             |   9 +-
 12 files changed, 603 insertions(+), 116 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..bd02cadcae 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7690,11 +7690,26 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Relids		top_parent_rel_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
 
-	foreach(lc, ec->ec_members)
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_rel_relids))
+			add_child_rel_equivalences_to_list(root, ec, em,
+											   rel->relids,
+											   &members, &modified);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7705,6 +7720,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -7758,9 +7775,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 955286513d..507c93d8b8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,6 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_norel_members);
+	WRITE_NODE_FIELD(ec_rel_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e2..7873548b25 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,12 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  EquivalenceMember *parent_em,
+										  RelOptInfo *child_rel,
+										  List **list,
+										  bool *modified);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -342,6 +352,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_norel_members = list_concat(ec1->ec_norel_members,
+											ec2->ec_norel_members);
+		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
+										  ec2->ec_rel_members);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -355,6 +369,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_norel_members = NIL;
+		ec2->ec_rel_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -374,7 +390,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +407,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,6 +428,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_norel_members = NIL;
+		ec->ec_rel_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -423,9 +441,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +529,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +547,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,8 +569,34 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/*
+	 * The exact set of relids in the expr for non-child EquivalenceMembers
+	 * as what is given to us in 'relids' should be the same as the relids
+	 * mentioned in the expression.  See add_child_rel_equivalences.
+	 */
+	/* XXX We need PlannerInfo to use the following assertion */
+	/* Assert(bms_equal(pull_varnos(root, (Node *) expr), relids)); */
+	if (bms_is_empty(relids))
+		ec->ec_norel_members = lappend(ec->ec_norel_members, em);
+	else
+		ec->ec_rel_members = lappend(ec->ec_rel_members, em);
+
 	return em;
 }
 
@@ -599,6 +648,17 @@ 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.
@@ -617,7 +677,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * When we have to see child EquivalenceMembers, we get and add them to
+		 * 'members'. We cannot use foreach() because the 'members' may be
+		 * modified during iteration.
+		 */
+		members = cur_ec->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them.
+			 */
+			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+												   &members, &modified);
 
 			/*
 			 * Ignore child members unless they match the request.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +735,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		if (unlikely(modified))
+			list_free(members);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -671,6 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_norel_members = NIL;
+	newec->ec_rel_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -691,7 +776,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +842,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -779,6 +873,12 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -796,6 +896,8 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -828,11 +930,20 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -843,6 +954,12 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -876,6 +993,8 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -939,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1138,7 +1257,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_norel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
@@ -1222,7 +1341,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_rel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
@@ -1559,7 +1678,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1695,19 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_rel_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+			bms_is_subset(cur_em->em_relids, top_parent_join_relids))
+			add_child_rel_equivalences_to_list(root, ec, cur_em, join_relids,
+											   &members, &modified);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1589,6 +1724,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1606,6 +1743,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1680,6 +1818,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -2177,7 +2316,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2285,7 +2424,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_rel_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
 			Assert(!coal_em->em_is_child);	/* no children yet */
@@ -2330,7 +2469,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2385,6 +2524,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
+			/* XXX performance of list_delete_ptr()?? */
+			cur_ec->ec_rel_members = list_delete_ptr(cur_ec->ec_rel_members,
+													 coal_em);
 			return true;
 		}
 
@@ -2455,8 +2598,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2523,13 +2666,13 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		foreach(lc2, ec->ec_members)
+		foreach(lc2, ec->ec_rel_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2626,6 +2769,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2638,7 +2782,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2651,15 +2794,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2672,8 +2809,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2689,6 +2826,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_rel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
@@ -2749,6 +2898,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2770,7 +2920,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2783,15 +2932,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2800,8 +2943,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2816,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2846,9 +2990,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_joinrel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2856,6 +3012,106 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the list.
+ *
+ * This function is expected to be called only from
+ * add_child_rel_equivalences_to_list().
+ */
+static void
+add_transformed_child_version(PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel,
+							  List **list,
+							  bool *modified)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the given list has been modified, we need to
+		 * make a copy of it.
+		 */
+		if (!*modified)
+		{
+			*list = list_copy(*list);
+			*modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		*list = lappend(*list, child_em);
+	}
+}
+
+/*
+ * add_child_rel_equivalences_to_list
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the list.
+ *
+ * The transformation is done in add_transformed_child_version().
+ *
+ * This function will not change the original *list but will always make a copy
+ * of it when we need to add transformed members. *modified will be true if the
+ * *list is replaced with a newly allocated one. In such a case, the caller can
+ * (or must) free the *list.
+ */
+void
+add_child_rel_equivalences_to_list(PlannerInfo *root,
+								   EquivalenceClass *ec,
+								   EquivalenceMember *parent_em,
+								   Relids child_relids,
+								   List **list,
+								   bool *modified)
+{
+	int		i;
+	Relids	matching_relids;
+
+	/* The given EquivalenceMember should be parent */
+	Assert(!parent_em->em_is_child);
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		RelOptInfo *child_rel = root->simple_rel_array[i];
+
+		Assert(child_rel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_rel,
+									  list, modified);
+	}
+	bms_free(matching_relids);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_joinrel,
+									  list, modified);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2889,7 +3145,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids, top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2898,6 +3154,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2910,6 +3169,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,15 +3193,25 @@ 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.)
 		 */
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = cur_ec->ec_rel_members;
+		modified = false;
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		for (j = 0; j < list_length(members); j++)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = list_nth_node(EquivalenceMember, members, j);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel_relids))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel->relids,
+												   &members, &modified);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		if (!cur_em)
 			continue;
@@ -2954,8 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3173,8 +3445,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6a93d767a5..b6a27268f5 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3088,7 +3092,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3114,33 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = pathkey->pk_eclass->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember, members, i);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+				bms_equal(member->em_relids, top_parent_index_relids))
+				add_child_rel_equivalences_to_list(root, pathkey->pk_eclass, member,
+												   index->rel->relids,
+												   &members, &modified);
+
 			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
+			if (member->em_is_child ||
+				!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
@@ -3145,6 +3169,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index e53ea84224..5933a83ae0 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -950,8 +950,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1477,8 +1477,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 5f3cce873a..49675f9bbc 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -39,7 +39,7 @@ static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -522,7 +522,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -607,7 +607,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
 
@@ -631,7 +632,14 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
 			if (bms_is_empty(cur_em->em_relids))
+			{
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				/* XXX performance of list_delete_ptr()?? */
+				ec->ec_norel_members = list_delete_ptr(ec->ec_norel_members,
+													   cur_em);
+				ec->ec_rel_members = list_delete_ptr(ec->ec_rel_members,
+													 cur_em);
+			}
 		}
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 34ca6d4ac2..d67d549b1c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4498,7 +4502,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4512,7 +4516,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6133,7 +6137,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6200,7 +6204,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6244,7 +6248,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6315,7 +6319,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6324,7 +6329,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6350,8 +6355,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6360,7 +6366,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6717,7 +6723,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6780,7 +6787,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 94de855a22..3c652a52a0 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -460,6 +460,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Oid			parentOID = RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -572,6 +573,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 76dad17e33..cfd1839ee9 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -122,11 +122,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -147,6 +150,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -174,11 +198,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids are
+		 * top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -232,6 +268,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -609,6 +646,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -720,6 +763,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -916,6 +960,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1477,6 +1522,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1517,6 +1563,48 @@ 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 == 0)
+			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 5702fbba60..f83ddbacf5 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * append_rel_array is the same length as simple_rel_array and holds the
+	 * top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -936,6 +944,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1368,6 +1387,10 @@ 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_norel_members;	/* list of EquivalenceMembers whose
+									 * em_relids is empty */
+	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
+								 * em_relids is not empty */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1422,6 +1445,9 @@ 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);
 } EquivalenceMember;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 6e557bebc4..cae4570823 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -323,6 +323,24 @@ 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) \
+	(likely((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 50bc3b503a..6ab9593d75 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -174,6 +175,12 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
 											AppendRelInfo **appinfos,
 											RelOptInfo *parent_joinrel,
 											RelOptInfo *child_joinrel);
+extern void add_child_rel_equivalences_to_list(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   EquivalenceMember *parent_em,
+											   Relids child_relids,
+											   List **list,
+											   bool *modified);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
-- 
2.42.0.windows.1

v20-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v20-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From 041bc748b7c06242a991907e000831775b2bea27 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v20 2/2] Introduce indexes for RestrictInfo

This change was picked up from the previous patch, v19.
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 338 +++++++++++++++++++---
 src/backend/optimizer/plan/analyzejoins.c |   8 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  47 ++-
 src/include/optimizer/paths.h             |  15 +-
 11 files changed, 383 insertions(+), 53 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 507c93d8b8..65d532d473 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,8 +465,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_members);
 	WRITE_NODE_FIELD(ec_norel_members);
 	WRITE_NODE_FIELD(ec_rel_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -569,6 +569,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 97e43cbb49..1aca5cacc9 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -567,6 +567,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d6ceafd51c..fcda83ad2c 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5755,7 +5755,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7873548b25..813a365e63 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -321,7 +325,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -332,6 +335,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -356,8 +361,10 @@ process_equivalence(PlannerInfo *root,
 											ec2->ec_norel_members);
 		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
 										  ec2->ec_rel_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -371,10 +378,9 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_members = NIL;
 		ec2->ec_norel_members = NIL;
 		ec2->ec_rel_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -385,13 +391,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -402,13 +409,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -419,6 +427,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -430,8 +440,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_members = NIL;
 		ec->ec_norel_members = NIL;
 		ec->ec_rel_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -453,6 +463,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -600,6 +612,50 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -756,8 +812,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_members = NIL;
 	newec->ec_norel_members = NIL;
 	newec->ec_rel_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1145,7 +1201,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1234,6 +1290,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1243,9 +1300,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1303,9 +1360,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1315,7 +1372,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1381,7 +1439,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1437,11 +1495,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1482,11 +1541,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1871,12 +1930,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1888,12 +1951,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1955,10 +2018,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1969,9 +2033,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1982,9 +2049,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2042,7 +2113,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2721,16 +2792,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3335,7 +3409,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3423,7 +3497,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3576,3 +3650,179 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 49675f9bbc..f2c410b296 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -611,6 +611,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -644,9 +645,10 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -656,7 +658,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 44efb1f4eb..cadf08c7eb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -646,6 +646,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 73ff40721c..1cce43a94e 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c652a52a0..7d1fab0929 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -482,6 +482,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2565348303..7a4d067cd7 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1201,6 +1201,12 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index f83ddbacf5..d1f0ff317c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -318,6 +318,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1369,6 +1375,41 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * TODO: We should update the following comments.
+ *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1391,8 +1432,10 @@ typedef struct EquivalenceClass
 									 * em_relids is empty */
 	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
 								 * em_relids is not empty */
-	List	   *ec_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6ab9593d75..0410847e91 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -164,7 +164,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -196,6 +197,18 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
-- 
2.42.0.windows.1

figures.pngimage/png; name=figures.pngDownload
�PNG


IHDR\8lpqDsRGB���gAMA���a	pHYs��]����IDATx^��`U����J�7i��4��`yA@�X��"�"�XP����QD�"�QQQ����{
������	��		[�.��u���3�dK6����'$��Oj��%,�{�_]�t�a���=����WN�r���K��m%&&����+����[-ZD���_9�K��}�W�^�|�����������3�v
�_����-����x��o�x���C."p����\<D��!�x���C."p����\<D��!���k������^���{O&N�(			�(�`A����q�$$$��������]�N����=���}{���
7������J�<��\~��r�m��W_}eo}.��c����58U�R%����������c�%?O���w����^����.W^y����S�~�my����U�V����Z�j���m�6���$&&���{���9�A���mK�w�c��e���1}�������Olk�����86lh[.. �EFF�-�����-�;v����2e�m�����%R�P!��9E��[��q�F�={�4o�\J�,)111��Y9r��["�r��[��@�v�7�H��|��p���r��i����/��6���7���Z\�����v+k���������_�-�S�X1��DC�31�1O�~o�~N�����'�KEH�^�����UK,X`������m|g��E2i�$����-I��L��y��7$>>�/����v��H�O�>m:N��y����J�z����������g�)R����+��
��*S��l���l���!������\�bE��-���W��(��@F
>\:u�d���w�!?�����.�
����/����s5�HG��!C��}�|����7�}�hGr���M���������?n��U���e���f�����g�}�<'F�����g%�91c�h�Ho�
�Z�l�9����'������6�<Se���
6�m�>'�v����K��M�l��4���JC�-Z��@��sG���7 +i����}�K�.2l�0�
������Y�N�������������?��[n��lJ���_];DU���M�
���;�l�iU�R%��#%��^G��j��K��5k����2�.Y��:��1��d��N���kP\����S����@�r�����?����:j����cvKx��#_�|v�p���i��\<g������P}zN�8a���u�ms���}�d���n��r���ol����:�:YQ�
��;r��ylJo��Y�Nq����G_}��L�@o����b����3W�k(�j����>��K��RZ(^�M}������=����������O�og���������{z��h��E����uq
l����]�~W��^����Q�_�>��t�Y&�s��������q�{����5����k��Y}���%�s���^��c�mI���6����=�K���~Mz����=��]sF�k�<'��������_�s����~���Y�������EF�So��E�}}n/�3����~��{�����Z��M��G?GROx!�~��V�\��/\������YS{�{��i[��g��}�!!!�=a[3n����m��I��+��g)Z�hb��mW�Ze�<k���)�M�l�����������{�N�����9W��r����tg�����6l���fN������������\��e������/��2�}����5%���~���%J$��,y��I���k��W{vJ,H>w����M_��������'�3�w�9��7�0�o��vb�b�R��~-�;w6��;�\sM������5����j���P�-J�������E��7�|�y����a��{����������x��u���e�����Y�B���q��%�������O>�~��������m�(P ����7�}�Qbxxx��J�*�8�|{F��O�n��9s�Lq{}.�}�]{V��{����-����?��'�0�)���sI����3�u��7�s�1�z������������-�v[�n��I��Gy������e��In�%G���Z�J<|��9'-�>���z]o�K�J�_{�5{�Y����\�����g��������,�^��h=����e������zL?��e��z^v�e����>??�p��m���g��5+�������=�����eKq���=�O����t�b>c\o������'�Y������C��x_��_��V��w��;�^p�~��)�������{�52�;���\~E�������[�n��E�-�����H�i��|N���������s��M>�|��
�]���5���g�m=����'?���;��[���?�$_�zub���S�&����8���k�[���^��9>>�����n�-������w�m������9s�$�����_�[�KCw�q� o������������.����	��:���n�I��]��qg�����y��{��{�u{��H�"�s"3��p�|�R�������qqq����/��"�yi-�����yX�Z��
�s�&M��3��5���k�mM�|f���'�W�^���[4>p����Y��S��
5\�g�,Yb��g��|��dN@5o�<{f�	3Rpi-|�������?L�}z�~V�r
\F�qNH�zy����-�����������{�si������\���{��"���$��/��\�F��+d%������F�<�����y����c����Jq��o���L����?��c�q'�9v��Y�S�N����_�n]���;��#<�������W+p�����v2�J��]�
w�������7�WG�L�:5��nHqN���5p��m]��m��y�y�u��s�.�����su��x�`D�F���������%u���H��z���R�NN}����u��v���:�A���2eJ�{�u�EXX�YB�����.}>��L�<9�5�E�*]W�^��6���W��s�=�������{J�+WN>�J�*�3g�4�_z��MK�S�N�qw�������^v�C;�ULL�9�t��W]t���+�c��#}���N��;���r:�E�����s6h����^����	�������7m�d�^�Ls����xj�������~3������k��������g���������Q$�9:�����N�u�u����uq�9�2����/���E�N�|����e��w}�]g����p����g���)������g��1#�9�9�|������^z)��o����2%�Q_U�V5?��q���z��������\�5s��<�b p����\��\H��WO;F�hG�;�;�R����c���i\��:�&-=z�H>O;�\�v|y#p���s
�y�{$Iz�=�P����IS�A��p��;�+��#�9G;�Ss��������/�gj0W	\�E;C�q��]�=W���i������st	��%��U�F�JqNZ#t��sN��A�cr���}���s���?o[3��}Z�!
*�|��x��tK�9�8�M9\���i�H��� �v����s4(J=��sLG���udP��AC1����m���9��E�z��#G&���iS�z�s,���k 8a����[o��|��gO�������GR�`�9G�4L���������	�]4�r��{��ci}��&w��V����t���B��5��d����n�D�y��X���K�n����gO�'��kW��D�c;�_�5k�d���l<���y�����������w����3��L�h�����E�R�paS�\���K�����lg���)!!!f��0kw�|�I���c.R�H����3�<#���f���;����Z��T�P�lk��-$��Q��E�^J}���["�����J��/�-�)S����
( ����{��m����M�i��v+��������������V�9��-�L�>�l�3b����2��y�s��.�u��%Kd���f�^�z��uk��Z��9e��UvO�W�^v�\�?��������n��'O������~�����l�].�K�.m����{�V�d��Q�����Mw����S�?C�b��<�������_�'>������w�q��7�q^���h�����W_��s�U�.]����E��V�v
�����������/n�_��1c��������~���/_>y��G�v��ye��
f���Z0@^}�U�]�D	��m��F.��z�6m��-����o�Df��a�2������G���={l�{����[����+��s�4h�@5j�b�6}.���K{fRG�k'qF�X�BN�<�f'��5�
�[�r�tOM;;5RN�;����.�	�2K;g�R�\9�un0�tz����u����

���I;w�v�����v�:\M����u��N/�s8��v����_f�[�}�]�%��{��-����*�un���N�:v��\�����>l��^;�G}d�v��v�}����}���;wn��1�����}����.�������L�3$3���c�$���q������o����n��6������z�j�vL�<�n����Kv�=}~�|;6m�d>�U�&M�g`ZF�m�~���G�����aK�j�[@�#p� ��s:k�
�B�
����l���#����_�����U�����lk�P�X1���������3+22�\9��^=���b5n����������������vF:WsgVza�k���1�v�Z��4:�|��3��H��I�C�u$����V|���r�����7��v���[Lg����+W�4k}]u4���l��n�����n���c'�����w��Y��-�^y��'�^�(3e��co��F���2K��p������{����y���
�3k��yvK�{'=�������I�"��/^l�Dj��!�j�r���Y��y�;�/_n�D���o�%&PO�>
����H�N����/
�
\2���&���7�|��J�	�Q�)���^�������*UJn��&3����s��������+����uY�l���\���)�48�PN����<F�@���W���]��8p�L�W\a�.����]���(3t�����s�u���r���� ,��{�|.$�:t������%��2�[�����e�=[�����[��#�vX�5R�U�������u���Yz?�u~{Kz�[Tz�������$�����/��}}MuT���kHh8�������Bt��M���+{��8Sjy#T���Z������),X�������N�:�p���v������O��$:J���?�{��� 2����������g�5!�+�g�>����������/ZS��+�L���Y*U��������:zHG��(�)���u��Z�$=�]A�Q��������u���s,.�9��Q*:���O?mF]�o��@Gx���s!#�
�!�k��#���)O�'��kZ��rW�h��i��<�(�3t�P3jH�Y�M�[������.�Q����\u!����r�xZFG��{?�.;vLQ��{���N���}�����-��D��A���\A��z�!If:�^|�������^3��7�|cF7h��N���G�t�e��z���������:�G��Ji���N�SO=e�H�?:�/�+��W�Fc(s!\G,i����Q�[4$h�����w�N��a:Z�q�Q&Y�����E��3R����j��I2r�H��1�~��t����%��Y�����
�L�Q�����3R���f�o9r�H�RNG�����2|�p��g�e��2l�0s;�'O��~$Uf������i��9wx;��J\2H��vjK��j:-Ngxf����{�����v/i��G;�p�k��������1|E;A���Z�A��y����K��_o��1{BG9\�u���_�[�6��:u���8�����S<��k���L�(�N�]w�e�U
D4�q��:]�/��b��hP}�w���PD�+o��r
�tOzt�3a���������ud��l��S��.����l��O�t����2U��*�i�4dF.���Y3�������k��
w�}��:�k��\?���R�lY�w���G�-�]�v�������2;��@���=����N�S���Z��R�z���3�;jKk'ix�����B��E�}���rO^�c^Gfb�����W�n:��t�1�����iI���Z{�n��fI�N�x��I����_�;t�Hz�{R�	���?f�-oq��:M�;������|����mMi�!����/�:-?���Y����_~��v����u�^<�\2A��wh����1���z�u����V����b�����
�������?o������\�vB�e���2~�x�wi.��l�����)�.:���{F���U��1������o��o��y�k!�e���mw\CW�*ih�#��k8���c��g���Q�\?7S6�0�1��C�%�#]<�A��g���q�3q����NG�y�N������[I�rf��e���<�:�W�������o7���7K��}��;Z���ys��6a��M�w��&.��W�^9�Wb���i��))
2��X��r�����O�(��[��}�+���wy��w��Y:zF����&��W|���[�-1u~��'�w����~Nl ?fO5m�T*W�l�uz�[o�5�U�k������:��R���N��%K�H�2e�	=u*�:��v!����?�{"U�T��_~9�t�3g��b��%�`�st:�@���/��2��S$j
��!�>^g�5��+���$Zd�	(tJ�����d�Q*��u:-�P�1}�t3b�������48s�����i����U�VvOd��qv�;�gB�~��^������$Z��d���c�������:��O�>�BgD�� F������[������g���o�@p p�$�+�z5s�z���,����-[�#g�������9:O�
7�`B��^;]C�zH�W��������rL;2��TZ_�u���\p��\���������	�.U����{�i��v ���5%�Jz
�] <�����h��c-B����=���C�}k��
�h�=�s�6��~
4H������@�N�tU������8���I��#��3-�N	����`�Z�j�yq��r��p��Q�>u�.������K��w_����7��7���:�D����y|���N+�:����������C�_�_�~�jH������;�����(��w��K��N�k��������������|\C_}o\,.���<E����Q:U��dq���u
m�;wn�:.�����t/����Y�4P�Sg)�Nk��f[;�u��B^;��v"�NK�:��B������|y��*����i��^�z��$����l�!�v���!�~�
iW�������P�>�������FH����6!!�n%q��s���k����KM�GZt��c�u�%��_�`A���������+���Uz��k�o��N�������z���Z�[�w�����a���|-���������� E�T�k���N�-[�Hdd�m=�y/�{�����=��5p���������5<��__g��V�Z��^�h}��"�tz��#���E��3��sY�7�Q�����'ONQs$���R�������p��u:�GG&9\��D�������s��}��s�~���?�1��tI���_�k��������_��mI�u�q�m��I�%���&�i�������3�4�������+.��	����J�|�NJ���������~h�N��W]�(Q�M�vh�(��]������Z�v��rL;�u4��B��.�N
��kz{������v�����u��=�Y�9��f�i�s�;�.�N��_�vr��Y�G�h���������k�1��C�7�tP�����
P�|�TC�ujQy��pG���������>��S����cz�����^z��S�$�3g��z\; u�4���?���t�H�
>���t�%��}}�u���h��^���o��)P��9����|;���s^�C}�t�AF:M�{��k��U�}N� 15�7�S_GGhQ{w�(����?u���}?j��a���3gN�9#�<��#}��>�4ib[��S�i�����~���3m\Z4��� }��#��i��=��'�k�_�CG�hp���t$JZ4��E?w�����+��#���������p��f?=�Y�_��={�k���T�
�5���=����g���o>+�9����n���l��
����^��+W��s'�i�����C�&u�������������������k�?_��Lf�s����}�s������?��|j��:5�+}�{Y����c�=��z6@Z�{Z���]G,�m.�"p���d�v7�!5'pi����h�RD��5��v���:R$=zE�CG_|��@:M�N�����#O-um�o����^�[b
P4H��Ycjph-���5+����Z7�up�E��2e������GS[�mkp-�����}��g����{���z�j���[%w���5���X��L�<�,�"p��W^)�������_8 ��m��[���#G��b:��{.@75[tJ1-��3gN{�."p����\<D��!�x���C."p����\��E�����+&L������f����e����{����m���*�F�J��9R>��S����g�B����\�Z�d����gO����m
|��-�
���;mK�j����������i�F��o��T�\Y~��w��'�m�7o��|��v�\�����h��q��2}�t����6���=�p�C111r�5�����/����X��k�<X���_�������{���L����O��8r����0'u������cF���?�,��(��"p�CC���������Y�f)E��n���}��'7n4�IzV�X!�G�6�����o�#W�\��U+���{e���&�q��y�]�vr�u�I��5��#��W�n��\���$,,L���n�r�-��b��V�2���H��o_�v��W_��e[��/4��Y=5x�������OK���m�Y���3�9r�uZ���k)T��/^\v��!�����/��b�3������sg�~�����'��I�&���7���*X���%$$������d���={vy��������Vk����O�o�n���[�J�R�$[�l�x�d��������3gN��3����o���#<<\r��m�2���_#p����.\X���kF���[���+..��|9x������Z�E�5J:t�`B�}�����?��3:f��5��g�}�]y��g$o����u!�k���Y�vc��I*T�-G��5�_]*�����`=z�����(
\tT����G��$w�y�������M7�d�����M���\\�4c�����+��b[3g��
���`F��k��ea�2���k.�
��E�
*������E��b
\4���m��(Q�I��_�}��gF�<��S������K����Y�f���_�����q�	\��+g[��G��5���%:2$**��-:%#a��Z-�r�2�����0Fm������5\TLL�Y�?\�
[�!���3SeF�&M�z���f�JG�����u�V�$::�l�����4k���i������f�������Y�Tk�u����6lh�����'N��/�l�]u������A���:v�h�:��>�_|1����O��Y��9�W�^R�hQ�3g�m������G��s�J�2ed��if����x�4i�=S�_�~f��oH�-d�����o�I��9M��n��I��9����*�������r>:Je��Af[����2y�dS����������K��-��Y�k�6A�+���M���C�|#��|���W��h0�i�&S�^wt�J��%O�<f���r��A)Y��d���������iq
��g��u)�J��-�!p@0"p�S�����P3X��e��_�
.��E*TH����4������EiMo�-�0.�/t���L�������-.�/f��a�����7K,���tl�@��"**�nev�3."p����\<D��!�x���C."p����\<D��!��,+""B:u�d��.�e����2|�pILL����^��kg�d-. �������H�&Md��1m[��%�tbE��{IF�!111v � pYR\\���7��%��7���Z\@���eK���E�R�F
��u��,k���R�N��o�������-��2D��y����9��/�(/����
���$�a���Z�j���g�������d������ ����-�����J�z��^����Gz��-�����n��V�����^J�6m���K�=��������K�.2l�0�
��0�dY!!!f�I��S�N�-�!pY�7�:�
�/��,+g��v��1�������}DD����_�)�/���;. �������O���,qqq��|���C. ��|����3���Y�d��mr��!������x�����
U���������:u�H�%$O�<R�@s����9�{�6��B������^&O�l����Z\���G���k��{. K1b�4i�����:pQM�6���G�=������YOT�����i��?�7���q�(�`F����J����Y[�n�����L�N�:%�k�6��}�������6|7L�y�!���R�lY)R���_�Hf�-%	���Y�������o�����%�J��f��IHH�i��=���3GJ�,)�6m2m�����n���:�O�N$y�k/�#��e�+GK��$!$T����=@�"p�"Y�l�|��g���_��lk�:tH>��Ss+W�������o�["��o�j��Ill����/�Y���m��-[��%i�����9��O3�a��Qf�t�H\|���
���r���sM;��E���m��AJ�(!U�T��m�J�-��W�7�gdL��-M�������X����YS��-<<\*W�l�D~���u~����["9r��[�;�'���r"I�&����I�;�� h��g����;v��)'N��u��I�N�d�����aC{V�4��2e�<���f�������|��������KF�����>x[�+����P�u$V�BB$4D��������[�Z{&�`E�����f=j�(�h=
`�.w�y���1C6n�h�I�����TdZ�����<y��2=z��[o���2q��4p��e��3HG9N�>[��5T�����q���Nt��R���{�^Y���<qZv9%K�������`{&�`E��<xP"##�i����,�^�����:-����~��]���or���L����6��%K��pJ�O+R��m:���[,�G���I-Zd����t~O�4j/�����
�e����Gj�&9���gV.���G�������������=�Y�e���f�7o^35���C�g��2�|���O���A�$..��%���t��kM�7�|S>,��u�-I��Y�x������R��%r��Rg��R�������Q�,$��
����UK,X`�����V������;f[��@���/��]�J���mk�k����q��-�m�<��L��������_�z�����k���9#]2����K7p��i��.]���YG6-������ymK���EHhd�$�����X��{$c���k��H�W����A�����]�t�a���V����W.��pC;�u���i��Q�F��{!!!v+i
1����98p��x���9�PB�<yR���l���V��Y�����-���ukS�&������k���sm�{Z��T�R���pi	����d��=%,*�m� �"q;VIh�|����2�2*!����e�^��$���	\��y:Eo�2elK��|���W.�*Q�����w����O��6'p3f��k��l;n��6�={�����f�����%66��$�����`�Z��-��ui�z��tC��G�s�=g[�7o�<v�HMG�HX��$%(*�	�����%����D�7%��{0c��OC��x>� 5'p)^��m�8������������W\!;w��p����e��������}��2~��7|5���p�[����p��5$�V��y�t����W���/'N��ge����������H�F�K�����F��5��p�Y�Fr��i��m���E(P����/R��Yo�����o]�����p���S,��2e��s�9�����i���`F������B����_�^���*��5Xt��pF��+����u�5�:�����tX:KF���s�&�N��W2�_���$��|��f���S�>3t�1��[7�v5xp��5:u[0H}u�Nu���9�7�/����]�LM�fj���r��Q{���������������N^{��K�^�����3��W_}���Y�t�m�T���*K���H�����s�yZ���?Nw�@r��A��D_���R�J���e��v�w\A��o��&M�������
*����n�Ir��m����/k�����8&O�l��<�����+��X���o�����n�������$����z�)���?�f����������_�#�<b�
,?��S��a�2d�{4I����%K�HXX��ER�}_#p�I�&���M���i�L}��;��/�l
��[w�u��,Y�W^y��1c�{�������7i-��/�v����E��g�o����������a��������ys{4�4h��ny��_~i�|���8 %J��|�E����M�����#��\/����s��I�l�b���3���h����s�m�t�5���'��b���/_>{��e�]f������-�*U���m0�Q?�6a���;.��T�@���k�����?�.�So�X����z'7�|�=����>0a��<��C&l�5@@Z�j�|���e[2GG��T]�}��mAV���=��c��E3r����i����m�6����}��g���r��Ts:%�?��UW]e�<s����-d������G��+:�����e���&(s]4X���/L
����go�.��r��Q��d��!r�����~h[Ht��[n�Ej��)7�t�>|����9s�-�"p�:u�H�����H��=�������'K�N���+��G(n��V�������+��{��oz�F��;LW�n]�
�;.����;���9s�F��Ed���i���U:U�NI��Q�J�_���}���6K������i��b����r���5�/���o�!W^y�,\���k����o�z/�>���/_��0�[I���c��*���{"m���[�C�
���Y3�'2�|����e���}�����>�H6l� ��-3����+/���=�d����d���sR/�/���X��#���Htt���@�8v���6l�Y�v�mf����kG<��:�P�R�Jf���k�p���h������h�9��j��Ixx�=;�3�	��\A��;����{�lO�6��|�A�V:�a���SY��y����5@P���*$$D�4ib����g�]�v�����l?���f�������[�1j�(���������B��e��Mr�]w�=��c���"��N�:r�������%o���
YW�-������?���lq���_��q�d���)�	&��,�
:T�w�n^F4	I<�n�s�j�2S{���S���o[��l��Q�\�r��;~��W�W�����W_}5y���<111v�s��.]���w��X?��+�v�J������q��2}�t����O���.��4c����%K��-dE����3F�n����
@@�i�
( [�l�-��h�"������� +�6m��o�^:w�l�#G��#Y�  �<yR�V�j���������>�G��o�>3=�-7�x��j�J���o�"P��][�u�&�����{���'��0���mk��F�X�f���k������"aaa��Q#����K��-�J�*�X�B��w��r�W��u���O>���@��MSa���2x�`S�`��%y��5�|�������}!p�������G����3��{������?���'��)Sd��e�S^�k��+Vx� <|K_�q����s�����$����L�:���� (��b������8Y�f��_���d�6�v���f
2�Q�F�w�a��K��;���QL�o\A'<<�Lv���3�%�)R��OM�4���a�:u���z��f��C=$��{�C���>}���$���DFF�@M��hm�={v�;w�/^���q��I��;. �r��u�V�Q���2N9��]���S���J��D������tj��+�=�_��n���}���Htt���pF�����5���'M���q����L~���UK,X ={�4E���l��Q�^�F�z������������2�?���4j��l��U����g�;v�(}���V������r�-����6m�$�K��{.��������%��I�����I?�d����@��R��vc�.}L�9�I��kJX�K'��2e�,]�TN�8a����>������� �4n�X�O�.]�t�a���V����W.Y_V	\��U������{"�]w�,^��l�������m-�?f����S'>|��V��W.@����O��Y�IHd��BB$<:�$�>%��'lc`����=�`�N��W��7���a�)X�������t��9r����{�0���\~������#f�;wn�F�e��e����G��D��Y;�f��i�^x�;v����K�~�i���V�Z�H��\��a���e���R�H>�3��J�r����x��k���F��)d�/�Y����K�R��H�"��bhh���BC��u�����n�(\�F��+_.����8P:$aaar��)y�������3�Z��J�K����ky��������m�&%K��{�"p�
\�����(W�����%,$D�9%6o��c.��E;duJ�
*����S��_�^
*$�?��m(\�F�|@��+M��y����<y�H\\�i�a���r�e��}�3�����{is�(��/���D��3��Y���(	�5�T�zm����-��;���[" (�1B~��g����;v���p�� C����w���,::�����{/7�x�,[���������������f�t\$:�Ek���������E�:������|���L�,@P������j4h���Q.����N���o����'�QKZ�8��P>,�����2y��E��OC�c�d��S�$,)J��������5�N�dq:����s��~R���r��-f:�:u��3��\Aa��5��I��4�Gjz�������W��%���D����������K��iS������r��&���wR�������k��X�'aLG��m+��v���R�<xP*V�(�<������o����c����v�,X��!��k=m�43U������o�5����h�"��D���U{4tIp�h��z_��='�sE����#�k������G���/���[��p��:t� &L0���l����c��!�v�Zi���m�3ZP>*<T*�.:he��r$U�a��}���p��"6�'m	8���vH� (�9���g�./���,\������r����s�='�����}��v����J���k��s���8i���=|/)t	��E�M��bO�9��D����k��\rE�(9�7�\A����f��o��9s�v�f����6�o��-����(�:ujr����=��C�h
�V�Z%��r�mI����?���������N��aE -g�����R�x�<� V����q	�zo������#�b��r�S�����"g�p!�������UK,X ={�4�������e����o�>S`]G���bi��q�$$$H�r�l�w����R�^=��^�j�d���v/���X�)RDv��e[����B���{�6m���K�=�s_��R��w�d��
��������O�����]%�o�����������`�<g���
%��M�����T����=�EGL\���K��^#������U�(H�������'���p�5n�X�O�.]�t�a���V����W�\����������{����?������?n[���];;v��;������/~^%��e��vHh�Dj��3������t�r��)��p-y���4p�9w�HDt���Q:J�����g��$�a���{��
\N�:e��;v�\��z~DD��>}�6�������*���-.��������^��8-]��t���%�����s��~����Y�
6H�
$G��5���;�k����z����N���3)Z0�|6�^���e�1�������I���}.�_^��y������Y��?)���I�r�d���y?\U(��������N+��h�l��M��	�.�����<y2S#\4l�����/+V��.���>����SG"##m�����k.��U�2c�i����K[��e���v��4�1b���5�\�z��WK�������i��w�~Nh��[�|�s�����[����w��Nm{��w��Nq�������|��,Y�DF�����G�2h� �'����J��m�^�����.�J�v'E��	�9����	\n��E?�BCCd�>-�J
����
F�:.Kw�H��T�HR�����	\6x�r��9"�z���~X���Z�
 �|-sc����`C������m����e��y���w��+W�z�)���o����7�iH���GF�)_��|��g)��?��,��N�j���N�8QF����o/6��Z��E�k��2f�)[��m=k���)�����*P����j0�nk�����xR�rE�lr*>Q���H���&�X�;F�&��@�����J/��C�
���7��;v�|���&��%J�����A�=��c�W�Ze���/��6a�k��5k�K�����,Z ^�2q�n�:�������Z�����={vs<P���J������y�	�\����(\����3a��X�{,N
�L
[N'$������B�zo`�.�G�
��e���X��_:�F��9S���*�����kF�(���|4T�����
t��H�Bs�A��)�������@r��m��l���n����\����n��#�\���
�Es������"9#��K��H]�D�:;�����'���.����;�����a���V��m9Kk�(-�����Zj��m[��ub�po�H]�%,,�n���-��M��\_��Rb�e�
[��&�����T,mn��@���&{@� p��-[����kj�ha�7�|S�x�����pF�xS��=e�����BQ����Y��Q�����f]�Jy������+��w�y�mXs��	����'_~��T�^���y���M]���]���-+~M��($�{h`�/:L�.%e���84t���K��rE�(���C�����g�'$��>~T�V-Y�`�	)���o[=�5?��+g���DDD$��5�R�P!3���c�l�{N�}��Wt��>&g-����Oi�
dt$GBB�\w�u������q-���uk�}!6m�d
{���>&��r�]w������n���o���_�e�$W#G��:�=���{K�~���Y�\�4k�o�Q-Zd�R��R�W�'w�7<,T�<��.-��7��'O_2E��s��5>-��-7\^(��}�2��������3�����-�����["������hp���{���a���zm5j��gp�i7��H��ic7nl�������o#p����k��V���_y��'�y��r��I{�,
'r��)u���-��S�9��W�\y�.��u���zK�w�nF�8>��#�����>����D��MM��0��i��M:tH���c�3K
�����������S&�.V���O?I�R��<�E�u=��B��7����#��'���S�����B���C_����3����m�9����g~�=����*pI.G�����Jn��f3�T/Jp���������~����\~���E������/��-�k���7��+b�Go��MJ�,i[�r�0\�p���Vi�����_��6�~���lk�8#\J�(a[<����?�h�;��?��Cn����d��!��kW�'��s�����Jd����\4�:;.���.wt�"E�G������F�DG����]|�#\.�#\nz�_����%7�E����3�g
?3��Ko�����(��q._#p��/���~Z�j[.
Ej��i�����4���g����_�Y�t�T�ZUn��&Z\��LG9S�y����R.:�F;�t�g_G���a#G�5p;4(r�.Gz������ +��a�-�S&�v�m�t,��l�m�T���	\�%k��e��8��y����Z��}n��`��~�;�N��z�%��B��5���B��}��w��{��)S������{%lQN����>����l����W\a�YIf����h��f��.ZO&�U������9G��a��p�	�5$��%��*.!QN�d�%.!����?7�����#\~��.��7����u*�n��������NzgZ.o���e��f{��=�X~F���
H���e��-�������Z-��j��#i���0=z���?�<���,_�p���_�^�zv/��#\�)�.:}Z����pq]�#\�Zv��(aQ9�dtK��FD�I��z:�(��.�����[8���o�>0`��j��'�C�F�|���W��R,#���NE�MZ�b��q���7i�D�M�f�������Ud��eR�J3rc��������^xA4h 3f�0m*66Vr��iF����_�����/��8�;.�J.��e�������%!>>���Y��������%�yK��e���Ibb���g�*��]rS������k.��U�2b�	

5�#��8
���{�#?�r��w�?� �_�)4���������2���������M��A����(
����c��/_.~��9��N�
����G�4y���M��5#G��:�������/��N��E�8`[<���:�������������[dd��.�K9p	F�[��2M�����_{Y�<\�F��+_.��$q����������z�l;�Y&L� >��ish80n�8�'���N'�N������_OUS�R%�d��%���J�h-�����l�=
��j��C�<�����R�:J���LjD�"p����(��tt�^�p��	[������a����OS�.��E������p����<
[��:u�<���v��4p�-R8����NZ�4��N�u��7��'�������{��{�Ms����|�Is>2g�����#��=84�r��.].��4q�D�7o^�tZ:��w�}'�|�M���,��)�0�1#]\���Gz��e�������;f����'��������xS{$�E��������#]]����� p�U�V���g�D���%44TBBB�\�xXX��.����������*U�$��W�{��K5t��E���F�H�
�=\�Z!}��p���~�v��^��z-�- =.��E=��a��1��������a5[�����-�7�xC^{�54h�y�����|`��4z�h���������I<�n�s�j��H��=Mr�J�,)��m�{%'N��{pg������ ����-��cG�����^`��������v����I��9e�k��*���y�T��R�����j��}2`�i�����Q��D�7����K�.]d��a��a�  �>}Zr��!~��������wy��we��Q��#G�4�K�#l�E�H:#O�<�����O<!O>��y���zJZ�nm��2b���]t�a�,@���YS��.:e�Nw�#44�������B�|BC�@*��aK�=��Bi
���X���1u�y9u��yaaaf;>>��y������Y�p��(��+o�w�2����yR4�
]�z�}/2�E����^4_�ym��-ydH���9��;�%�����z��I��umpi�h>���H��E�>��#y��G�^�C���#Br��a�����_�g�nw�iq^�#\~��.�o�pq�k�N��k����}�J�^��pq1�%x\�#\4h��g����;tZ��������.��v�m.
�p�#\�_|�����C�����|GC'
�Y��� p~��'�d���O�>�-�sd���k}�����.m���{��AK����x��$E�/�O��;vd8t�����'��'t��_��>��Si������W_}�L%dE��!F�mV�w��<�3s��=rb�	
�����r��q�����8����409|��:tH�l�b���G���{���Eo(������Y���������7������2s�7u���y����a�!�j���r%�J||p��^�d~�����,�����:�����w��*�H���r��Q��qqG����/��-�mK`����w���/���
Y��������SJ�,����3�E=���.35k��_�r%E�qI�h>��\~���e��I���uJ��z����8�����T�R���3pQ�]z��!�{�~�Dz�#ysETGgZ��*<<L?�$�t�>������T�<2��mc`9u��l�1F��E����=!%�< Q��������
��;w����.N��K�9.lQ.���|���W�
\2;wz�%d��R�R%��������-�i��e����R�L	
�f�
d!g>��� %
F��C����Tr��\G����+9T��5�i���x�������2��Q��m����1x�`�9s��L�?�4��_u�U2{�l����l��]*W�W�����B��-�N;@�$$�%+."A0�.IH)RD
( ��3�3h���l��/z��� (|����c��h�R�~}�P����YS�w�.������WKll�	d���gn7~�x�������?l�<��	[.��?������M��4c�#^��\A�����f������K����tj������c�>�}=��kg�.\�^��_�~v�o:"D���:P�^SC�����p:����uwn�,�����7-@�(T���r/::Z��]k�D"""$&&��!�3f��o���e^����o��vp1hXq��Q3:D������k�.3:V�
}L:������������h>��|U4�|���~��4����o�S�N2|�p���
�;w���{��#��(���c�=&�G��{�#[[.
IE�gI���$4<�L��f)g>_�\'%
f�h�%H��k��@�S���}l��2���)����K�|\r(��5�_�*p�5k��8��?(w�y�DEE��C����;��@�/_.������K�����t�M�^��J��2������n����%�#p��i��#C�����'gxx�)��#Att��q���!��#G���;�NM�������:��^����RC��5��0��*�w�6��E���y�J�2eLM��������+����k��6m��dq�F�2����@�����Z�D������;y�-��K�,��4d�;���/'N�0E�S�>�/�X�qp�%�o���N����!C���������}��gFHh���M���t��e����q��>�6�R:��.:�ad-���#)� ��hH�#s���k��R�J��t_k�����m����H|��T���k��������E�d�������J�l��b��T���C ��.�B
[4@NH&~
7u$(p
��N��A�.9r�H[�q
]t$���K��..���Wmj��M�6������Yt���[��3h4t���:�aMB�i9�k��<�GBB�mk����������!W�\fJ�����.��mE�w�q��R�|y)[�����kg�����J�*�l�1c��=��#�?�<��! ���������Gw�Yv��m<��E���C[���}gN��7�G�BDD�)������t��3'�t� p��O?�d:KZ�j%��5;g��.Z(���o����z���E��U�
	�.'�����%$4�=��l9�UN�'Q�%������e68��	[� p���{O���'�N�������_~y��u]�<��r�B�"�M�r��N/v6tI���L�^���%9����x{�U�������s��v��D�_�B�M�r��9iC��f�#�s�s��� ()RD���O�>��#����t��Uf��m[3f�����}{y���R,�:u���~�� �J��(W�rnG���''�����HD�<����l9m��N��u�t�m���8�	|#$�	g~T�V-Y�`����S���o[=�S��h�B���c
�fe?����~��v�,�������zH��g�R���+d��5v/�6n�h
�+W��Y��i������D�Ju�%P��B��rl�&9{���i�
������BB���uR�`6�v������#G��;$::��>
+��O���K�z��n����44n�X�O�.]�t�a���V��K[A�x��r��7J����~����K/�������_O^(#F����8�����,[�,���vN��+���;�]�v��N��m�6��u�Y6o�,s��5��KBHH�b��@\��h��G�0��X4pIyN�.�T�
��v����_N�<)���f[�����@\��G�.F���W#\2�i�;wnsU���S~}���2k�,�S��mM������l�
��eF�h��m�pA 0#\F��������l"��+����S�H��%<[��wB��.*����:�o��(%��#\.5�'f\\����|�~XX�}��;�H����f��r��){40��I��K#\�F��+_.���y;4D��-�T�T����>�O>��|�:��k��&������9���IKLL����C^x�y��7m��� h���\��]�����BBt����,��u����r�mCG����O�R��������������_?i���T�^��D._#p����`��U+�8q��^�:���t*�*U��?s��%�������S|�AS��S.I#\�H���IHX��%$4Db���q1�S"��$?
�c��;s,����#_��s9���-��f���}�d����t�5l+�@D��5j��N���K;w�4�L�=�E�k���h���&l�����[�����3���z�LC��~�?�<���&���z:$1A"���l�$2G��Y��*(�q������-�D(#��g�GD���Ixx6��=j��\�R�G ,!!aIv����e�-�#\~��.y��5�X:$y���p'H�"E��gZ�^��T�����j����{:���	�[�n2x�`�*2p�@���t'�-����i�&SX�w�����d����/�K��WKhx���@��6DN�%�Gw��(G�2g��}�!:�����3�y�K����� ���g��]��H�P��-w:!`^!��NW��k�?^���+�+W6�i\<��!�J��-���1����o/w�u������m+?��<��#i.z����-[�H�2eL����_�7lQ#F�0#b\���G�����?�4��y��x����S���O���#�%<*��E%����P9	����C�MS 2�)P�<�&�7Y#\~E
�����������_|!��77��x��7���_������g�y��f5\&L_&�>�%%�V
�.���KXx�v�HbB����JxD���y�g������B��j� �����;E������1�����o�I[t
/o�-*:Z;dEJ�(a�@�
�K�%<"G&��M�����
[\pi��s�"�*�|\A����2h� 3���O�h�����������k��n�*�K�6���i��4i�Y��b�������:@PX�~������13f��9��E�S�=�7��r�=�H�����]v�=�q5j���k��Z.��P��y�L��!p��_��{��eB�U�V���X�������ic�:B�]�v��e���+���?�s����oF���?�����3���x�	�L�<Y^|�E���M�'�|b��@P5j�	3���+%K������������������k���;V�L��b�"�����9G�����O�<i�***��7l��LO����o�)�5��G�J�j����@���%��i�������l���g���n���$��-�	g\o7m�4��3�=�D�
5k�������E�
:E��#GL]��z��0		a�j\jB���Q�Z�d����gO����m�\��UM��y�f�".			vO�v�
.z���n��������n�����T���M���{f/��������ygFb<�3FN��H��������F 0���O �Z��5j�V��q��fZ�.]���a�l+�C��+_.��������4���-*..N���J�5k�m�;.�N]"�?�#9�G��%�i�f��������O�����i�=h[��D������W����� ��{���D9~��	+Y��"w?;Y
��!��KbOi�������J����	�X�* x�|���W.Y�pq��0N��)��k[d.@� p�E�AE��O?�T&L�p���g��w�}g����K� p��Bll�,X���y����u���,m���-Z�[�C�
��w����K�����O>�?���e���f��m.��0d��Y����9Sz�!y����Y:u�$<����=.��Q�n]��� (T�PA���k��� (L�6MV�\)'N�-�����| �+W�V�ZI�9�j��f��:u�$/��r��i����@Px��7e��e*111�t�R�5k���3'y���?�x� (���Kbbb������ ��������td�����d�<������B� �L�>]�y�y��wS,�������kr����3�������#d��
g?�9Ypi9������V�Z�`����������z���c�+W.����%K��-[����q��\*W��m�u;���s�����-����S2h� ��BfG������r��i�d���'��w�"E��VK����Z]�t�a���V����W�
\�v�*C��^xA6l(����������>*S�L��c���U���;�g��3���45�s���Y��i'o���m�����k.��U��!���;v��[n��t�����f��9R�N��y��*U���=�� p��\�F
@�h�����Q�����?vO�v��&�9r�m���4��-J�K�j���x>�m.��p�w��W\q�Y����f�6l� 9r��{�����K/�$�w���y����N���c���%K���^��k����R�G�
��v�2D>,4m'N4E����y��W$""Bl�@Vr��s�?@@
I<�n�s�j��H��=�����{�;&			�;wn��|�r���O$..��{9s�4�H����sX�\9� �v�=*�v����_��K���{z�L��'J�w�-������9}���l��R Ov�Rk������K�.2l�0�
�������<G�\���L�I3VH�l��%cBBB$w�HI8�����8�����bNI��H�������\~���e��&�N���y���R�hQ�w\��z�>Y��������w:A�^QDJM��E��5�_y+p��?r���(V��	k�6#��Q4��H~��5��[n��r��7J����=���_Q�%�c��#\��@���w�������G�XZ��R�JR�pa)^��)���ys{�@@:q��)��b�
��'�\}��)_}���*U����  ����f}�}���C�L�+*T��[��/��b��@�Hs��1���'��c���f=i�$�����/^,���{g,X����_o��?�Rhh�$&&��s����-��\)$$�ne����DGG��M�\�r�"���o�5���o���6l�����[bbb���@P�������+O=��\y�����J���M������Q��Yw��1s[�;wn�v5�|�������;��F���A���eK	7��.�����eVz��]��S���Y�L��������?.�
�S�NI����C�&@��z��I||��;�����z������6m�Yf��!���DFF���@@JHH�;v��F�Y���3���-�9�M��O7�>���]���[f�aHF}����h�"1b��wi���+f���\��[o�5j�M7����z����Y3{������@�p������'O�uTT�Y���������d:]XZ�-[f�:|����/F� cB���Q�Z�d����gO����m
^y���#G��"�:����*UJ:dn���>_����<�����$:Z��{���K���*,,L�v��<��Bm���� *W��m_������.]���a�l+�C���K)p�������[Z�h!S�L��i��d����z�jS|��yr��7���tbw�}�������j���$����Yk}g���i�&S���~�_��DDD�e�]f�2���k.��T��}�J�>}$��f���Y�x�\w�u�����K/�d��
\V�Xa��^{M���>���R�J������v���5s4p������m�5h7Vdd��.]��d���\~u).�h��5]v��m[�v��1)P��	H~��'�*�r�J�X���7N��ic[��@������Q�F���aJ1#��Q4/��'���[7Ca�9r��:uJ�,Y"*T�����)����s��g���E��w�}g��S�lY����C�����S��Ni������/���r��m�D��/����������r��	s\��V9r�0k�jLk���o�>��Z��Y�?\��
��9s����/����f�c�=f���bk��5��U���0@v��%���3���u3����_���	b:t�`��<��'����3��������{M@�,;v�������E���Os��;w���2k���f�=z�Y_{���t�R�����g���Lk��\��;�4�C���3�T�VM�5kf��{����={�����b���K^J�(!o���9GiQ�Gy������8���.�����	��N�S����wo�\��,\�����$:����j��H��=����5�4`�Z%u��	}��{�<y�|M�~�����C=$M�41�:BE���EC���[�N^x�y��g���n��g����/����l���<�\h�Zp?!!A��+g[����qc�>}�t��E�
f[��_y+p��� �|�)�<D�*�O�6��W�\�bY�j�)V���/�3�!p
-P�5Z��|��S,W_}���QC�j����z��!�v����+�K/�d�j��m
�_y��f�M�6r��	�
x� (|���i�{����h��#C����W�;��#�}���r�6@P8p���o����<��C�f���g�5k
`o#p�����-�J�*���4h`F��F�
�_�|���vO�B�
f��?������>|���C�
O?���Y�F���NY�r���Q������f={�l�����a��f�&@P��-m���~�A���g�^{�5���_$$$Dn��6���G����4��'.�F����_~Y��+��U��+��%K����6�o"p�C���S��Tb����"?��,^�X�/_.e��1k��\A!_�|��o_��^����f��v�@@:x��|��w��O?�,JG���?_f���b�:.��M3�+V��x�  ���[�5k&w�q�\w�u���o���n�I4h�b�_��4i�����S'��)$��
����UK,X ={��������z�j�����h���������_/-[��'N�3����K�%�j����G�6n�(			R�\9���������K�.2l�0�
�������������O<!����-�P.F._cJ1@P���?�	[�����[.��2|�p			1Kdd�YW�PA&O�l����4���j�����.S��)�_�reS������{������>��sY�j��h�B�<��!����e���f��o�o��F~��'{�{\A�_�~R�P!�2e�mII���c��5�M.���b�
�����s��������v�@���u��ro��]R�lY�x� (�o�^��g���|�rY�f���x� (�����������^3��O�<)6l���K���������5�M.��P�LY�h�����+��"U�V����K�r����^���h�5k�=�.@��^��	\>��Sy������g�}V���9~����S��	x�  ���O2u�T�wVhh��m�V�*&L�w�yG�7on��A�HM�4�������  ���_���g������C.��f���+$��
����UK,X ={��������+^�����O{�1IHH���8����b��y��^
6n�h��r��� �5n�X�O�.]�t�a���V����W�
\J�,)��m�{%'N��{p������k.��V��#U48�4iR�G��y9r���u���C��`D��5�_y+p)R���������m�������k�,
�����t�f �<p���m�V�U�&7�|����Obbb�����k��)SF���k�"p�����%
<X
( �}�����K���_�����/�w�^{V�\s�5�y�f9z��m�?�R||��������~�zy���$��f����s��������������v����={�vXX�Y��\��^x�����g���U�� F���?f�::f�����cG�����?�������B�
������k�4�����k�A���SO�\.��N�5q�D3�Xj3f�0�%J���4j����v�6��8B3;Q<�U��,X�@z��)������9s��r��7����n[�6u�T3Z�����&M���Y��n�������Yf��Mr��i����l�5h7��-����-��qc�>}�t��E�
f[��_��k��9R�N��w�^)X���NKLL����C���~�4i�i�v�r��	����t����S
[*T�`[2���k.�"pI�5lY�d�T�R�l��T�Rj���E���o�/��B�7on[/��o\\�.\��Y��p��+���8���������?�X}�Q��=���?_���{$m����<��3r�=��w�!��7W�����ft�c�=&���7�]�V�jo�97n���)W��m���(��E��o_�+V��&	[���[���o��'�|R�u�f
[��Q����_�m���}��~��O��
PBBB�������l��]v����:tH��g�����~s����?���tt���_/���3�O����#�g�9}�������/.E�M^���#e��1����+���7����C�f�~�z3�X�R,�������v��I�"Ed������S�N�X��\��5k�H���%g��*QQQ�,�S�,X��g������P�Bv�D����M�6��-Z�%�r��1i���=[d����� 7�p�mq�n�����(�5�-�'�x���C."p����\<D��!�x���C."p����\<D��!�x���C."p����\<D��!�x���C."p����\<D��!�x���C."p����\����W�����^��<y������m�{z��
�Y6n�h�����%-[�����P��-Z�l?������m��E���*��=����9sJ���e������/^l�)W��9���_.111�L��^P�lY�2e��o�^�/_.S�N����^�y�i���=+m��.]Z��Y#C���k�����o����.�g��~�z����������+fy�������-[6s�A���F�)�6m���~ZF�%+V�;��S���/����e��	��b�i��f����K��]�|�����O��I�L{�~���1q�D������o��2p�@�������gO	3��.xh���f��Gj�:u2��~������My������o�-I4h`��o�n�1\<.xh���r����#�m9���Sf�����zH��k��rF�<��#f�X�p�4k��l��������c��>���i��|��IDD�m9��^0�����uF����LC������\{��f��)��y�fS���[o���K�\��6����$�a���Z�j��L�-�����.������7��_|����>/Z���O?����n�D�M�&M�41�����F��)��{�9�9s���b�
s�Bh=��'OJtt�m�������b��������tdx�.]d��a��!p�����s�=��w�I��5e����5ctz���O���[M���[���+����9���>wz�R�J����������~��9qqq�e��5Y��,�����^��|���W�B���iS���oM|����������;�R�hQ�����s������l��q��������$$$H�r�L���O��(����S�P!���.�� >>^J�,)6l���{��o�m�����F����������f]�ti�9r��j��l�����{�����.x��l��c�|��'��O��1aaar��1Y�j���������������z��={�L�8Q&O�l�]��������N�������)�5j$��W�?���L��,�f��m����E���K����	X�~��Y�H-���IY�d�\s�5�x��A�$44����!C�Z��O�0A�=*�������8w�q�\q����0`�Yk��+�-��"��zk�R�n]����?s�=z�>\/^l[Dn������L�R�vm��=�	[4t����t����)R�H��9�[����sK�|�������\~��G{&�%$��d��H�Z��(��={J���mk�:u��e�?NCBBlkJ���r�M7I�*U����s��aM�65u_\����3G��['���7A��)i���?�����l��I�����?I7nl.�����6���=.�
��@` p�S�x���C."p����\<D��!�x���C."p����\<D��!�x���C."p����\<D��!�x���C."p����B����\�Z�d����gO����m�Y�n���O�����9��sXX�T._�����RJ�;%���#����%�$"g^�Y��m	.�O��m���y�������n����P!}����q��2}�t����6���=.�"pp����8�����_`�u|�H�"�e��Gl<u��1�vO.��[@.���S1R�a���]�\N�:%�=������'BC�9��E����������vO��e�����h�;�C������R$_6���mk���n��Y��i���T�P�9��;{�l����{w����G|G�����M�cc��V��H��/:L2���.�v��R�����^���w��!���O��g��-�Z�h�yccc������K�#�����=zH�b�.8t��u�T�^]���n��=3f�����K����n��r����#����?��_~1��m��R�L{|���k�p�%'$$L"��I�Y�fp�gn#��+t�:����[y��WLG�.�y���L���wo9y��m��'NH�������-���"B�Pt������B�����O�v�j�R�0�b��r��7�K/�$�z�2A�M7�d����,����[r�����BB��Y�r�	�6lh��/���\w�u��bBB�=�,]S�F
�^���l��^}���������$fp��g���p|��Wv�\�����=�t|�k��2>|�G�@|A�f�RL���.������K�@��%��������G������J�������K����Y����B_������2m�43e��#Gd����z�js��o�!_~��4o��|=�����SO���]�v�, p�>��I��3g�Y�6u�T�N�A�+N���>
�t���������BG2����R�Ns^����H�V�Z���>+����l����<4�Sc���F��m}O=�����S'��k�U�H���/�������\��2v�X9z��i��c�?��Y���{f�����n:�+W�l[����:]���x��W���?�GR�����N:�Bkei�Wo���|���f����r[���o�1#�>������#I���I�&�����M�6��}����h���I���vG��{��7���[���'N]������������q;v4��?����:�-��H��)!�/_>3�Xj�\s�Y��C��P��*5�ZLM�0���@E��X�9L���~W�TTZ$�i��S[����:f��&�����������3S<iG�Nu�53?����X��l��N�hM��`�*U�t�k8������B���-3�84�<x��S�A���e����h�������[�ni�H(d����kX��N����>?�^��;w�u��%���>�J����y���V�#�}���4������8p��OMG�(}��5k�������*U2�����������8y��'�����n�
F�w��]�xq3�@������w�N��~����2e�9O�`h���i���]��Z;����O�(�����JG��9S�i���[��Q0z�;v$�P�R��Y����^{��[�li�d]�4X����Y;����WG9���'}��DGG�}wa����J����9T�z
g�N��h��Yr����)i����!C����q���m��Uf]�`A�v�����m�Y����1����J��r�#CtdDxx�	-R+T���o���!�
d�::F���_f���q�9R���i���m3�Sj�h�~���s����,0����z����Z��@��uk���������O�uf�~��f�#�Rs����[�"���y�i}��h(U�D	����Q�{��{OG��@z�w\���7k��i�t��&��w#(�>��Q��^��L:CG���{�����_�n]SSe�����)���kStf;��������	:*c��
f�.Z�D�����"����V�
d���).}Nuj����������V�S/}����oF���&�VKG���.�L������?RL����?���h����\�s�6�k�K���		�[@�"p���N�������
*)�����bww'6&�X(&v�,���"���(���
[���=w�;���������:���3����=�>�<(gA`C�����~�n����h���.Xk����[�%HM��������S�B��������w����:-��4i���:0����}{�i�������7s�Lw(Kp�"��)E�h���$e�Y@J�q��ww���R�M)��t���L�,��#G��`
���H�������+��� ��[�f�,[���fff�[ Qp6N��p�JA�(A
�hd�:"
�2������pf������fT�����-z� ����]:� Ta���
s�+
Y����O�N38����JA�+��{�����c�q�e��^�q����@:�k��U"	��|�T���U�]��O�f.l�]wu�����
S@J��k�n�DE���z����Z|\��i������[���@��Q�E��J� ������{\��`vMx=� �YX��I���6Ji��m�m�u�;t���It
b)�Z0�H��+-XYi���i������t�y��m�-�t�������#�p��B���kc��q�J����@	f@)��f�D��^{��`�L�[o��n�5"p6"�S���/w��L�h����]���E�X��!J���-����h����U%��1,��em����:��$�A��� u�����n�b��	.�����o���m"�:,�F��w��
�p����[7W�F��Z�je�v�����-�$.����w)�*�-�`vIXpa���.�T8�X������z��{�95j�=��#.P�p	�py��g��f�h6�f$������EW��c��[��"r�W�Y4J{��E������<Z_F��\{��n~�����m ����W����WJ;���5h���x�	7+������N�o��Y�����R��q�n��^�5j���Y"���'���K��a�����5
�r�N8�������w�6Nx�k��Y��E��;��c/�����W��=����Hd\P	�Xv��2m�-� uWx�]��j�:+a�����ta:�h���2]v�enV��S����f�h�v�8����l����"���������89��3]�@��+�����������f�h����/wLX\9���)��d�n��%���n�2s�t���������mqt���N���/�h����{]5jd����K#z�(��s��JI7��,A��=�����>R
�'�|����"H�&J[�����XG}�{��/_n/������Jn�=���(�.����M7�d��~����g�^�s�ZZJ�����Ul�fu��gN�5�[�b�-\���	_�^�d�-[���Y
�+��`K��u�6�e
h6��u�l��7���4_��;w��	�����9s������G^��?��H����h��_�c��f�}rtK������;��~�m���r����xz�g���~���W����s��K�[a��u��u3h�E�B��>
�JC���?
v�TQ������z��~oCzl���]`0��9s��T��X4�@38����f�o\���/~�Uk�[JJ�D��,������^[��*k��=�K�^���^vf��j��5����I.
���g��U}M�)P�tr�"�P�p�7.�����M������@�����@�����@�����@�����@�����@�����@�����@�����@�����@�����@�����@�����@�����@��6���t_������K��*9�|�r�������c��O��N;�V�^����Q�N����}��w��O{��G�=��F��.��Nl
_|�
2��?\�n��f�~��U�V�����n�:���K��G�5���h>@�����@�����@�����@�����@�����@����JN._���>}��1��U��k����e-Z����{��w�}��-[��5k����;��Q��|`�����M�g�}�=�D�P�=��Sv��������������~��q���h�"WNOO�����2���W/���/\y����/�����!R�*���l	�[��/��W��/���_��P�'�|2?�"�k��%DC��t���_�3o�<_2���}	����;��/�{(	.��������a�M�:��g����Gtzmv�m7����}
J�� ��X����O�|����j{���}��s����������L�a��Y��}������������'%%����[_�� �Hz�j��*U��o�\~��.�p�UW�<`��w�;V����~��o���������w��W�{���/8f��!��<;v���]�vn��
��)Z������G��z����c�9����~�9���\]�~��Q���k��R���>���7(@B�Y��/�\���}��I�&����w��~�a�W��k����~/O�g�^��/���(�>a��7m������[g���������o�nO<�D0`�+G3p�@;��c�^������o�a��-�5(@By�����.���:k�M����~�Y2���Y*a7�x�K�u�����q�����a����p�B��'�����������(���g�=����k��{����Y�j�{��'��>�����%A��P&L�`C�u���M���c��#K&r��>�����N;�����������q�y��.�������fr�QG����y���s�q�*@��S'��E]�K���5�F�r���[��:�@RIKK���)H1}�t�g����������hv����6�^�z�Y+Gq�}��gv�����9s��y{�1_���r	(x��_���i��������,��ro���[�$r�}
4�G���)0�c������z���K�-���K��<y�/��Z��K=�����f�m������_��@B����m��������t�R���k���S�%�����P,��rK_��?��Ky�=�\k����j��mZ�g����<,z��p$�+V�R�����RA���Ti��I�j�|�?))�����O?��@���E_�3e�_*����
E..��P"�����T2�4m����<�<�����o.�lJ;�����/���~���.(_\�V�N��N�:~����������K�Z*a��������1��J&2�#�o��/�Y�f�u�����v���C�V�vmk���u���Z�l��@y#���N=�T_2�����W��<������^~�e_��c�=�Ke�`JX��+]�tq��(��@������@y#�������KyF���P���Z�j�����=�	�@Q�#������]�F�����b^�%,<�F������s�=g�|��v�a��������G�nQ}l\�Z��������{y222l��y���Y3{���]9�@L@k�\t�E~/��-
��x�����m��r��'6_|�����~�l���v��g��l~���5k���}���~@��j�*_��|�r_*9�	DK�u��G�������0���?��:/��Q#_�s�}�Y�^��^A�
���<��S������K?~��x��~oCZ������`m��R�)l��E��h��D[��{��g��s��{f�����������Y�l�=��c�=�������?��2e��h��N8���m����D�P�m��.E���n������6Z\p�6t�P�g6w�\k�����*��w�����W��.]�����W ��4Zc
@������-[����u]n��_�C�P�|��6x�`�W���:�.��"����b��>}�����f��a�~���5k���s�1n����;�Z� .1"�@�����@�����@�����@�����@�����@�����@����JN._��z�j���[�N�:�6�I�9�k����)������O>��������\,X`��7�{��}��v����������n��v�_��b�����Fzz�/��-6��3\.\hM�4q�i��Y�v�\�[�*U����~j�{�v��8������Q�8P.^}�U;��S\��E����_������KP�-Y���Jp@�����@�����@�����@�����@�����@�����@�����@�����@�����@������|.�&M����1c�G��`�X�t����X�*U|���Q�Z��U�^��n��U���i�����A_��V�X�~O�W���M4%9�E�����5k����K������Pr}�������l�2k���5o�����[��"S�N�)S���]�f���U��u�f;������^�(�����-##��k���>*�Q�wJ>��S����+g�����_?W�����}
�_��6zI���<mX�uJ��ITz�V�\��A�=����F�[��j����N�:�����(������W^�_~����;v���:�:t���(=������F�e?���;��m���������5k��DY,_������6�3�Y*�!C�X�>}�vr?�����)v��1c|-�M!����y,���Q����{����;�6������v�Z_���4i���&��3g����l7�tS���^�z�%��A)�zjKKK�������s��i��sFn��s���z����m���}-��&###����������y���������m�������C��7���x�b�M�0!����>��&�W_}5�w����}mb)��/j�A���������{��W
�����WY����^��o;��C���������F����sZ�j���*�G}4�sJJ1��Fx���E�l���v�QG���kj���/Y����_��/%������������F��dN?�t�����^�L)�8�����)��w�����9s������a�
Fy>��3�;`���e5y�d[�v��3����
�W��Fq4s��?�p3�������=�	��(���DV�Z5_������9+'��_N}�SN9������%;����-�_|�=��g��5yj����5�"����[�^������5(����R�4ci���v�w��0*�XH)Tp���m������[�-���?�<L�,X`����w�y������tap������+��u�B|�9��C�tg4�:�(����onxS�J���o��f�������N7�[S��t���#�p��w���o8��cm����Ei+��[������v�a���Ni�JE6k�,�gv������:?A'R��w�}���	���R�|��~�u��G�{����Fx.��"{����>��C���WR��'�����[]�G��H���Ba���}M�_���{�&Mr)W��+��x����^7],�������N=�TO�������}L�u������r������0����c�F~�����k�q)�_�u�Mm�K.���5����>s�+�/.mo���v�����W_}��)��s���2]u�U��7��#���v[W���uX]�� ��G}/�~
$SJ�������6�6-R�	������<jzvI<��3������_�{��������6�|-Q�N�����i�|-�)x}��o�k�&�^!�Q\�������������r����p���9p�+�vJ�* }.��,)����k������p���9-[���N;�������?��w��W�Z$���~;�\{����t�e���������-��>�,�y��r�-�������s�g�{P�7��J"������6R�	*<��(g�}���x���|	�&�����(?�����YJ?���+k������]�vn�(�Q���{�=��m��Jc��a��7����Nre����!P>J�>��m���?��,Y���L��]#g�#>4;?P���a�w�/�
4���
��/��x�������{��>�����Pqp�T�4�\��O���[�t���=r�H������\�����nH������(5@N1�>4]S��V+�i�����=z�S�#)�_|�~�H��i����}M�����_�����;���O����~�k�L�0�=g�����b����-�Z�z/^������:gz��kF���nJmWJ��|�z�^��s��{�k���3�����v�������R:���}��z|i\���d��T������3D�;�t�����������O>�5�[�~�KW������>����gV0C���_����B��c���9��)�>����w,��Q��D�VN��]����O<�K`Si��A~e�e�5����Q�_{���O��vQ{#��~���@S�Lq��>F��k����e����6:�j7�y����k����v��Km����^����j�
����S*y�������y�@�,�7��s����"�(�vd��[��^#
v
�#�T����L�����aJ�U�6rY�|�������~�������^��:����O���4}4��R�*�k����k�u��P��+������S/8�E}�t-A��^����N����S����@?K�<���S��$����$�B�#u���<�;3X����'���D"�Pq�S�u����O�����k����)IJ�g�}6�s��9����
o;��c�)�^{����r�9�
���]��T�R��sh������Pp������O8��
�C����RD��E���"�j�������n��'���[�n�������
6wLar;=9��~zN���<�Z�j9�
Tw�RR��Y�fn?��j��k��I�6:������{��<�
���r���9��C6x��V������G���6Nsrz��v�QG�4NE���x/�7��=z�?26z]>��
~����f��7�x����n�����������?F���x����#��k��v,���w�}}mt%��~�w���W/��`S*���	���=t���x�RxM�81�$����_��R����������/�����������:@�AJ1 ���w�����^{�?��>��%K)6w�����;.�f�����7�k���>������I�~W�7��v�}���������w�(=��M�7�����Q��1����{�����������[�h�k���.Q�kj�G�V�?	o��-X��@��������+�����Gy��-��A���W:�0��U�>�������lx��_���r�<��
�i���������_���}��T������mq��s�9����s���-���[�?N���R?�N�:���ZP�_p�����k74�����t��k7���=z��?6��{��~rp���^��7��~��n�������{�?�p��Jc\�M)�f����7�|s_`S	���)&�Te
��<N�����	����
��E�D���;�>5�F���c"�O<�?���~=��[o]�1��+�(h���p���[7w�6��~>J������Y���
����~���^�x)M�e��Y���C��Up�c�=�S�F���h�M�[
�E���&M��g���68>��s�%��G��:������u�Y�y�~�iwN#;��W'Z�������������}����Y�|y��#l�����u\���hf�����y��c
�����i�������k��|����a8��(A���NVQ�>���K�p�_p��@E@�Hle
��m�6�q�����->������VX�E���	�W���c�)���v��(���SNq�����9/��B��Dk*�)�Gi���0]�	�S9�@����P����>�c�M�}��w����<��
�����}m���y�A����y�<x-�?	���~���;.��l�u�
�4xO����7o^��a��Y�IX�z�H��pj��5l�0�M�6���k_j��^ZZ��-�#�8"��x���|m�!C���7l�0_����I;����O�1���v����G�AO>��
�����+08�� �#�<����kK���Y���kl*\�R��K�"�E]�k�p����>�{?�������@���o�l��f��D[H<�Q65�uaX�	_��6e�W�4�����������O�������Jp	:Qj��7.���-[V��{a3ST��>��L�:����F��[E��:AK�,q�+�t�y���4��3��QP*8F�f<DR@'|�:�k��u?�6��
wl#�%��C�w������z/�����<��a��z/��f��Q�h���C[8H����_x��K��T����)z�w���?�?A�%�v�a������3�;�_~�e�1��Oa�YYe�D���}�g��8���,����>JQ]�?N�X�v	�/��O/���m�zp�.0�V�g����s�=����q���%��<�n5SG})Q{Wk������XI.�v�M7�>K�<���F����
\���y�O��@��{�_[E�����}���7o�y��T�"h���z��Q;:<H(Z�V�A�>��czm��o�����rK�������6O�f��j�{����k����k���?�,�����={���{��'��G����p	�<	��ySJ��~m��)r�����TP�`��QQ��k�J����u 8V�� ��.�xY��<F�:aE\�zu��0)2������	�E
?�F�D��@P��\��qS�p[���45;���SO��
������5D��Sg6R��h&C������H�����`.����������tw�9����wY�G���kYi�G�<��d�.W\qE�}��F������!,2���Ma�c��&<C���0�(�<^#9#�;�%����m|6	��'��(,q���k�����.���C���
?DK���m�m���������@GX�,����qpLd?�4��R1�/fGk�����h�ia+r�%�����^X'�"9��W_�_��d��s��?������%~��>�h_��p�X)�"��_����\8Pr�i����Sp#x��������*+hR�3����(�<	������7�Q���
\�s�F��l����/�M/pa�| �h���^z���
����6 �^�z����0�����m���M���_��/��2w[�<���V/L�^����{]t�E�d����
U�^�y��W���o�����%K��w���
]z������y�w����O?�Kz��g}��KOO������t������-����H_��I rq��N�/����k���H��a���v(}�dr;
�dv���[����^A�w�(d��s��������%�F��R��h���������m�6m���ow�h���h�`z��Y��{�g��+V�(�y����V[mU`!��z���|����o����v�})�}�K��=�����v�������nU�V�G�Q����u��M]����s���r�)���`wQ���K��T�F233�\�z�������/�����E���K;����^A��v�U�R�����/w�lm��f~� ��5j���/���'������}��'�R�������-z�]w�r4�����ta��y_�[��0�
�v���������\9��bjy������}�
}�D�f�|�������_�B���}{���]Y�-�_��u�H�\r�/�~�t�M�T6�������
�H�k�.��@l�	������O?�5�#7t�t����h�3�<�udJJZ�,Y�fw�}���.|Q=�E�@�)�&�[�h�/m(hP�[�n�Tv��w��
m��6�T���?�;'r�GX�j�\9���:��*>'
;��:u������6�<o���/��x��������Kfo���/�L����AQ������?��n�KY:=�p;;;��������%s������W��@4����/E� q Z�EA6]���:��������>�5k�t�0u�u�Bt�d�������;�tm�����^h�^{��=��G}��x��w�����X�[���(����n���k���T���}����6WT��(j�F�j����K�.u��� �k�����_����?�x_*H�C�	�=#5h���
�_���������jw�6���R�5hSun���������U��K�0���zE�C����E
k���{���<f����aA�EAm�8�����#�t��Mu{��G���/�����
$��
�J
 !���?�j>��s�6vj��1����'�\�x��u}�h�]�����Rt���7�?��?��Kf����/N�DP��~���rK_�Pa��(>5�iTb�������#�&O��K%3e�_*:���)���l�2W.�<w8��<�9���fz�l�r�sl�i��v�,�����h4V�1b��
���S����lR�L��H�����������b��Y�����4i��)�Y��_�>Cq�:��Q��r�s
~���@EQP�(�����`)�9�k�U$��.����v�m�n�)��n#_u�UE���w	������������
����Q�F�R|��O����/�^0O�c���y�|)o&N�s�M��&M��#�~��_*h��7����3t<��c��G��7�p�/���a�\@YA�`S���~�}4������p6>.@�m�����k�n�i����(x�a�v�m���F3\4�B����P�k�v���j���[o�Ke��R5��Q`�1��cG_*\q�����3�
��F����6M[��T�����R?D*m����������!���4�H�~��/���V��+���<'��~x���	|����O�b��^������a��nK+<�_���=m;����Qf����|	���	��/��zmj�������\p���U����j��
��O��-�|-�~���@/e�c�rJ���%��V��H��1�4md�]/^��,��M��R���{���/�}�Ta��7K���o�������`a�|G;'����F��M���J�
�@��7�S`�G��h^�u;��C�������K65.@	���EK�S����Feh��5k�=yv�e7r����������H_��3��E�dQ^�����`������wJU���=��{\I�6�o��<���G��(�p?���Eu|����"r0`�����?������ZQ�4p����/��&,�hS�K����@�#�#�.���4�J��uS"�,�l{��-�z������X�X�����8���K�F..�UX��\��c��{��.�`\a���P��@8Ew<��hv�f�E;��������$��`K��1cF���U��_���	��\6-.�)��Q�u������\cB�-"�FJxse^��$���36*�z�����<�}��u��������M�+�����<���<����~��JG�y����p�����EC������"r���[b�m�����b��<�������}��9���C�q��h�������c�t�1��(�~�}�����Y�.�F}3k�F� 2�DF^{\J��H�gN$�p�8�Tm�p_�8��W%�3�gx����q��:g��B����������Fa}�p�/���v"7
*�uHx}�&e8�t�g�����"�*�Rd������+�H���_��5���a��(n��dNu6|�p_*\,���Epq^����.��C��T�`���~�n����m<�;j��[��������'��<����S	��*nd�:M��Gb-J����"��M	����p��4>��_����LA�h�r1+�|��\�����������|����[�O�����R!����%�A�������(Muen#�nK�4m��Z��~va����c d�}~�)���������l�� �����*��u�|iC��"{��R�i���pz���N,��K��O����|�r�#����~�K�ZW&�EL������
�Y8�8���Bz/j� ��)
-��h��h1� W�F�������fl�"�~��~��-��A�z���R*������Z(�%\TM9�#��*�9/.w��=u���z�"�z(�qx��f�����?�K�r��Tx&Ri�o`�����})��T���p����g�R���&����Rt����/U^�6���E��D��j��]�6�f��}F.��o��K��Q� X�T��?���_t���j��O��OW/J!)����)S|iC�=��/m(<C���n��������H���>}�x��7��	���@Py Pz\�U!���?�r4'�x�/�)�"ne��X��Rh=
��U�����{
:
6��,�C9���:��aZD1l��'*�����iA������n�e������n�NJ�����5m����g��i��Y2��)w��=|�w���:��7���9��S�����,�9��$�����0w�F�Ul�lZZ�%�4�Q�&�
F�WF�{��%si�5K(����C+3����R�*��f��u+mzc���@Q��(}V���fr����f������������?�Ik�����f�y���Z� e�6�S����"=������^��
S���N;��������{B�c�YQ*��I�m��E)�T\��R����������h]��h�`�l��s3Wdj�l����<l�0�u�]��~p�m5��|�M7�I
P��nTw�qn�R��5Z�|�����tA_��s����
��8���]�@
����,��fg�yf��))��Z�D4�]�Gy$�3&���A� <S����I��t�g�}�������\����>*�����=F����.J(���������Qf_}��/�&�xep�$��~I)�4z�h�g��N;�R��
���Z.Tl���v�B�A�N4�F��`$|0�����.��@�~�Jmc�vZ�S���.tW6��<�U�Yjmd��v�1b�/���pi�(x��K��X�
���=�����R���N-v�I'���o���k�h�V�)C��Z[���t|�S�O���{z��`��M*k�4��/(H���z/�_����{����O�����1e��m�}�q� ]���Y��8���Mi����:�}�����E��p���E�@>uR��O
����jP(h�Q����?�
."]��F������F����!�TR��.�Q���lu.5j��Z�3<JH��^�?��C���n�{�ZR�i5��9:�
�5e=4�[�� ������\	�4b
hj$�K/����OQi�`c��NP�^R���.6c��AK"i$�AA�F�k�������>�t���q��e��~/6�tm����\(Q���|�A_*���p���s��Gm��_~���
�P�G}
�Q�<��\�\�h����4\�������j�jf��jK#��A���M�u�6��V���.��m[z� �����=�@���`�P��~����Y�������~�����������.Z�T�������Fi�����z��wS�i��4 ,r=�0���k����z�/���u�Q���U0�$X���']{�����M�0)�N��>���8�\8��Fi�C��D�N����O?�]���QS
5*�}�Yw�6�+V
�����p9Rx!��~��R�����h�p�t�t5�-<;E��H��U������Jd�T]L����H�x�
w�3%�	��"��LE�����L��k<_aZ�g�0|�^�|�?�:����Fd�Z���r��q����0Q^f���\+L<>z�h����C�?c��,z=��o:V���>�l����Fz�Q�<y���h�w4�K����I(t�t�:)��5S@L�-����;���p��>���$
��%]��w�x��J.�vM?��	�h��Fq���D�9"���}�6�F�k����`�w�Z&5r^i����f
�nDq�������H�����mk	��"Sz���D<����.�kVy���^3�
���U��.�]*��Wd��$�W3��u|�<D{�j��^K
P"�.��������o��������8p`~j�p�Y����{�k������{4�?�������>S��(���Z@}8�C��}�^v��GG}�u�-J��G��z�?��s�
f��
P}F�4�,<��{
i�Xx�hX�����_��J}��RpJ��4PO�Q��������*�>���Me����+�����
u �=��\Y�����"J��.��a���5���������������zL0�E
5�E�$�����
`��kQ���aR#Y����P��R�����6Op�.lktNa�X���t���!L?�:yj���~'$�j���F���.����s��[�
.�������~u"�K���%�K�f�r������0a�������*�^_u�t.��PZ�p�/^�I��R�C�Q�8j���Nz��[����)�z����N��4BJ��������:qAG[���x�~?�pRU�U���{O8����+I���3g��H�\EQ/H)��o%y}�^��=Q�3��(
�w4�L���
�n+��#�=�|��	���q��PID�i�kE����Q�86�M�V}�5{���iI�B��Cj��Bla�T�'�v�q��m���EH�Q�FR;H�� }��O��
]�,��������vB������Mj���R�O��s��^�.�G����jp]F������$j���W����
����]�{�#J�������UVz�?����_\�R�T���Md�R��t����[�m�s�n����^�����hJ�F.+����fl�d�Li��P_Pi�5(I}8����h�T���-�������?.��������o�Y;����Iz��}q)���{"�ip]q�����C�#J����P�	��4i��-;����6��E��\��^�x�[��� k��.^kDS�3�qApB������4�K���/*H�����(M�(�`��X]$�Fkx���/� 	P�E�������_�������m
l)�Yg���PY�.����"�F�7�����������Pc^���-�"A
0���������vQ�$*h���h������iVXQkQ&���[Di� ���� �Ri�z4�7{�u��=s�reUT��Du�=���K�u�_�bY��W���L���X�b<��q�-���f��&�2�������G�%sK^}����_��O�s�m��@����Py)u����-v$��X��Zk$X3o��Qn���^{�����>���~������w�h�� U#P���r�
7�����]��4��@k�@��.�o��~�p?��]z��~����3���a
(?+V���6����y��6o�<�Tn_|�K#v��7�� ��q�Y-Z�����r���4h`�r���9�`�������k����=��wZ�|��y��k�
�=u����J�����/�[����"��P�pPa0�@"a�@p��p��p��p��p��p��p��p��p��������O>�{%��/��<`��{��=��@����O6h� {���l�����d�M�fw�}�
<�����G}��P�*9�|9����I�&�<f�����+WFk���5j�I'�d�����-^�^���/��{yZ�lis���{"effZzz�+�����woW.����_�~������-[f����{�9�����^�{E�o��l��~��]w�����[� l��!��OWf�K1����nk���nK����r��K.��u������6w�\t�xi����}�����{������s�VJb��EV�fM�:u�M�<�&M��6��)i����b���V�J����5%�����f�>���1;��������y������}-����>kYYYv�������k����+KII��o���M��;u�d:t�m����t���m���Z�n��P.Q�p�
��}{��Pz��X�f�������
�S���-m>e���c����~�.��B_������Y+����r;��S}
�� ��f�h���3g�O�/)����V[��Q�6����/�[��X)�f�7m����6l��^��8�_S�3f�[�n��9'�|�]|��6n�8W�d�D�����E]��eI)�������-Z���;��;�<���V�Z.2����k��SNq)��9������7���NAQJ��:�������[�=l���s�(�r�l�2�r�-]y������O�����h>��0{�l{��Wm��)n_��K2K����s�}���_t���v��G�c@�xUr�9��O��4i������F6UV��R�jU��:t��-�����Z�j���*��sOW�6m�u������?�����R�2�[Fr���(M$8�W��&>����lr�L�5;;����,�D��mzz�+k�E���]�8�~���re��j����TW;v����������f�m�����5��U�g�}|������)=��'�*�!��w3���M^�����Mn����}��y���FE4d�7xI����[o��`�<�H����2���\r�=�������)���4]�C�e����w�qL:�����������g6y�\�[�����\����t^�t������UuL��G"!�?�s\z��n{���}m������SO�|`�z��-���G������FHA[W��D|��������g0����tnEm^}/���pn������:���pn�����ge���p������*".�P���W\a=���y��Y3_��1����y����S�h4�fF��{Ni �v�Z��HPz�/�y�����H�0�����Y�j yL�<��m����A2P�%5�`G�D��t4�����r}�����I��[�nv���G}�kK�o��v������~�?c&_|���m��S'_�d��K�
��C_�d�f����?��	����m��M��(��j��� ����v^���������p���s8w�\wM0�2V\�ON�H?F�%�C��3����&uN*��x$?��9r�����K��`�l�W���y������?���j0P<��g09�o&�69�\�E�'8����D�7y��\�p����:��^|���6L#�D�� V;������Hw�q��
f�D�Q�����/����}m�j�������j���k��KE�y���K�.n*�����N9��He�����w�u�.��M�W���_���X�z���������n{�����������[��U+����������G�
<�j���,�?����o�5)'L�`w�u����n�^)��K1�)Z�EI��L���_
,Z���/[���]L�xQ�E��w�q���?�� vZ���/�����8���X�r��~����K/���(���c�zF�]v��~���0`����Nv��7�z�Jf�9�d��z��6g�{��g|�RSS���u�j�0��Z-����~�z{��7��?���v�Z[�h��/^l7�t���?�
rA�k������n����?��+V����%K���-Z�#�JN���.\hM�4q�1c�X�=\��a3g����������C/���K5P����z�o���In�}�N�j�;w���4_�d��V`%�\������n;vty��<4S�y����qc_�d0k�,7K�M�6��f��������~j�{�v��h
�`����F���8[o����eP��GS�Z���;�<�W9��?��/_n[m���A��������t�))��M&J�����Z����N�:��B�T
g�D�Q{Y��:X�5|-6%������+pI`�p���f�����A����2 ����$'.���Kr"�R�pIn�p�l�;����U��}H�%ypI^\���E���	\�K�
(UZESQF��x*��|Tn\bD� F\bD� F\bD� F\bD� F\bD� F\J`��y��_����>}�M�4��N��k@�!�R-[��+��������i:t����[�n��c����aC�6m�?$.��e�]�m�F��mIdee�`��.o�����9��.Y����rK[�j�?$.EP������{%��W/w���?�1�c�[�v��|�����������K����U�R��Py��w|m�,^��F�i'�|�u�����9���z0�5����~�=����@���wo{������"���O>i�z������G�kKFA9��#���3��W_�o����7o��j�������G�m���F�e����1c�X�5��P�h��g�ac���E��C=�����K�}��}�������nKj�����S�N.%Y�v���SNq3?���c&Lp�'�����������G�~*��-[Z�
��Y��
�K�.~(.QT�Z��Jo��)�V�=�U�f����)�.+W���v��,X����>����4_*�����R�tY�f���]jj�/%��w�L�se���Wn2�3��8��Gm��h?H,M�4���<��S��M�E",\�0���P�y��f�(s�y����C}m����J<x�U�^}���f�~��v�i���/��k�N��,k���eff���hv�������kJ��k��{�����[�F���e��!v�!�����}M�
.�������MN����yMN�y��pn���m��7�z������v��������u6JB�$����������l�����*�V��1�Z�ha[o�������/���g�[l������z4h���]e{��?��/_n[m���A���Qe��,0�%�����uk��������u����H.j/����J,�P1��u�>}\��K1Jp���;���o�K/��~�a_�u��c�B��3g�;�P��g�Q����u)�JK�xv�m7�g.��N�/�`GuT�\�������K]�����Ar����^[�F�\����l���������������l�2WV�$��))U�Zz���V1�j2�sl}F�efU��������wm�DB�e�P[^i�#iP�W�KE
��}��'��>�l{����^l*����K�"����$7.���K�C��Jp����]v��}��v�u�����s�Fh<�
���5�z���k�#�pX�y��cG���{]D�����?�,45�.�x����o������};����^rS�O�����x�b7�k�m��5H�;�t��p\ Dr�<y�����qH��v�.,����K��i�l}f�u����c3K#�\�!����B�.���2z�h�V�@�6m���X'N�n����cS�.�;wv������
.F)
�?����������h��c�9����|mA��}H�%ypI^\���E���	\�KgA�C�h�Uj�x)�F|a���#����nlSZ�je�������^Q7utv�e��
U����Z�~���s����&7�k�Q�����)3��n}F�e�d/
�e���m��b;]0��������_�n5�)�~
������n���O?m���j�Q��5��]��>���������G��:@��K�i��"��}����^z�%w{��G��d�v�Z_*�������#��Q7�s����j�j�Y��iV�v��������c���@b{���]��g���&���J����`�X(xK`a\b���k��u���Z��_~��j������?��C;���]���r��$��K7�1�^�ew�2�6kR�RS��K�R���j_M����i���fs�t�I����k��t�M����Ov��&����r��#��j�*w�����>{��G\4�����r�5(M��ZD��HVQ�5�3gN�6-$>w.�9��w�S�Z�j�.�R��4�����H��*�����=�P����g����[�2i���a��?)n�z%Z�2��C�?��v�W���hD��o�m�����V�Z�6�OT�,-���/��Q�����J�C�����4e��#��uo6�qS�Tb�IK�bs��y��,�.�G}����3g����]9^�cVHY�S}���G���i�-Tm`^e�sP��^������6�������VlUr��S�Z#�d��1��GWF���������k�C�����H�}�]�(ee��p�N�j�;w� ���[,X��-��F�������cG�V���E2�<y�5o����MDlf���f��i���lh�FZ�Zi%��.^����~{�����A�233-==��5x�w���\��Z�~�\�YGe�Act&O=�T����zM���K�[��Zdee��������N����}M�����~���/��@�u>�&��\�W��u�h��enl���A����������^N.:�:����K4�w����9�8d�����+pI`�p	fj��eq|.H\���E�%9�$����_Y��E�=�����X�f5}-bE��b��.��_�sQ��#G�{�n����u���A���>�kb��Sz�c���e�����?~/��|������G�UR���Iw����2Q6����2"9U��������o\P�*r�E���:�,�v��[ni�[�v�?��]~��n�.d���k��8\��$'.���Kr*I�������������Y�.���w��%HA��!������r���6{��O>i^x�[�������e�4�-[��N�:���������o�{e���
��u�Z�
��N;�d���=��3����%Q���j�j��R�!��^���Y��[7_�d����m�n�*�O��t}N}$
r�g��G8�B�qw�����\������@�-HO�|�����U�^��lJ3���c��si������f�)��"�����W/�\�x�����R��Ky"byN�Q�v��?��3l������FSj���I�&���C�=�9�lA��"���Yi�88������U��nv�{a���6��������rM���F�q5v�X������P�ED_5�E�����M���g���o�,]oU��XfV�����dgo�IU�u��L��u;��-|-����~{w��#����f=��c�|�����d��];7[u�]w�5�g>|A���������Tl_OZd�^?�����<�N��{;���6��?��'.����{�M�/�:mN��|�Jc��-X�����G{��V�j��,[���d���v�z]��X�i��g���,[�6�����#voaC����HlJ��uP`\p��=�Z�^F��HH�u9~��g_*�^�J����Td�?1��zt��^�e�j�Y�:U�a�t������cG��7��C�q���<�L�����������n[�j����0�S~�0u�N8��P~���,�F��oo��\auk�YVv���z����;�I����n��N���u���um[������v;�wZG�V����<���>����e����v�%�� ��o�i_|�?*�\z���Y0j�(Hy��g}M�hi����J��"������]�%=-%�y���T��5Rm���v��y)d	p�������]t�����v�����XO<�D~�al<���}����iS�9���5k�t�ZU��k��`Q����K��V�7c��'~��^�jUr�����k��f�T{��v�1���f�5�a���|[r��v�)������@�������o�tT��������f�|��G��MG-h��R���s��/\�0iS��e�]�KyN;�4�k������.�2LA�������x/���v��Y�:����H��U=�F������_�X%T�e���n�u��A���]���/��>����RF���6�����N�����Fy
�nY�~�[x3��q�8�u�G}�/������>O������^�J�xE����}|OO��*z�(��z��5kf�l�������O��~��i6����j�z����r�)���_�Gn(��Q����E����z��%_~�e�g�(=��G����t�I�Pq�����K�=��Om��US��q\�%!.��-s3XZ�nm��
�
�~���F����{.��������o7�t�����6w�\WNKKsS�Q����o$hI� ������
����Fx���\���b5����uY�����b���_�>���q�W�t]��-
����>��o��.�{�v�W_}���sO���6m�&������KE[�fM���BTD3�����vJI�*��Ul��u~����2�_����(m�f�,^������
�hD�AdW]u��~��6~�x�V�+�����<�^6�h�d�t��L�����/Y�:/�������o��q�n���NGn�e�z�y�6���m�N
��@|�j���]:w��RQ�o2e�7�Bi��wQF3����o�~��[�p��v�����9s���k���?#�m�����g��v����\�oX�b�/T
���R	��Ua�����/��������}���a����[l��-��'�l#G������1-���o��	�dV�vm���{�^�(�@x4�F�)]A@P�x��4;����>#�jVO���9�jm��sa{�O7�X$OE���'O��'�j
�6m���O>��<|����������:���x�5k��[��H�s�9�����}:������������k��o�[��%�����m�H�7:���cG7b��g��5e�����wV�V��"��2!�o��n��f)�q�����o�F��5���Sz8�xX�b�r������Q����R������
R���������#���L|\�fK,�=�\7�L��|(�[�f���/v�g���n�k�
������U�������V�����d��0���{���Vq���(��.��W_�XU����[o�fK�K��=]n��Rn���z����R��v�m~/�(����V�Zn��_=���&�c��{���F����~��^>����[����]#�22sl��������>��fU P4�>��~F����*
���?o��r��1���s�=gu���5y^|��b/b����,_�j{^:������I���*n���k�,+;z{E������9���3�%^�.;[x�w<hm�x��w|M�i��R�
0��$'��
�+����e���c���y{�5�_��R�������^U���v��-m��l��=�(�/_�K�>�`_*�-��x66ZjTK��US��Of��i~;���������e+3������s����n0�/�~[�>+��;a�������gF<$l�E�����5I���W���V����������&M���fk�U�v��m���dT�jU�fNI-X��6�l3��1�p�|�q�W6�Z�_;�U�������������l\��htl*�5h� ����������s�������'������J�������[�=��+55�FLZg{_��=�����Y-��ed����w��6��=�����~;4�o����1��-��iS���~����\=�'�.�����o���N�� ]WJJ��x��V��Y��:@�:���m�F���1If����E���]��h5T��N8��h���s�=����mW>���z����fU�Rl������n;�{B$��hMC������}�Y��]����7����6��>���1���:7s%�-�T���u�#�8��l\�'-�#�}g�|���SY�f�K��|u�uk_�����yng�V5�?"��gw��n�����'��n]�%���M!�.W]u���z,'�|�+k����
j���w�}������J�e�]l�vp��9r�[����n�Lvv��'�i�P�~
��l���w�n�������:���o��Jo���������)K�a�tSj���2m�����zZ�v�������`����+�/���]�o�����z�jW������s�=n���/D��������)[�RMO�4�
���6{��v��d���>O�����R�)�r��5�����!Wlk-W�����p�O?��M�=z��4����s���z����e�<�����M�0��i��i�g�}\N��m����+�=�I��7l����
��J`��y6q�Dw���M�6�lc�s��y����cG_P8]���T������[��y�)���p�qWx�6�H`��������Pr�!�<n�8w{�Yg��5j�5-ZdK�,qu(�k������>�n��&Y���c�����}m�������_\�"f��k��G~��no��������b��f[�:U������7�`]�2[��H���d_�ti�|�_}���]��]��l�#�J��g��O<�����KC�p���D�]���`� ���C��`%1y�d{�����o��O?��n�,?P��>NN�y��&'�k�����v�M�l��s�a�tK�=G+�dY���l���m�m�#���bc�������;��n����s�Gu��
��y�q�1����)i��M�6��g���w�q�5k���?Z�����������S�Z��U-5������h��S��e��������?EB\4k"55���_�k��";����
�������k�~����Q���
�c���4+(��3z�VN�G��������&���S����~�1l��)��6}f�^0�1l���Q>���i�>��g7�1lo�m	�eg����3���u>��L����Z�����+���o���6}���U���������V���U
�����n��zkw+�����%(��,\���J��\�e��v�%���[�e���|������^������-�����c';�{5[��B�S%������jx6i�����S����M?���+M�\W'E���Pz*��-�\s�
4����ZWWV��j����
:��n��i���[��N�bh�������b��)����o�.��R�i��{��k6��wZ�Fe��5v�����?��E3�L	:����[g�{�v�)M�=O��5]9�����r�$;]��y�{/��	LA���1�.������D.l&��.�4r�V�z���7��h���0�j�L5�����]m�������f�Rm��������E��U�w6������J��H�}�'�V��$����Hm(���>���^�������k++��fX��u��q}�����%+��Z%l������k�����74���7�����Z���'�W�5������x���Z�!C�X�>}�^A��-�4l���V[�$}G��C�.]\�C��_����w�E�l�T���"1������g��J������&��u�kv�-����Y������i���:t��R�b��	.�2l�0������z�0R'Ri�4j��.p��H<��
�(����_~qAutl�����WpH�P5Tb�����3m�}���\]�W'OA��}�����o���Q�G�5-��q�%}�E�;����o����@r��
R��o���ID������j�*�!�.����JV^��v��"HO���w0����p<��\KKM���*�>3��gd�%G��S�k��������t�h����|n��fI�]��S�_6����J������n;���]Y�kM���z�
[�l�]w�uv��w�����
�����v�G���	�ES��vpI^\���F�%q|8f�
�`�-X��j�Hs�4�?#�����M��c�[��
jW{Y�].GB\�o��v������8��~F7��
�����4�Qp�$������Y�fY�^�|M|�|��.�OEuV�{�=7��2P`N5�.�.��h��PG4����������:u�`-1$6��U�DAl|�WgZ��'��.s��t�z��L�r�Zv����}�Z����`]\�:���1���]��=�_?��3��qQ@�x����'���d��.��b�/w����L���
"��dA�%ypIn\*��^d��3�f�[��<Z�%#3�V������'lim[l��!�R��.	��R�5l���6����"��G�����H5u�����������sk�����<�%�y��g�R�<��c�����Y	~���A�8����lr��n��o�^���:c����jY�9�lU��~��Z���l�)6��;�����6����V�-���|)>�-,�P�)�V����hW�d����5�r�)f��gX�5�����G��6j�_�MP�6��D�D�}���QZ�No;����1������j��w��H\�Y�������h-�.����+;;��|�g��o���b�����Z��J���no���	$&�_"g�����.�@�����]�X�_�YI�H
*���]��,g���M�kEn?'�R��~e����n��j/���m���Q��|���v�]w���h4��7������ti��"�E���dQy�Yg�@�f��q��6�R?ha}u(����h������z����;�?TF�N^d�����MY�7�+�?u<�����}���N�a��f���g�}���4k������:J�P F}�U��a��@��c4�N�����o���������]G����R\���*v����;{���K�T*�
p�b�-\�D�����K_[z��(��n[�l�kKF��b��@�:*Z|58�0���z�j��<���y�\�_(��Z&��u�k���V���������L���l�Y-�����*���wqW����s��y��`���V�~}78��4I�k�.�#~��Y���I83��)�������I�Q?F�|��������|k��/�US�,��k�lmn�������{{��{��z5*�
p����-X����k�h����]w�KAU�>��v�m77rIA�N8�HJ��P����"�����|�Rc[[qi�����^���6z�hO��g�}X�
�JF���a�}:��W+�Rr�KVfX�vu��{we�������+]�]����{o�>�.�E��������\���~��-v{����#oJ�����f��o���7�y���m�+��W��oh���u��dE��[s�r��v���K�Ab��k�4m���"y��W\���{�q)��fT*��;��IDAT�:�M�?���m���r��t���@���9sf��IIU�Z����F?������Du�1���V���X!6g�1b�������w���'����nJ���7����;��Wu�����H �<��.�����o_}��|��.
���n���v��G�>�QG����f�m����w3����J_����mA�������[��.��7����o���m_�_`�m������j��u2�rl����MC������S:��w*�ey�Q����O>��
S�E�Q*�O?��>��C7�e��Q�c������:u�k�|���_���������X�|�?�;{a�l�~Zj[�2����j�<���{$��4^���W��k
���7{���\��w�������o�����_~��+�����E������U+6l���x�]<6rS�*���,	����m����~����q��{z�edf[�i�]�h�zk����{�.v�E]�Q���HV	J�s�=]Eo�y�����#���_��^z�����5*L��������R�����R�4CI���G�K 
�z�p���`�:�S3���s�s��������o���1�IH��t�Mnm���l3f�����cJ��>����������*�����<�@�1c��|��v�E�_��f�?��#���);�h����o��iF���IF���\��0���������p��>�{;��m���*�J�JV�Z�=��\���YM�$������ys�F��SN�SO=���E�����\�0�����^�1����m[l��HV�3���A?�/�n���[zZ[�&��n9x;��6�H �iE�=\�E}�=����P���)S\
�-������������s�m
p����\��8����5l��^�����Mk�4�����VxSJ���E�m{��7�#@e�|U�]��$;��16k�jk���II��R&+��]lc�i���o�9*����n����J�)�H.#\h�_�����JkX7��jY�l��WK���w�&���#����'�|���
:��������0+V��i��%����J��4p������;_����|�M�m�5�W�
$[�6��gd����w��cl�j����LN<�D_��!C��H�<>����KV-=�V�������;Z�;���B)�^{�5��+R�&+==��F��d�}�u��h����u�'��A*6����d���x�}4n�[��j�[���Q���^[�5)O��E�F�e��7������^��k:��6���Xf�]��V���uD�srl����{7�e�6u��b���C�h������{�q������-���S����l��A�b�
�h��d�Tn��f��.33���d��7�l���������5��f�T[�>�V�����oj_>���9��?���Gag�u�[�s����6O������'�|r�M���?�x���!�b��������w��	�]/�n������#�_�e<���vg�`�P�^K�u�f]�t������������+����y�;��s���~��={��V��`N���9���m��v��O?�;�0�8q���l����_s�I'E������{��m;�����Y����;�hH�C�II��t�"y���nr�{9v�&.�#nc����ia����6[�>�v��������v��V�z�����r�V%G+�EX�p�5i����H����x����9s������&>-Zd�7�{%�'�
���v�i'�0a����:���T��i�a���--m�~a�|��U�n��6��`��u����[���Z5��H&J���y�R�M���FK��'��E�\G$;���_;o���<����]tJ��e��Yn��6mX������ ���~�.p�����_�~���S�(`���r�J�U��+�o��n--��e���nM��um��e�66_~��{��7�����|ml�|w�y��}����dF��f��}QJ�_�X��k�Y?�|T��`2���y]�j�������H���;���|v��.�������Q�s[j����nf����'>��&M_f�s�1�)U,3+��e�X���v��
m�m��uy��ll:���/���k�k��~~�~n��Y��j���2�����\XE
��M��������K�:eB����Kr"�������f�����R-=�JnG$;�Q\�.>�����������K�G�%6Z ��c�u�9s�X��-]����{o����l��)��S'_[v#G�t�C������A����5%�Ab��h���w�}���'>����*��Q@�:�wU���L6:�:���V��z��������
�5����>����j�m��@K�j)������������V+w?=-�u����k�l�&������������M��V�����������J����8f��m����k��o�����o7Z�2�H��4j���jT�
�E����Kr"����o���v����/3���`�mUjVK��u���������#+._�\�����n������D���o�����Z�j��])������x�����G���@�������/1Z�jU��_�xq��S�}X�����C���b��������H>j��m�����H<���6��4(���Y��[�l��nY�4�"5[�.����f����}���6�#���:D��$l�7�`��F�v���C�~���o��v�E�26X�������]L��w���3�pe�x��j�s�8�>o��f���ck�g�Y��n��B[�MI��v�qGl�g�}���w���i���I��~Y]z��.��A;
,�&��Y1������#����s�/�������~�~o
D����/����m����hD�9�lA��B���H�����r����_���v�u�����g��U��T�Uk�,5���#��w��P���[�%d���s��������P�E�W����O<Q��p�?�N�+Z��6��X�hEu0��y�1c�(H �Ve�e�l_��R�UKO�����u���w��h��P�E���Q��x_�~}w�������9���e�Y(�>��[��_~��%������@���@�-����$��?~����E��nM���Z@_�P����z���v�uc��Qs�z�T����Y�.��
;n����A=��[�]q�X�bJ���c�=f�>�������^R@�^L��5�Yi��W���A
��������:M�]v�Ko��E7����?��&/u@�z����d�t
�)�3�����_;��X������N�e��T������ha��������?@X0;d���6}�t�GP�4���O��.p�SO=�����W�/
D�:��#i��`[�t�[�<��/�+��;��k@�������	x���V��'�����
��,��D�V�N��@�y���v�
c����+#����j�3�m��L�o���nn��������	�
�X��s�z�)w�����QG�+A�i�����4�%V�������aI}���.-�#�<���*�����Z:t�?*������N�:��[�v�M�SvV����/n����Q-�-�����~{����������W\ap�+k��0�[4�d����W�r�p�fkh�L�5h��.��rw��w�}.�3�<�k��X{���}��n�����V�SN9�n��fd�����|�p:'Gy���������{���j�B;��8�����jm����fY���%+2l�������l����5�W�?(��
�h���T^�`�h�5JI�����o��f�H�
�8j�r�!��<�_~���3�������k�z3�D��:ca�������m�����
�#��������a�R;��16���V�V�U�b�p�z�{�&����Y�v����	.�"��9����m�&���t��f��r�Am����������������{w_�g�����;����`=��]�E����Q
P�iQ��9P.%e����]Aj8P1�2c��u��v������~L��i��-���iQ�^���=ti7k���;���
����RO�tA M
/v.��B;���m�����%��_�[�_��S'Wh���t��%�\�Ky4N�+���n�����a:�
Ji]-������L���-&Y�Zj�m�+�C2���n<��i�D(6g�w�/���{��%���]#r��p�G����g�v����fqh���Y��m���'9E�N8��o����ot�]@kN6�}�}��W�6��S������v��?�9��h�����5�,%�o���V�N�=ry7{��l�6u�#��H��bA��o�9���^p��z�r������O>�R�i���P�����wS�#��S���4`�H�:�,_2[�~}~�g	~�HJg��r��G���f�vH��7��=��KM�b�����:�����f{uo��P����_����W^q��f����'����-�����o��������c���<ZdTZ���g~��no�O]j�k�Yzj�-��OM5�z'{���G7�4(	p�������C����>u��E#5��3�t�s�9������kd�F<i1���b�
�h�~�W_}��&�H��m������}�x��M�%������v�5�m����}�����h���[���g�I�O��+2�����bM���<r��.�1����������IKKs[`��vr�f|o<Zs3l����T��k��z�8~{���z����_�U#��W�[{r��,���v���nG���
����h���k_����}��g�zM��h�}�����v�i��}{W.-l����k6�������te�f����d��6��R���v����@�|;y��}�7����m]F���Tq��������d��8�^�t����U�e�2m��5���v���l��@i�����V��(����K]�Y����0a��_i��qD�=��c_*�R���e����������e{\:�^��o���Q-������+2����m��{����G�+�.���/��bW���_��w�}g����+_q����/�rE�`K�Q�(��#wz�QR�aZ�E���k�h}����L�����_e����w�,8��)����&�d:�KWf�EL�m;�X��i��Z�RR��ta�����Z�:�uP�s�����skg�����6���%�i���h?E�����^�Z5�?�k+E������ ����q��5�@�j�H���Y-�����������l�+��G�M������Y9nA�������
�vt}`c��e�F��
�X�3�����4-��������O
T�Z��;�<:t��-9M7:N���N~J�x���?l����U��Mk���������������$�L>���\>��������������v�{����#�z:|p��M��E �VJ+����Tf�p�}�l�p.����W�����h[�Y-�.{z��X��{���~�����=��]yX=��y�-[�i�I��I+CJ ��N���$�~���O�w���\��Z�~�\�"��U`������{q�-�p��5`��1b������[�������@>eOS��A�Y���������v3bz�!�^����%K�X��
���*[{O�D��	g1@r��_�]�t���3Hj+h�b}���.������`���d��K���~�����5�s��r��Y9�lu�ukW����6k}=�d���>�:t(t�9l\C�q��H�\�[,�~�~��]y��av�QG�r�(�2s�L����:��)T�F�:O�����+�U�VY������^z�%��~�\tL�r���n}"]���|r����W�����|r�yUCg��E��Q#w���lr���.�o�.�&�y���b�g�����a�k?�c��l;t��v�	[������E"] ���E�d�.���N�fU$�d�Tv)�"��W4������{�l�C.���K�"����1�2k�j���_s�7+�~�47sE�V�������mgom�nY����T<Ip����O?��R��7��t�z��5eS������Q�3�:+^p�T�X�H���*�.(����/�N�y�������{�m�����o���pHQ�\tQ^����s��O'�L��.�&O�l-[�,����d���l�+��Z��G�gf[��5��~;���2{�lwTP1%k�e��5n���}]tQ��J��\���g�}��JN3a�a�(\�,�$/.�-�.����;^��F����i���� �Z�i�j���'v�^;6�G'?.O8��p)��C���={�u\�?����������6�/��"?�2g��r	����hO��������[�2�~���A�8��)��V����$Y�������l	�����}?����cllZ�P��SO=��Q��_�]y��n�36�w;���^�(Yq��uYv�K���W����-�F����(W�QJ����-���v�T�T|	pQ�*��Q���o��n��&7�)r���k���n��*?ZL1���F�3N4�Y#c+���y�,��b���;�����[@l��"/mgI
���L�����I�����Y�������v������������
x)�@4�������������!'���?�e{_��}<v�5��n��Sl��,����[��������?�8*��J����kd��;iJ��g�m�<������6AJ�e�����%�\b�=������ys�: L�t�[�$V�.�O�Lh��x���4�O�����	h��#�8��%7��N�j�;w.4e��[�P.)���������c��NS)������o��2�����g�]'���.j_-]�a�������f�nJJ����M�6�M��������������-�^�U��b�i�����P�O���.]��)���>�H)��H)��4���b�+QS����\{���bu��������[��f������=~K���J��bO���n��|M��
4p�a�`���[l����F���L��h��+Vl�E����t�Y�����2[@�ddf��C~��?���-R�@�k��.�&m���N��g��n���S�����/S����?�kG�8��z�w\�S3��/�jmne���m;�-gt���T|	p�H
�����|M��he]����H�yMT�V�Z�n��Qn��F��a�)���o�c+��7��Z�j�n5b����Yc����������vV2�r,#3����vk���*��:2
���vb:�z�q�q��[�`FbQ�L�\��Q������3���8m��~��z�$[�:���N���>������U{�����K�Z����@bH�������f\q����:A'$�c�=���)��n5�e���.U��w��g'������Z�Sm��l�^5������{q7�e��L�>l�������[������Au�,�a�vp3�w�m770��2���`J��.���);��3\f��9n��{��G�XL���.���3���`�5�[�4&L��
����t�����]�D�p�v���"=��k �~��n}���:��v�)�������Pz������tB������q����o�Y-���k/�7o�[����W�������R-=��-]�i���c����n]�v���>��^�iG���.��u���w���O!�>P^~��7��~��v��'F��h;���L�Q~���Z�R�E�����/�P ��G}d�^zi~����}����s����`�:�q�/v�����i������*�bu����n���d��eg��k#� �$\�������N;���H����=��=����W_}�y�w�O���G�X����k�|��y�����sQ��?��1Z�|��r������J��F�����neC������k���vh:nQ�����������(?G}�h$���z����w�}�-N�����o��A�
,�4a���9�;���]�0Z|�����{�$����{^����y�}5q�[��jne��,�?�������v��d�AbK����'���A����=���6|�p7)r{���m��a�Q�'��Sj���3��SR�>��/����B;���6k�j�S#��ed[FV��{Q�����#l\'N�w�y�����g��y���6��7n��E|)%�_��`1���9*�.]�X����(���|���v��c�����T�5����uY��r������v�Szm�$��
�dee�K��u��_~q��8�7)r;���l�m���D<�y���T���;��3�^�n������O����3��!��B�������[4�i����������Tz����;�����/j�E���)���t�M�T8��o����3����!C����'�|�k���������!��q}���}�/� �f�����}x��v�q[Z����*���u��hd+l\J'�4oGq�;?#F�������3`�;������f�m|	'3+�.��G{*��R�V��T�b�VeX�����7�h��W�G���U��,*��MC�5*�^��w�e�9��4n���~��5:����w��K l�O��~c���Mu��j�H��������S������-gt��uh���p�C-�>e�����a��V�fM���k��n�Jvv��7u�TO�%K���aa�}��-X������(%���9+������]�cu^������w������"8������9����`���n�������-��j+�^~�e��~�UW]�K ���/��N����lKVf�t�99f���6-j����d��f�5��$���(���p�{��}
6��-[���.]�Ky�z�)_��	��3���ht(�[#��q����5���c5so_�eG;l��(�f�m�f�y���?6�:�R�Q�F�R�y����Y��^��)r&L�f��~��~������%N�����f�_��i��V�v�=v����
;�V[����W�\}�Q�������*��n���\�JW�:� ������/Z�%l��e���U�V�R��s��RA���������v����NKZj[�*�:��k���:lF�����G�6m��,Ja����z�r��>�{��}�����Y�a����/�Gk�j��BP��^�i7����y����@Kzn?e��L�7���;��w����L�	$��
��_�����j>|��1�4i����;.�Ux���O�l����	���HI;tN(�u���������`��kZ�E������.� ��a�����v��.�����{�m��Q����W��6�����	ZD����&�h}�hu���0�}#*���f{]����y����f��Sl��,[�&�.9��}��nv����G��P��U��?�`���������M���iHd��v`v����X�i������[���y���v�!��Q*���q���Y"�o����6m��qi���_�f��~��v�����_~i�o�=��@�?�m=.�����o�[+�jTM����m���v��-��'��3z��G�OB\d����w��5���v�i'�zk����a���C�����*.�R�v��{|o��sC��Lk�����Q�,�����j������/���*�w�����v�M7�����?�C�P�}<n���g�
6�
����m��g���4����e��\p�4�2J��*����L���N�c��0�/k\��Y�����win�����	 ��y�w��O�����������O�6D��D�egg��TG�P�|?u�|��v�3S,==������cKWf�u$?���{Q�Y}�*�
p�2e��q���O����o��7���K/-r���m����1�r��d^42Y�)������+l�+��Y����Z2�r���[��������R_��s��w�}��+������A%�v������+��6����J�Z�|���[�n7�����`�vLfff�@���9������{~�5�����������2���������n{k������A�`�����I�&�<f�����+olZ����v�y��Y���,RX�-���������(��|��7�4�x��6s�L���}M|,Z��7n��
��������~/�N>�d{����^A��q�~/��]���N�j�;w���4_�d����6�l�k�t!J�W:v�h��U��H�'Ovm���&�����kw�������R�V�vl��J�n���6�#�f�r�6m��T4��������~����������A�~�\yS�B�+g�}�bh ��O>��Jb�����E�Y`6��(�P��XaF��~�x���Hw�y��x��~/~�>k���-Y����)�������m�����V[�$��O�n]�t���$}�������uk�S���E�P?�n���nW��d��������V�F�UM���^1[�6���jW�������Ysy�^�g�C�V�F
_�Mi��!n��T����a��SO��/�h
��)W�B9�_z��"���{�}�Q��MI�MuP�r�c���W~��u�-%���X�i�u�o����`�@�Q���K.q�\p�����D���+��T�_���\v�eq	���\��xN����!9A�-�'8�|v�Si��W���;_���n���m�5�����jX�����u�t���$�R��\�U�.�L�L�x�������\�p)3\�0�%y1�%91�%ym�.s��+��f/Xc�k�ZVV��\�e����<��?
����/�g�$��r�=�X�V���&^���K7�v��7�[�a=�f��}�����M5�E���~����zk������W�������������j������e���������M��s������f�W��b+�d�;cW���\�����_��m����w�ig��jWO���(�&��MI�]�'�*�]���v���]��m�����+���
p9r��p�	����\��;����7�m���(�+V�NK4;���M�0������f3E��G�������E�%9pI^#���������ji�U�jZ��]�m�j���t��:5�G!��T|�p�~��]�p����&����m��4B��>�.���U�V���);�]���.���{}MA>��]q�~/�pY�x��+H������I���N��6��7�g�C�dZ�	���&']����m�E�����R��,�|�����V����h�����'I�}�^]������&�Rs��r����,�]�)t}T�R.q��Ad�[n���&���s>|�x���p�6mZ�jY�
������;��k
�s�\��QK�t���[������������s��������b7��E�%9pI^�py��?��/�X��i���rM�u���=v������K�����#�)�X������`�|�I�SUh
��-[Z�N����I�\�$��FwjvKa���;�<[�z����~^�Oo��67`-�{���P;C#���Utj�j����#������Z�n�|
��������$][��X�i"}��b{x�4��p������#�2���=�7���io-�6HE�k���r��H���g�}�:X�5|c)���.�����6��9�WFt
)�W�o	�$u���(���~�z�������x��E�������xZ�.�.<�~�����J3
S����6�+�k��By�={���p���!��6�R�$��v��
�R��W^y���W�����r��������~��G�������X4?y�h~�R��E��W�E������9�~������E�V,#3��v�n�zv��l�-j��QQ���������g�(��~�h���~��#6x�`�����k�);�EQ]3\t1H#L�$�[��V�E�9C������
~�����������LNuP4��A�1�W���Z-�������W7zL����vnr���8������������{���"��9s������]��S��DK5@�e�P
��c��� C����p��}����������D��"\���E�%��?��a=k���M�k�=��4����V3���V�2�rl��,�r�Zv�����6
�#Q�p�x.�����W^�kOy\4�W�����Z�n��Qv�i��}�����]x���b������:����NzE�YQ:���MA?$��3�����K����-+���,o}��^�f����:�AnT��]{tC��q��Z�e|����B~"�>(-�wuEA�D��5�&Q�w�}v�5����J������}��,��h���kM	V���t���(�$/.���Kr�����`EU{��U��������Sr�{��zm��lT�.<���s3�$
.O�\�P�9T�����V�x/(_c
��u�t�HA������UvO<��]p�zfK@�/<�E�Z�.�A-����:��^�E���l0�E�����g69ht�R�i�K��
�L�V-5�'kv���i��v�STe��L;`��v����F���Q���AA�����}N��b�J�]��D�]K�������^P5/���^����8��k����`�1�H_\X�E���+�D���E�%ypI^k�g����h_M^�f�W���������V�z��{X;q���;��p�x.�RVj�i������p�4z�h�}���^���SO������k�$/�pIN���������k���������R��}�l����v��[���1��K��,���;wn�\��Mm�}�������>�^;]D�,��={���+*�"�=����T��p�!����$/.�G���p��o�7��z�*.��z�U��{mn���R�,'2.O�\tq�k���J��`�n���~���f��3\
�E/��ee�����Yg����f�L�:��K�w���$� H�Y�J!����K����,�����������)�v}�Y���Z�Vth7
VQ���K��,��N:��iD���;����MV�\�����A����#��-B�%ypI^\��_��g>�e�VfX������$���Z���[�����5cO�M��K��p�|��KT�2�":����H�y�;����^b!����$'.�+���-�M�O�.�����vt���U��uzMw���X�'._2\m
���$�lA�o�����!����$/.�a����7����s�����c+�f�����~�w��
��&.O8��)W�^�K�)����g�u�]m���~/�%r���V���S��`������Js���/^k����p�[�J���bw��w���+�;L�``��8m��|�x�����bu����fs�p�zk�Ym���&v��l62�&l������?��Q\�i�]v������%/�-�M������W������5R-#+������}��e���GJJ3\�����������[ �����;����{~�9�Z]��O�bKVdX����dn������v-�l����@��^�u�n��|����N;��	�^�y���	�6�'��a>��[��jz�[�z�������]���>TvZ@�����{�g��9��f��=
�6#��{b�v�X�m�
�W+�����������cw���������uCw���M�b��*t�%H%�l�2w��������?����`�9�����x.��'{�����t��������ob����j�`-.�2R^m��}��gW]u��K�����-[�=
w���������[������b��f����v���l��=��]���lJ:�r��G�/+�b�C�������{��``S�1o��{������V�v�ee���v�)[��v�G�l�����'��������J���,X`��7�{D��'�l�����G���u��z�[�>�������la��mg��
�" �X%��k���:�``��d�;�����cV�Z^��J�w���N��<�Px����+�^��?�5m��������=/eO�7����U=��gd��U�{�F6�������G�H�Tb
����~/�<��v�y��=6����j7<������Z�V���.m��g��n����G���>h�_~��K<J#��)_������a�
xn����X��i����f�tj]���r���X���.**.���?~��K�=��]p�~���h�z;���6|�?��^U�0���v����Ww���*�H@y<xpB]���K1@T��^ag���]��D7�K)���s�����������Y�Qe]�L���=!�	D	�����,������,,���!B�xB�m\���V��$�L&2Iw�~I��z%�]�n�w�l����E����(��
.
������S��O<�s�=��REQ���e�(�f������U��,���4<q� \tTwYFQE�r���B��m�����(��X�����?q�m�`��J���P�]J��s�����cp�|{
EQ�\a���;v�=��Pl9����)EQEi:<n ���c������;2�n��.T���*o�<��6��VEQ�4�"�0���-��(Jm(�����8����4�9^cX��}���=���#���-�5EITpQ��q���O?�S����?�b��(���G]8�������"%0�5^�aZ��K��(���������� _��)��(	�)����������q������cu�b�3��/��#wmg��(J��r�K(B8^����
g��Q�������b����oO)��(J����
\��*�\W�p$n��;�'n<�����(���F���a������!0��%�\bO%#�	�EQ��}�X����Y2���
�v��3���->�{G��!��K+������B#�W�^����^B�7o���4k�-Z��-�9��_�~�����-(�a��w��1c��������(��l)��v	N�}"\.7��4T#��p������]����(�m����_m��j�Jl��
��D}��-��Kr�!�<y���1<���I���x�b
#�(��_N\�C�����������RQ���[��[���z";�c��(J*�R����F��,,,����h�dgg���
EEER����+V`��i���;$\��xv�e��]�|�I
#�(��l1�n�}m��=p�VRqE#�6�'w��n����EQOff&�v�Zm�#//O:�Q��4�+**�D�`�|���8p ~��W{+JcHO�E��}�����(���2nZ!�q�D\��4�W1	��X�����%/_7w����i�bEq)%��\.	w��`��w�AYY���'�+++������A������!6n�8\s�52Oi]���{{j�C���s���EQ��X��
G�8���J��0�Oie����?��A��%EQ6�+���:��	'� �������Wc�������x�����2��W^���"��x��r����S[���4��IPQE�v���g��.z�O,Y@~�e_0d��x��Ax�����C����(N"�r�������s��#��k-<�8��������/����J��?�P�<���d�`�O>����]�a�����?���~�9s���������UD���zJ�EQe���o�p�m�`UQY�nId�����
p�-��EQ6v
��r��G��W_���t���f���e�{��?��q��W����*U�������������:u�kEQ�m��k���?q�]�0sar�<p����%?�����^�n(���:�+��LRNpy�����sg������;�w����r�������[�r�]S?�e����er��c���2=c�{��s�EI,�8t�Rx�=7����%��=��38��s�)EQEi:>������J���c�[�,|~�(���AYU�^RQe�x������KCP��8q"��Y#����'%;�)��]��o���b�*��(���xy&��q&�(�\,^���37�+N�������hg.E�H)���	{�1Q~c���n�C�F����]�����d]���D���t�Mx�������J^�U�VI��>�{����Tj��b���Pl9����)EQEi�*�8��_������� -
X]�������A����c���(��)�� ��;H�^����2
I�l8]���
{�iHx����(��-�����AW��O�-G��%Ce ��`���_?�3��]�z)��DJ	.^�W�2y$����_~)���$3g���!���G��/i��~�IC�1q%�����l.����fz������{��o��������=~f��P�EQE�L�_�#������Y$�$�]0����^JQe���Kd}��_z�%)!��O�.ec;})us���6���z�(��l����b�}�Ox��E��1�o�&.,��[���w���o�� e$�B�]p�R���S�N���P�H%��b4V~��G�U�H
��CI�nH+�#�K��$�����N;�55$z�-Z�H�Td��Q����������>�b��(�������q��"�����D�e��wn�=�WEQ6?Gu��/��b�����6l���p�!����K}B�a��i�����.��6���W�EQe���j,~�/�b��Qc[�,bd�f����p�����Pe�$��6>��cD����?�n7�u�&9R��(��/��
Co�3��{���!����kW{jm�Q�%''��5��������A���'e�2r�H�7���tx��:�,{JQEQ��+���'��+^-w��1�_3|z�(thi��QEi
&O�,����/6��0������_~���|-��!6N���nC`�}~N��������u���2������K��M���4A��(���sJp��p�SS%\X��)���wj��������f�>{
EQ�URNp!o��&����x,7�9s��4{��;��>�e�l��#����X�l����R������pfd��Y��|V���O<!a��{��'e�3b�I���P�R�EQE�T��������s���o���9@ai�\�~����^����,�������	?�(��T����+�C���6L�������["�z���=��i<�}��e���H����?h����g������< �6��:t��EQ'�tu���W�~��XQD��LCQY9�^<��x�����m����(�:))������X��������u���	���;�KYL$�I'�d���B�.�������!�����.��e�`�A[�f��_>�����6��.M���C������s�i�:H�����Vi��:��5����J������y�������-���,������1Kp�Uc��$�r(G$
�~�����{&�quz/V��Z����)6;���a�uN�~���R����v�����Y6��EA���@<���sa2$�Sx���7ItY�t�DRPEQ�M0���������T�Y�W�*#���������;GbH�{
EQ�4�������^�-[Zq����!��V�\)I�w�m�:C�QDy����GSC�l�`,����?8������e��(�lc�=��n�����v��%>m����k�(���U�)���������P��(��q<����ro�}�>aWI-x\)��^F��=����2�1������������b���c��8��
�,���Wb��������,/*QY�.���/���������!�����'_,;�^��I�DN�T��Uz���?��:?�����;�S�l���]w��k��Vl�*�P�vec�\�>�zg�y�t�jJ���[�m�V:�mM(�<����T�������
�y����W��,x��W���3��`[h��i�������g���I(�t�K����)�9t;��_j��d[�mA}v9��y�2_xF���N�*�{>e~�~�.��L�
��^�zmP�%�Xi��
��c1�e����_|!a��s!g��+��7�pn��V����r�_~�f�%F�����`�����������#��k���n�y��'���h7_r`#6�^�>rzl�I��Jc��T�k��}��w���x��
d����>xL�8v�����5�R�H�/��=�/��:���J����72�G��!Up���������?f���())���?.b��@�����W?C)z������-����Q^}�U�p�	vm��,�It�kK�,!K�\��
.�E��y�����s��%3�
�&��+��������zX�*�8\���\xBQao����o.
�v��&M�kj`<c�,c\d�v��DO)z�$�am�}&��2e�]��$<\��g�f���.��i����=��E(��3�1�@���X��(��/��O���}�nP�|e�������<W�p��!�(��
�����i|V���9���:l����O��8Qp������xQ�������g_~�e��4��r�$�����B�fy��$L��"���E������sQ�����b��O���������}��a���^CZ��{�z�m�J���\TpI>j.)�����KDla�0������q����m`�zzRl(���|0uA�����[H���H�Y���Dh��w\}��Q�e���S�T��Sg�8�	�%e�0yN*��X��"P��2����0��u|�l/���[v�uW�v�mu�/�y�/F6�+M�-�K/�$��>(����z��v�N�*����T�Y��/���0[��!�bEIE�NY����[_�alr2=���(����v�x�������))�(���H9�Na���ghXln(
�����J@����>ZZ6�;��O?�A�c�=o���]k���o����1�����k7�d�pI�sf��Q������@  �R�������=P���Y�A�9�0���'!��l�r����e����D��Lw����~6�z����9�����l�v����Q�
�y�<��#����q�9������k��
�pa���_]B���Y
-;�����+**�k7����N�8�u�A��d��|�I��"JZ�������tHO���}�x}����=����Y�Z�-�{�wJ�N_��+��������"�x�i�����um�������{Z��S�~�k��������,�^����k�m&�h�?R6�
���<y���ksA��=��~�i�z�����{=���8~��'���
�������.�L�?f��i�����Q��NgR�U�Vm��!�*�������������4\��
.�D����V����1�KL��������
.�D���i����~X���T��
\"a�0�-�u�����%Z��dy�C�B�1��!���*+�8�m�k�sy�)����/�m2�_�M��,��|�:�����B���y�#���R�9?��t����Ge ��yn��Kv��!!��fH������,���,��k���o����fy��T�t���r���Mc{|���<y�]Q���+)P�I0V����,�h�

*��d\��,a�n��V�Fi*�8\��
.[���*������Bd�N����*�����M�mx\��
.���z��{������1h� ��i����e�]&���{�=~��v��C�W��L/��<����H7�x���`�~�q��<s��3"�RhOj��k���vL�N���e����Z]�x��tb/yz����X��
O~0��,��g�1�S����<���
�7k��S^h!n����I~v���������e~��p���.�vZ2����w�!��O�/��
|�����D�t���BW�u]����D����{�M��\�.�(�
.�Eg����a��2���y�yj�1�\H��L����j �{�E"q�v�P�o�a�Upq&*�$?N\��N8��{���P���?�^�u%;g.z����M��0i>#��g�v.��SLS�EEE�������0����5�����i�q<��|�5z�x�������>q�N8��N|P�k8���(�M�8�k����["���uI���4���r�������_PWVV�5�A�^��{��G�ba����+�E+&��66���(��(��)����S���_g#7���%F���&=�*��:_�RlYS�9�t�`�EQes��+����^�?��O�GI�x�*��0[#|E��;L���������Y���&�)�W=��$q\��:'���X����W�\c2H�-�
�H�����#�<��c��z�:�/'7)%�P�2d�$��a�)��[PP ��kT�LQEQ��c��b�������~��sKDh��R^E,�����;���7���"�T��
�$�riE������>8q_���(����7W\q��3G$�-�^���a�bv(kJ~�^&�����8���8��Kp�AI��b��(��
|6n��g<��\D�qdgxDd)��`�!����#p�q=�e�EQ���\����(�Ph��0�3�8n��1N��(��(���i�8��_q�C�K��j�%S>q�����b$1a~n�^0_8�n�^��1�[�<��2��jkoYQe��iQ��N��%��u�ds��aRW��{��{���}�Y�x������;������������~���������%EQ%9;���27�0���<���"�]s���Cq�}��@�@+��������W��R\\,=�8�\�R�"�;P�����5EQEIf~�s
N�m".z�O�YZ!B���BY��P��:���F��#�����P�����g��3�o�'.��;�Z��1�(����n�|_�oA��%��l�3�#oC����u�7���;����5?���z�-�vab���>&L����n/�(��(�����8��I���?��0(B�����M��l{<y�`tk��5��$')%���-����T����(��$/�_��n��K��������NCie|M��n�����4���+���$l�u�����e��~�������L���'c��I��e](���%!�4��RQEQ�&�������*����JCIEy�^�^������g����(��!�EQEQR�/'���7��UOM����e Qh1��������p�����j
EQEQEQ���n}q:��q<~�U���n�3��4��GO|p�H�>�����(��%��/�����]�I��m�������.�(��(��|2n9��g���4�����m����F.;��}hg��_'�L��(J�A��"W]u�L_{����_8p9�LVEQ��<��\��X|1q%2}n��\�DQ����:���w�Q����VE�:$��RQQ!%��3�1a�c����d�)EQE������w�O���(����B=���]�4\wrOZ�������(Jj�F�Lt+,,��.�e��$�WEQ����a���W�Y�����pkJC8dT[������C���VE��$��r�G�; �6m�H]b�1����eEQEQ���h���=��F�E"�����0��X��������p���MQg��E��<��Sk�(����(��(
�������?����H~��t7"�8�1�O�<�+�9�'<��PEI���(��(�f���b�����w���B�%
��H��q������;a���EQEQEQ�)������OMA(bu����XUB���������^Q%�P�EQEQ���������������$��xE�a�g{�����������RQEQEQ����)w�����$�h?i���/]3/_7�ZgZ+(��$!))�|���4h�$�lh�������(��(M�������O ;�#BK4�d��
�x��Ax����e`{
EQ�m�c�9��5��n�=,[��^CQEQ�
��"���)8���X��
-�}�����{�����[bp�<{
EQ��%��-�r���O��~h�(��(��4Dc1<��\?g4^�r�-�n��q�VF��e��d��y8v���dEQ�](�����(**�k���� ��(��A4�=���������u�w*Q��u��]��;c�!��5EQ���k�?��#R^x��X�z�$���b"���'�n
�h��x�}EQ�)T�x���0��1x��E���H�p$�����������n�a���EQ�M�N�*enn���g�p��h�Zs[)��(�����{�{|��24����u�����r��0���q�~���EQR��\h����Oh��}�Q4o�\��u���v�e��@c���O��x<)�:��
_�]:�����r��aR�(��(�HIE��:{^�#������
�$@�N�x�����5C�}�|{-EQ�m�g�yFJz����W�i�5��PEQ'���K���~������4�D�������V���q���`�����(�EJ	.t������F��W^y_|1���K�~��x�����e���|>|������[��g����n�/��"���(��J�.	���g`��������"�Q�Z�o��������i\eEQ��dddH��K)EQe[d��q���p�g!����L����ill��n�[N�c��(_Q%uI)���"�����������|������.�C=�}�����p�u�I��7�x�^�~�?�x)?���x�����q��7���^��+��RJEQEIf�����/L��W����V +�����*��^����C��K�O�{-EQ�6G}��������(��-��_%8��I����(*#/�#�[���[�,��������e����(Jj�r9\�yR^^.�'M�m��&�< e��o�]���>[��"�|��8���Z�SN9�}�.��"�FQEQ���K+p��Sp����/�,���F E ����������[�l{-EQ�.��^�z��C;FQEQ�,�����3����U"?���4�)��u���/\3���k(��8��\�er�QGI���]��x�q
$��{�������V��4i��o_g�/Keee�T��;�w�qR��bo�E�������:���(��$3����&��['����"���\�������LW����������(Jc�={6n������	'��[o�>���l�#����eEQ%Y]��/N�q�L��s�Ehq��PZA����O��7o�����PEq)%��B!�1����L��7�=��x�\~��k
W_}������Y#e����\�V�ZI�r�J)�"�l���8��c���x�N�$i~BxQEQ�da��R\��8��_���"�����P�"���Qm��1�S�]+��(��8���^�t�I�����K~��.��o6��K*��(J�@�������7���W"+������(\i��Gl�O�����^CQ����
�x5�W��N���?c���2����bx���QYY)��"��c����T��+��M�Ba��u9��#��{�a����Sz�]������t<��s2d��s9���d���
����������{�5���8c��������Zb��e��Jq|�L�6
}������kS��f������J���Dda��*��g�j�3����m�^4e��k���5�k'�p�Bs����`�l}x_�z�����9��o?_w�y��]$u�8[��~�	?���~�]S?�����H��D���[�m�}���k��|�r����g��v��xo�;w������h��]��G/|��|��(���:�2�����wm�������������3���+�]�Y$�C�������T��'����$�����+�[��n�����G��5�����\.��<���2�
M�u�
�|z�l*\�Q�����g�K2��NI]�L�3����.��"H�4R������u��N����x��RL_T��%bK�BK0f��=�g������2U������m^�|�SDs��z�:W^��/��~\������S+���emTpq.*�8\���
.�]��>��U�!dgx��%�I���F��EGuC�\}���P������|��Rl �&T�������>�H������a��x��SO��/�h��@C�E�X�j�]��$6zTpql���+++K���X�{�_|�� ����,��3n�7����?�a��|�4x�@��"A����vj���f�y��p���{��X���4z�:�D~&N?��?����S*����Q������\Tpq.+���c5}g���z��#q�+v����]�h����
.�E������o�-	���������l�_s�5v������X�{���e�=���%4^������^����W]u�][������05��3��b�EC�9>o�O�.����d���E���s1wi�-�y���PEU�~7N��#N���x�(5hH1g�!���
.��|��W�����
AYRR�^xa��$?*�8\��
.�eC�)sKp�k�1sQ92�A�yv�*3vE�.�"���k/�lmTpq.*�$)-��k���J&��o.B����k�.��D�e
&�1~�x�_Gq�}�]������m(�~cIx����>v���f����� �������Tpq|&�Y�������%�f�*<��\,^Y%���DZ��AD��3��S���4�O�;|9�pL�u�PRvVa��K�.v��l8Qpa�,v�jK�.u��\��
.�E��X�eya��4g"����E���2���3p�	=������J����sQ�%��-������n�%//O��0
s��;�w�}x�����1�#y���6s������>����#FH��>�kjH�)�w�}�TEQ����q+p��?��g�bMI9����PR����
�?�N����-��(Mm���r�����'���~�p������@�UEQ��
C����t~�x�9����`X�����8�=�>�s��-��(�H9�4e����$b����/�'O�������y���3�<#�d�9��jo�Dr�A�I��Kn+���*$C�m*���L�������3I&�w�,�3�CQY��4c�����Gv�����V��z�8�pI~����N`�_~��z���[�������������z�8�pq.���\�py�����wK�JKC��%��*�Q����C�����-�����sQ��#e=\(l�7x��&[�_|!%OZ�/	��^+���V�������	�����{����V�Z�6�N�p�b��(��l�>�y9o.��x>&L/������Y�=.���:A�=Z�w��<,�Zn9��yhg[EQ��/������23fn��6��QE�y���u��"�0OK�����1V�p�������*�(��4@J	.��������
��i`�/��{���q�W�������C����pr�]wIO�u��+��k������K/�$��-�(��(J]���R�x�����x��Ex��8��?��%?b��*{)��/b������q�B;u����s������v�A;���PEQ�4�N��2�W�n��&��QLQ��0KN����{{����o��t�$�_Q��^���]q�����(��>R��BCi����vM�q�q���{��e�]V��q��W��}"�Am�5k&=���SN9��UEQ��9���q��3�,�+1��2<�6CA�y�����������"�<��\��.Zb��x�0t�����g���}�����(��l-�<�L)�;�<)��?p�x�3�=�EQ��q��Dp�����''K�-� ��RXF�������.���<WEi)'�07
s�0?�/� !�EQ�	|9a%&L/F�\��JN�#��]hU��W�V�o-�h\�������J���[y�EQ����O>�7�|#��c��}���7�|SB#3� ��1���(��L���iwM�����*&����e[���+w��A���BQeCH)��
��]�JR r�gH�+���k`�,EQEI��|�/dg��V�����.�*#h�"��k ��uv�a+EQ�
&�?��������>�;v\�fYwX�l��������0��(@8�(��8����|j
�f�-�D�c�����^7��������=�.w��(��I)��.�LNO�����@h�(��(J*0oy%V���v�/�$`N0�S~���x��a���=GQEI6�i��EUUU�vK��l��)}����-��4Ea%u	�b��3���?a��B�����NCU(��A.<�>�o��a�EQ6��K��|�ry������~k��(��(�MiE���F���Lw��FEQ������������[j\�U+g���s���P���C�D;��etH���+[���ZF���q����{��y�����O�%t���B0l�Mf�}������}:������7���e]�����i�<����>�i����mjX�z5Z���������3EI>f������c�}��k'$n6{&z:*����+V�_�~v���� f���^�zI���a��"<��L�W&I���X2w����b�(M��)S��M�h���Q����(����]�$�aG+����c������q��wJ�.R���l ��7�g���v��a�����|:��g�ah����C�Y0�:�oBV�����������*��Oe�������@ 3�D;�J�����]s/J��G������Y
�<�e[���x�\����Z"b���O��.���\�����ZwG�B!��z��1�����9�Cg���sQ���l���lM�����[�%����4Jl!U�(����#
�k��@g��K��d�����oa��c��
��{W_}uRx�4����w��}�{����g@���7m�v���Ky�c����c��/����o���n��>56�s�-���2�.0jq(C(������#:�k[/*����`������k�A�Z]�x1222������P<�m����N�v��x>3b2����5�\���������={�K0����3Q������L6Dp	Eb��*���R�6��4��%y[��4F������3����(�w��h�m�*M�
.�D��������s1j�(i���K�����i*��a��B��g����V{��a�(N���s������w��jl�G���i����m��p$.�v����tC�.�����r����.��V��i�N�G)m�YPp���w����v��5�-��\����{�Zla�
��@���,=�EQek���r���Lz�8\��4c�#'��L�[���TDP���YvF�?�*#u�,���X]�I�tP�EQ%������������8���Cuo���������d�MS6<�zl�I����M^�-����s����W 7���q����E:��@<w��k�-D����c�L��MnRNpy���|���p�B����2��7�`��e������w�}e\QEQ�4�Ua���R���_p�>�y����g{����
�����������G�������v��;�DQY����C��r�|E �kN�����f��(���L�8QJzt�
���+/�-��c�=V�]z�����(��4�EA���4csL�sJ������&�2��zF�v�0�������(�������x��A��N;M��y�q)�v��'�|o��6����SEQ���c��1h�o������k����fa��*�f���R�BJ����������#q�?�cD���\�<�/�x��=;f�_��c�x���8~�����(J*��H��SOII,��������x�
�����L(qPEQ���@��>G�8���
��n	M\^��]�:�>�gG8RCI)��liRNp�Q�#F:u�$e�����{��?�����(��TPh),������&��������������=������Vx���x��a������>F�k�[N��g����t0.<�:����*��(�BB�`��������pIp��GKBy�K:� �������(��,<��||��xg�RY2�n��B���������'�k��TEQ�<)%������B��]c����|�r{�&�TEQ�����V���&��W���KQTDN�G�@(*�,]�e���{��w����F��9����(�������*)	����5k�$}������\����o��g�}��REQ�6���\9�~4iii���%a�7��x�\pdW����EQE�Z�\�#F`����8��;��Y�f�S5l����~*9b~��G�f��6�EQ��e��J���l��O�����2�^��<.D�qY��4�k;|r�(�p�q�w��_QE��8���|��g�$���w���0��F�R*��(
��jq�8���Y�����v�=���m#q��}��U����(���H9�������C�x��d��C�����B$�n��&�����rC�<y2


����SN�.��"�-��7{�
���L�q����5��(J���OK%�=R�7f)B��;�Y�6lP�<<�������p�q=����^[QE�V���#Z�h����O�z�o���2������~�!^z�%�����RQE���sN)�q�D\��Wh{�wn��W��G.���4���(J2��.'�x"*++q�m�I����/	+����J��1c�H�c�=&���������X���=z��&;��������uk)MQEIf,(���O�����������U���%)e8���0��.6<_�3�]4;���3�(��(��/�����?��)S$�=��#�����;L�_z��b�(��(�.�����Y���%�������ad�=x��A���C����*VEIFRNp!���
�.]*=�,^�X<[\.2220{�ldff�s
�-���C�J���?��M7�$ec9��s$^svv�$�WEQ��p$��}�^9��5	�[-�(�3�`T���
����3���a����8rD:���g��(��.�haG�^x�����+��-�������t�D���WEQ�=����g���k��y������I�#���Sz��{v�����k(��(�HJ
.�m��8����)�e��������~����=g�x��G��Mm�'��z��R6�y��I�f�5�~�YQ%y�uf1.z�O�<�{<��\T������u!�auIH�"_yBO���������9!�\e0f����%EQE���N;
���xB�x���w��~�a�VQE���������1���9h�T�(.���v��Gw�a;���VEQ���\�
z��'�$D����kW���[�+--�kEQ�d��"�����������7�>�9^�f�������"�x+||�H��H�[;{m���,��(��(��l�~����w�,En��>���aUI���������^ZQEI�Zp������K<x��>}����(��N�:I�.F-Z$eC0�?�>}��!�(��$���
��5	�^�^�j��D����X=������*��������Znx�JEQe��!�X�����C�(,,���(��8�/'���������#^�Y�a��F�i��O�&����(J��������?���a���4i������X������E)�'�!^{�5I���w��5Mn*�"q~��<SR=�[����p��fa����UOM��e�h��C�1nx����q�q��]�������`�a����G�Yg���y����'�)����1y��:m��
K�,�D���(�3�ef��n<�yf��iah���zu���w������t��^CQEI5��u�^�^�Zr����#G���-
s�,[��Z����	(��[;�KC�X�m���$���.\p�x�	I���}{�vm***�����3��f�������1�|2�p��UR��D"(..F�f��E�zG9[�x_����(�ip�}����cl��@c�T���Va��Rdgz��u��D�!���e��t������{�V��KCe0*=��G�k���n�[�Y���/�C���������x\���3//���b����|I��J����ze���?�~��'����;��u�]'�[��e���������F��*|������	g�|�r��������Fyy9�������kg��d��
����:��^c��{$��@]�f��{bp�<{�-�B��MC������c�*Na����������,�^����G�W��5j$��-���+��uk���b�� F�����cG�vmz���Y�f������\�O&�,++�����b��B���b�9�3�m`o����e�'zl7/��nc�e������0�/~����vC��%F
����11nZ�y���L�> m�=�
�d^l��^��D�Yg��W�Da'!�K�D�.J����\Tpq.*�l<+������������������`�s������{���mk����Q������|��������~�
��
��Gm����g�d~�����RO���3��f����c�cs�	�}����Q��H0$�6f��$�k�_�~v��9)�������������~���d$G Ev���7��{�������M������o�����$/S�LOYz*��g���%9q���������_��ZGq��h��Q������\Tp�p*<�����2��.I���_Z����:�������%Q������\TpI>j.)��dx.��O8��{��8��cDy����%6�����]��-�%X��B|>���~<��x�������?2��������o�-��R��&�RqzL������E����/�O�7��������<��Q1j�����'�������������8����RR�n�_�'��x������������E]$�C��BQ%5�D0wY�.�@EU�QFb���I�b��~�'?/G^�G�,f���};��{G%���(��4
I/�\u�Ux���d�S�N�i�����\z��x��7d|sq��'K����K� �����/��.(�\v�e��.�������OG�V����9�����:�^CQE������o�����=�'~��Ynd1�1rJ��^��]�������k������������(J��j�*�5
�`YYY�y����K�>f����(�����u�����+�������c�+���'&�������%����x����0vG��M��(��5���<�q�(��8��\��B�{�=,X�?���$�gO1BAcsr�YgI��/�w�u�������q�g���/�\J���|0��sO���t������(
�O�-�Y���#����wKP�!?��;
UQ�"1�����.�������!I(EQeKq�WH����K�~�A�.2�(�^8��g��2��(Jr3az!�r�x�{=.�����3��������������&���fK�/�1�]Rf���6��7��u'�B��WEQ�OR.�KJ�����>��HII�]�y�Dw�k��Vb��q��~����jC��O>��}����oX������(��IyU�����2{q9"�Y������f��k������*�c��t�a�~iE�>�yPg�{������y`s��I��(��([�"��*yyyx���d��iEQ���}f�~f��^�J������LS�y/~��K������������^;����[��k)��(�I-�0q8a��`�|��m���=z4�.]�?���f����m�����p����h����M.6u��j�KQe[������k���;'I��:]�3>��^����0^�j��uN�{>��B���,/��Pd����fx���"��}p���;���(��l)�����b���R&�EQ%y�����e������m/,���h�2O^:��(��W�l{)EQe["��=&�U��B�mn��m��6g�O�>����$�o��}��_�~���(���Z��i����%V��EA�y��c��?�q�����������7��#����5A�dz���E%7K�V�������q�9��}�|YWQEQ�z�s�z����_EQ�����V!������r��}��
C1�w�]�(��l�$���>#�J���EQ���=���������V�l��K\��]�����g���Z(���%<�!���������������|EQEI%�~QEI~(���V��P��	���l��-�)EQe[&�EQ%�a"�O����+��\b}f�e�Q�n*�Qq���)w�_?|q��������^��EQEQE�|,[�w��������7O�D�?6��^{������H��REQ�eRBp�/r�>33SJEQ%����UH�����^,Z#��}:��v��,{m�[�(��(���U�s���^�y<���(������o�W>5\9��2W?3��+���e����Pb�bsI�s_QEQR�
���c���������^{�5���/2���_������?�������e��(��uX��w�����(����7
���s��EQ%�aJ�+���^xAl�h�"����k������"�2��(��26�/3���G�q�=�b���iwM������S����������H4.9(KC�
������6f�F���EQ�m�����f���h��������9r��oi>��sp���������M�������1�|���>v��hd��5}��������+V�_�~v�3�����U��*q����6g�g���I
��x�������~�]�8�)S��M�6h�B�q;����.]R�^�-A�"�	B{`������q��w������:L�-���gY�l��{6�����<&M�$��w�}�Y���������kp��w�Sks�Am��l�~�-��m+���,�/_���R�����Q�Byy9�����������,�.��9��0�gcEQ�����������,W��*qBQl�6���bx���[�V��8��qXSBF=���^v���3��I-b���7���s��J�@;577W����`{��n�=���a�*[�'�x\p��'���7{��
��C���1b�{K��(JS��5��g��+���.y�O|1a�6��n�{1&���5��QEQ��d�������Q�7�o���p�
1o�<1�)�\y��8��3��o�K
8�a��1R:T�b�����V��(���ex��%�������~�A���k������@Qy�~���4%��*��,����e:���=�`~{nO|x�H�vf_2���-�!����P\F4A����T�������
�eEQ�$����0���L����8�������b�J|��J���q��u�;>%c��4��m%�o��/���y}��.vM��.�E=\��z�$?����5�����>g��:H�o���v����+���{�k
6l��5M�z�8�pq.���B;d��"L�^�?�����v�{����{%3�H$fy����sc����7�{�O�
����G���wK���
�����#wm���*���z�8�pq.���|�����(���V����e8�����Y����~�Gc�#n��/��T�7�������.�c�����<c��.	!V��/�[��
�\;��QEQ��PX��P�K�-��6�v�x�p~CTTTHH�#�<��QE���UF��/+q�+3p��?c�����/b��
�e{���[���{qz�P��s���������0�����K���:o��B���.����0���d���]R^lQEQ�\EQ�:�<��9�^�3v�����LL�_&.�����G{������U&�=�+��c$�{xc����ny8i��8����]�)�����"A ���@����'+��(��X~��)�>�l)ks�i�I9v�X)�c���R���^R*��l
�-���-����=/�;_0�>7
��[�RcK�����0aq���*�r3��h�"���]0?=�+��'�zF_�c�,P/tEQe�����(�"TTE�k�9XF��=N�k��~)�L}�<��=��_��@v����g?����x��!8e���������
��8L��>�B
�����%F?�3����EQE���k)�
��xI*�A�����Khi���0p�@��QE����}��"<��\�}�o���8��	x���0nC��e�9�V	G�(7vIe0
:��������_�=&<�;^�a(.>�;F�o�t_�	�EQeK����(�6��ex��98���������0�O12���Pa~��h\D�$�[d���:����������������������
��������������G��(��l,��+u��fl~RXX(e}����R�w�}8�����k����N��y`��/�E��i('��(*���+p��3q��0��oq�����a��rdgz�,���t�s!��<[$;������
���7
�w����5'��	�����`��(���h��F��;M��\�!i~EU?L^�o'���Y�(4�O��-����&������iN�dy�}�<�D��r�!���&�w.�4��h���G��o���?�������v����V�B�V�$��3�<c����.�HD
,��%�����.��Y�fX�f�]�i0i>?�m�� ��>L����eee��W��s���q���>}���v�u��S#��d�``��J��^����1mA����J���@��a�s���em�/
m
��'C{1�}�������oK�����-�b������$W�k�9����������c�x���ay��kWdffn�D���L���*��oJw�q�.]�A����/_��h4j�{�=y�Q9����!��P������\�����_�����+1~Z��/�z��e��c�+l��,�~7�6O�n�[`��-��K�,���
.�Eg��K�����A[�a��\(�0,�V�}�Y�v���B�����={�������������/m��K �Y�%�o����LP�u����0�2�0gyS1gYV�QV���Y<n�Y��f�s�z����L_:���'?z���c/����y3�p���$�v��[^�|�����d��c�6���,�������s��r���k	���
.������?��O�~`�0B#��k�����6m '|AA����B���{����2�����LTpq.[Jp)�c��"|��j�1���,c�C-/��1b�f�E�}:�b�!-1�w>Z��h�!���\Tpq&*�$?*�lg�q^x���3Gz9�����Q�������m�n�]v|�A����8��#�������C����B_9��a�:�2������/���~X�j�n��UQ�.���%�0��W��tM�(�5���e�qq]�{%��]�H9��c@��[�������.��e��B[Eizx|y�.^�X�}14����t:�F��MOO�������]v�������N�v��������\���XD���l����I�:�[�����7�Q5Rlh~��G8�������4�����p�Bt��Q�7\��
.��������[V��~[���b��rB1����o�&c����X�b�b�e`s�6��bq��E�(Tpq.*�8\�\6���^<X�k�=��k-}�Q��_����~�8���;�}��x�t��������O�/l�R$�T�=�B��g�v.m��q�(��Lf�_�������cwT`��*����V-����
��>�,�����1�����{�l47uJ���4u�Ti�=��,h���tC#�(�^�l�QTS�>���~����+�d����
>��=�������4�b����O>��{��WJEQ����<��[��3
G�0��<��-�����Yq���(�� ���c��pj/�y�p�~�0�DW����b��(���0	�y�����!�%
����cuA��=
7�K�mI���q�c��0���i�x��98��_q�5cq��S���*L0���A�f�������#1�{�2EA��z��������;����W� v�A���|$�Y�v��[g��nr���:����R:T��j����~h��
��#F��wI�o�����l�M���p��������O�����%/�#�+|��UE���E:���=��b0�3��C�!���u�za(�6O,�����N����/_m�P%�`B���������Kp��wK��#�<���X�d	~���F ��r�i����G}��%8����dX1EQ�G4o�O�-�M/L���L����������]���*��%��~o��	c��������2��h�����wn.���?���^�r$?��(��lk�������+u�����`�����c��qb���7�|#��H6�(��)�����kp��3p���p�����c��
��4���T6FOqyXR���kO�)���7���v��������Q�"6���ug���?1��G0������C|�r{	EQR
vc�z�3t1C$3�$;|�����Rb�S��g��k B
�HS������%�;;�u�Q���[�%EIe*L�_���\��G^?��27{��I���0 ^���������(�+"�s�W�l��O'<}��x��x������^�wXk�m�!���(���D���T��R�e��IY_��D�1�a��9�)/��)EQ��^,��j���7	v�����/+EP��� +����Wn�%�
k�,����^�=>�sG<z� �K;S�^,�����s�b��O��n���������q�/�7=/���(�uG�x����(����|���D,a/�oa���
�����`������;_{�5�r�)��C=c����`�(J���8���������;'��&�������sJPZ/y&�gG.�_a�/����c�\z\�pE_�uR��|{�w�v��%/����(����0&}�>|�-��{���6�|�J�-�P6'�4�+W���a��������������z
�/F������A�e��5��SO�k7
������{�5�`������!����$kJC�>��M�2��~����I]b��e���&i~ �o����o�1qFV��$�}��
�1z�^�	��f��~N���J����@�l�\��0���i��q���c�*N�I�����z�@|�k�������xc!���XL3������U�+m
Z���\Na���~�s��v��lh�|g���m��E�>}��),_�\��kD��;oM�W���1y^��.���'�����/�-��J$��s�{�,�������a��y(�����e�������c�\�o�I���I[������Vq
L����+�]�Y$�C0�^FF�][7+������CY�/������y�"A��w�w��J���*���q����>����_z��
\b�w��k�����t(���?_z��BS�C4���� 9���2��)�W���hB��������w��P_4d�����=��i�F�`50y~f0e��s�]fn�z�E"q�������y����{��_�t�f������>]����Y�^Q�E>[y���������.C�{���N��$��k�;I4���5S1�{k�
>i��us����=n=���������/�m��Y���f�����|i��Ks�����^��d���GB���WH>�[)��\��
.�E�-m������bL�Q�9K*P^�$�W�f1�=6����N]��������S6v�����i�!�����s���8>?�����Q���4Vp	-������l�	��	���� F���G�=��xTp�6�x�=����R��>�|��x�$�7���{�{����^mn.���A�Tpq|q�-;��M>��
�"8���(�2|�#=����w��P�ix������bMIX�W~�Y�?��QZ!�Km/
,o�������9���VynY����\V_"%4R��K�Yg�k��o6^�~r��u��6�!��<R'��V":�s|�*.��g��	���}�����F����7qo��L���L��k��7�Lg"��Y�z:�\�������L����=�y�4e����7<�fsqdggKY'���s��8������n(����P1:������r��7@a*	���Z�����*�8\��
.M��� ��/���E�8����v���<z�i�H;�<�����`
,f�N�3��K.������fz��*�8\��
.���������1�|F�u�B�%�����V�ZeCQ��h�����v]����-�1����/9�	���1B�7'R���B!��1C�I�m���O�-�|*�^��=�2���["�c��"1v(�X=���h��*��+;h�=wh�Q��[()A" ����3H��b�
�h��5_=�U�1�U���$�����x���vs�Bz����0
��� <�<��Y?����������f=�sH�����g#��m���7�if�9f:KJK�L3?QR�1�,�y��.^"/�tj���x�T|_�+Z
����G�>��B$�������[���#�W6*�8\��
.���+*��_��0�g��**6s�$������;oq>�����+�{`x���)���\Tpq6*�8��3f�w��k�w�rD��FBX6�/|;y>|^�</"��w�,���H�F�PTpi���?o��F�F_�2��������-�4��Kx����>v���f���� ��)�Jp�m��2����XA���n�$2��G�"���#{n�}��D���HP��`0(
�^�z�'��(�0�(C�nU�����@��V/1%�f������U�TyrP��5C>������_����x��(��vv5]b.�������_L�w�)�����h�<w4d�4Ax"�4�^3�f]�pd>��%��������c)��7�����D��mLI'�t��3M�&Q�z\.���F���N]����~{~�;
�1���!��K~�S���-A�1wZ3���
.�F��������L�Y$��&�-�����������^��;m	��scP�\�U�a��1��y�ofTpq.*�8\��P��
��Uf"��:�����!l��h��`%��%�K+\Ua�f�0��(B��5���D�<���S�<$�l���������h;yy8��C�eCQ��~��G���.8��`|��][#�p8��c���I��e�������3Q�%�y�����������cCC)�J����{f��=�a�����Tpq.[Lp���z��#F�SRL)+)��D�'U�Tx�Q����B�%t��}��>L�����-%�xv3�nt�O(N@�;��������|���XA�����u��&n[�!GDK�q�5�8�%�S�	�|
;>3xc!�E�D�I���b
E�lx2�1+�������%�7}|:��=O�������P������\Tpi<�&�(����0y�<���Q���-�B{�y�a
,Q4��K���}�1�O3th���Tpq.*�8\6�f�U�#TUi�#�"����eL����C8F����2�Q�e���5���D�}�x~���zV�g��p`i?&	�k]���_/Qs/�I���#�2#���1�����2i�$1�>�l<���J�
G��$�������x�������f��l����|1����<��]�����LTpIx�N_P�Y���rq�-���<�~Y74�2�n�K[�;�
:��p�Rt��/��
.�E�l5\9fl��Pm6�%P��k	*����x�J�Qj��e��������P��Lt��8�T�mQbJ"�I��F3�]u
>�O^�����m������7��qH�0��oh<1��������""�X%��MP{���IC��b��H%�3�!��T/Yr$�V��#�@z����S�
.�F���K�,]S����1nZ!&�,���`����a�o]��`,��(�w���>�����Gf#;~mNTpq.*�8�mJp1��h��J�9�h�P����%���<���lP��(�f�Bf�H�^#V�����4�5�g�nI���Z��1��e�Z�<K��-�p������ k/Sm��_�;S�6���t}d)3-d��>>���>a]"f?������f�(�
.���'��UW]%
B!��[n��\�2�%�|8u��E��4����1~�x{j�Q������5�c��
�ZX����0{Q9f���<��%����o����T"8v������ez����]�~�����Tp���������`��r�<��//^���=��f/��Pp�q���z<�*V��yS���h%�JKP.C)��JQE�sT�SLi����{��_��6
������?BB4I	�#����UVV��I�>Q�����.�5
�.�����:�^����a�����������������������L����e���+qAe0�#��	�:Z�d�MGg���sQ���)sK1~z!&�(�������bW�(��!����s��1�vs�r�g������������y��[\��
.�&)�H;�T!�^;�s�4��x"in���(5C�#��%��
A������ ��<��0����_H���S��������?k�c�9��{[��z���,bJ�K�`�V����������<F�q�g�i'{��N�9�����_v><�����4+e,`����J���l$~�@(���t@��{�������CP�������PH�������kIf/)�����*�x�`a�Z�wI@�e5�$S|YkJC�������~�{��*�8
\�.��~���2�����g'�`(���1p�1pgl�9c�T�m�y��+�"�f1���PVn��rT����L3
=Sf�x2�p l��@q��?,�����v��M�����$,�<�p�Q8PX�4���{|��������w�^��������*2�o���mI��=!�p����xB�I�'�K�'N�%H%H��������nwu���>;��C��U+�F�Tx\Upq.*�8�mUp�F�{�!�~�]�iJ%���1�D�m�/��{�%�39��3_������B�o
*�8\��_s�"//-[lxH�x���+��d��J�Q���H�<(Pa���2YN�K��,�wF�,��.��J#����o��������DYk5�����$��R���R������N���_�7�'��e	�G�{����g���
_V>�>/$WeZ�,x�3�m�=
)^���,c������g���3��K�>��~��~�m{7�a4C�7�C�?��T6
\�
.�D��A�W`��r�XX���*0wi%��	��c	*��B��T��a�����M�<��>����S��~	�*�������=n|~�(�F������l�"�����g�i=��P8����}�	V�Z�M��U�\��5+P^0�w�)+PV���*]��3%����>{���B~��o�s��0_��U�&��`���D]�������R��.==]��,Y��G�Q"�B��[�����#F����wB�����h�"�c[�v����}1�\�J������+����C�9^��Ag���s�V�%��0��+f�sn)����\����1��?hSP`!�o]���`C{�cP�<4����dG���K���*/���!LO�p�h�V�����?;OBdI{��L���(�0Y;��Q����p[Q���"��; �mb��%J�Y����lkk��O�u�Vs�����j|��K��'���3��I�����������b��1fR�F��x���=��g���
of�)3-a��y����Y��������wz&���v���v{��!�����Cq�#����Wf���Fo!sB���A~���5��A����3Q�������e��I��E��g�,��G���h�*.y0�6�
�z�V�v��O#)7�������c6zu�A�6��j�^�����~E�<��.[�5����E�I���3iJ�%��l�2�2�����F�� ��^}	e1���Km�����1�sr��QZFE0�����(-FU(��'!o�(�b�����F6TL�=���B!�
��5���q��a���bt���j�
���|5)���Xu�����;?������~�4t�����k���
.�F�����U�A���";c�m)�K&Kx�b��W1��/EQY�>�v[��Cx�It�
�c2��E����+���H}*���sQ�escn"$��Q�����=A�<#F��Q3�6��a�(���V!���6�!=��k�#�la���m�.����ID	V������.�?R����f{����lh1Y��G��g�����2�|
)��e�s�-���r����b���>�%vxE���������g	��F��n�("������m�7����S�
\����/��1��y^�k���h_����;��|kAe�Q��!���LTp�;���tu�2�
�Wb��*,^U%����b	+�����Woq	a�����8bH��7���F/
+����e:���^w}|4vzk*Q1��6��4�"7��+��{Y;��
.��)���X^T��;]���X%zt���:��./\6�� �U_��,7
p���V�b���;u�i@���k^�4b�i���DBz�+
~���#�%�����.7D����d0�MfV�1Ns�E
*	!ek�)J�0��'��5�����
��v�5kf�*�\��
.�ek
.�pu��S1uA��?�#L�>'������^j��V�1iV	&L/�����U&�U��hs�\`���C��vF����Lt������C����|T���l��K��`����L�qZ�V�z�0�H�
�`�\�a3X!��<#���F8P�pU%"����B���C�l<w!j���Y���Za�����m��,'��G��nY�=i���Y�SL������Cu�IS-��#�m(�RfpQ�q{�����i�%����-����K��Y>
#�yru�Td���=������������k�,����
.$�z	��V�|�r��[�e�y��s�ME����3����`+��_e�"��_Z�e�X�&��@�<�V����C�o��Ynd�=��.=;e�w�1���H7�7��]\�3-�x��P>7F�k�s����Z\�*�d9PY�67C;�$J����\fN�qL��7[g���y6���xx �[5��A�c�hD����\����L�<\e�2s��4�Hub�HL��P<a�0"
��1X���,f{��%��i�����F>���G6hl��j��xH��x�F��������o���������-[[!�23$�WFf������R4VpY�l�����ur�Y�[�os���4hv�a�W6��*�8\���\��`��d2���)�_��^��UQIB��%���Umh7,Y]U��~��r�[^)��"��v��l�$��p�9�^���aH����^��h]��B�&*�X���(.4cqd�7������%��x�|A���6�%
�/R�9E�(�<#�3�p�G����;$���i^�CD�2���`���Y.j>�������5����8���(�Q�������5mSk����[��+#��Z�j��a�,e:�)E��b�@6�G��
�r��,E
�
�$Ug8-��=F(�$�+��?+��ER�����f[���v�%���o�{f�*[��\��E����3����_K����R���]�L8�
�v�|����V��$�
C���YQ�+��a1�h�$\�%�Y�/p�k5�
E�c��N�3��c�1x�$X�f~cx%�h��.c_����ae���;�h%��G�o�������T��9��������c�l$�E�X�9�����k6�x��4s=Y�;h��7���bin[$)F���a��-Q�$�7d�����h��mE�'�[�
6��
~)�fH��5_
�V&���Y���C#�
�;����;��9�Z����	����gd!=+[�?�+_
2d��+��������j�*1j,��%/$v�u�����yQ������\������b�~��h���a]x/(��`�a�q��}��_%?��U*y��+��vx0	�C{�4hh�0�J����
��]�f	Y�*l��K����)��*B����7D���m����ImO��S��s�����|t�+����bu��@B��j���T8d���+�s��!l�Y|���	�0b%U��=a��I����k�\��f��ms>�Fl�A�r�������5�X���;v��*�_���aTJ�,k����0B������k�g��`g2�"�1������'I��VT	�E�cs���������������9�%�H�������������QY^;*�$*�8\���\��p>��t�<�)d�H��F'��	�u�F��(����4$�
�Vf�reQP����52��!���c�P5���������>�fy��Ezu�B�N��l��>�d$��mVp	������_B�P��A���&�iT�]hV8
;����17Y��!c�����1�7����EQ���,��!����K+�3��P�������'�$B�D�/I1�Hbc�D�`���e��mG�ad/��o�I^��'�}�[���H���'�F���F�����z�`��[F
�71R8�]��e�w����0��Fp�!�������u��)h���6��d[bC�eee())�����kZ��Upq.*�8��!��s�O�
�p���'�``�/
,b�X��,����)�^aG.&������t��e�e������	�y2�<K�q�������n#v@��C��L,,���&:]E����s��]{d�����x�T�!����k$
&�6�"q��g�\7bW�fk�����G��-���^H�����|~'��j�E����Y�k[Xu�����X�8����5xl��+���;�ao�}K���������aH-�a�#�r|�tx�3������7b	#�v�g���P���8��!�!��=[��m���Q���*�$*�8\���\�}v*>���;l�VoHv���^4�����2���f/���s����4$����+wxCj��EA�������$���i��GNV��$��w�
�mUp���3�f��~���!<��k6��e�8aH[d
9T����3\V�����y��f��s��$F-��������h���_ac��'�1J*�.4hh�p�4���* ��
	��qc����$��RL��"�Y�:�/
��`����i����G��V�=n����.�����if�Y=�h\X=���`�-cT��~s���3�
��6)�2a�e�d���N������H�}�qc��
�~�`8�Q}:���=������3��E�����lTpq.[Zp��j\��T	#�hW��	�+p)yl�0�b�������I�����2���s
���U��������������'3���D(�H����!���#�D��Fhg�s�g$\Io�r��-A�sz�3��Z������b�7h�n��K�� f��2�|���xE{�F�eS����W��_��R�����J���z�yL��o���`O�Ls%^���4���1hg0'��oQ<Fl{C��\M��40����16����������[�3v	�v��\(�q�G�Q�^�?`�@v�sWq*�$*�8\���\�N)����������e�[EYeW��'Z7K��9%����������4
����<+��BaE�9�eh�T+���x~�-��hY����J���(��J��d�����0+�c��b�m��F�wz��T
���X�;���[Dr�IC�>�*�p&?��@F�����E������s����@#BJgY3�0Z$���fO-�5���X����k6�#v)���_��`f���
��c�.q���g�E�%"�%���N����k���_�=�5_�%�7���?�,_�t�1b���������k�2��e�D��anc������q��f�������{�l��a�|��e��4_�]vo.n��F����7�XY9w>�n,���u�7�6�Vm�����N�*7\��
.��
.�F��9����(D��`'����9�xEq�UT�(7%��=O�&�����j�9#�6O�P�C{����m)<����
.�?|��-����7y{c�����"vP1��iY���AFJ�pZ�JK	[�<�&��
)�D�����F�6�e�X�(�3�u$���]�
�{��Z�Oz��*�J�VEm����5Z'5���6�L�3��+j��l�DI�B��m0����5�"�0���>�0Cr��e<~��5hgdK�_v�e��#�|���q�\�>��!����B���K����CP���l
����'`�1f
I�wi�Y��P��7��*�a]aE�f��
.���E�z����}���S`in�L8����<�~�U���d���$��Nc�a;�c��e��/�P���@$`�f�����H3u��Z���v�K��	���e�J�:b��l���1�W�4D8Nv
"8xN��i6�M;N���ih[��VIj���F��wR3�O��1Uj��-��:�b�Zw���������/@�$tS�'��<E��#��3�)�H��_�nc����I����W�+EI�q�w���9��N������'�����i!p���n����xTpq&*�$?*�8\����+P^V�����k��IiE�:������bM��s^U(*��UZ�G>��UJ�(�������g>{����g.�|w%��>�t�s�'{��%1����e�Q����(�'��(m��X��W�_�S$�%b�/V�(co�3��R1C��6L�N�f������!�\C��7���|�t4d������Qj#��WL�B���|��J�O��:��������������Z�HbS����>.��7��;^w,���G�El��v�$`��c��YX��_���K�'yF�����\��]��`��a�����\��
.��
.Ag���K�.��a
K�Xj���������K�4�bt'NC��J�s���p�����=h7�,7|la�=��2<h�<m���[�,��Dq%[<X��(y�����)��������6�x+M���~	T����D��5��\(�P1Kc0 ��h���+���GhLHYel��
X��=$� a!�wGM�&^)>�q
#��n�<?���0
��?W��Y{f���6kScp�64H���������u}���_w^]�ZX��e,���qY���8
�]r��/)��f0�X�1G]��64���U��9������HR����'I�c����2.�$k�_�P�d.�|�=�������#3^��u���4Kl��\��
.��
.�F���tuf/�DIU����k.��=���QV���K�lu9|Y-���(`��5��Q�!h.0%��|�k�UjB[�=n��+9n��Vn3$���le���EfZt���|��G�M�PJ��yi+L-m#aX���n;��|ge���:g�\�36�Y�H"��x�[�Cx,�))��
�k�1e"�R:r���N�.��i��e�I�a����!|)��':Ks��F������'��"��Q}��y��NJN�)��if��C��+$��U�n2a�s�������i���]Z����lE������
�����"��)~��{%��Y��3�������b���y-�Hh�4�}��`i��Sc����IaD���)I�
.���B\�\�
.�dc�+*e.�V�X������c�ea�L��� 9$��Q��7���Kz���vz����f���
b��t|�t;H��H�g�k��v-���-���H80������B7A,X�7_�"��j��.hD���8������\iy�/[��=��4�EB��K�A�����-��4;�G��@I��s�/�r`�-I`�������0�c
��� b5�e0M�'�U���q��6��2vI�_Zg�]�`=Zl3��$�[b<A�qR�����k����qoymX=��8������[i��Mn=H��e��1v�L�U�VH7�e�0'�1lh��������}GFJ�=��2b(�|���XcN��%��c>�g{���]�0�u(A�����4Cff�1.7�����3Q�%�����sQ�e�0<��ON�SJ�D��A�
� ?���N����Y�zS��(;j���V+�C�����L�luA�E�}a�D1���I!��L��b�y��D�G�o.S~/������Vi�g�-�v�����'!��4�K/&����9����={#��)���������%n�?��x{��o}��L<,6�V���C���B�%f�G��'b.���j�4���� �
?�/��"~�!l�{4�F���?�a)�y��_������	�.�����Y�(��=�^7�Z��.������z!�?����U]�#��:5S� �-�������i�z�9Lou/eD�1v���^����q�z�����k���eo������e�/�H#;[M�>]:wFV���u*�8\�\�
.N%���f�k���4���	���PL	b����1F�)�{���2th����c�Lf[�v�1���q���9�!��S�C{.BN���Y1qk0{�1T���dV[�,���GvL�+��H��!4(�����'��EHg�w=�������,��!f����e��2F�e|X����9KS�mX���3X��>���
�VYk���k����h���H��UG��O�"��l}p~7z���9jk�1\�1T���4�G���]�;�����_�o��8k7��>}:z��#���P�f9�|�#x��U����0y�?��Fi*Tpq&*�$?*�8�o����w@��u���������JQQ�1O::�o�!����[���V���&cH�B��]!��#����z��^��	Ct�m�`��]E����R�(�(I�������!3��*!�$�t�M�&��f����,%QZ��"��[�-i��	�)�4��I�z
)m�������f<3��L��fN��_&|
�/S>�H��V;��f�)�<y�avM�5�����a� ��
���6=2���^ �L��^ �KH_�D�F�V�g�O��	�nCf����0��.�G�/fJ�u��6w[�w���_}����2�x����������5�c�qk��U*��m�'� ��D-����Rz��=+%�k�"Vn���t�y�����G�*1j`F$To&������g�?c���dor�q��#�'�?��
`|i��sg������SP������|����e�eeI~�pd�'��FYe�4�������*��UY%cG�7XyUm
|��:�n�D�����$����Ye��+�0{�jTF�EP)1�����(��R[P���o�Y�6n��fo������|���0�U�����,�@�7�n�������C���;��[������D,B#l���D'zv������Q������� ��F	]�i�py:�py$f��1[�r�4� �]��=T~�����1Z������m:�?2���6k��_{y.gO��<KsZIL^z}H�+s$��D��8_�q�������K1e�L�����g�WT��c�BvAs�65���
�^�z�o�T��1?L�H�O����S��]nZz8�Z�s��Bg��K������a�w�v����*��Dn���u��|���t��YX]@(P�6�[�E��i��3�d�2�c�����F�L?�w�
�}�4�o&.�oc>E^V�4||�v��Po�c�q:o�&�I����9S�]{/�qc���f�#��o�����#����lf�T�P8&���B
�[	��W�����fX���l��%�����u��j{C��4�������&v�,�(��%�c�3���jowc�����:���]��x;H�T���Ev��vmC��U���@����"/�A��au���!=�m����Y�[��a�Bz�����le�3w.�D]��?�FVa��_)�:Nf�*�*{��_�5no����ej-]k�.d+�����|W����*�B�\D�p����+��������
q�F1�:���U���n_��a�^�kv�'f���^1v�������z���:��^�
���}����������v�p�Zga��G�5��
.�F����
.��
.[�d�l��
�>�(��sO�����bL,Ym5~2�]"��A���K��|u�1�V���T\j����ao���
L�].�}ici�}�/�����Y�	��yV���b^��B(	���	cY K�3QR�����3�'�5SVDH����)�gJ��k>���(������^�r?�����?��_bY1n�q1z���q�*�����
{�����_�[f����������s��}�}Z7f����W����Ve�DM���U�$1�F�=���N1�,!����0Z��+T��C�:F�1N��.��i��Yf��F|������5n���g�i� �6Lj���&�]{��U���]�
3�F�xNIY�'�eFX��,�?���{�p��a���1&j� 2p����1}���ab3^[ I,�x�������Q\b�i������u�.�u�����as.$RY����E�����fY�r�����~��������LTpI~��W�e���w���N��s���n��7�,���p�*|���(����i�F_2�%$!=M[���|f���h#@�a;�6��y~�:�4��p���u�,��q�9WZv�*����^�P�td��m���W�i�f��������#1�oY~K���+�����{B��'cM���������.����RB�+���
g��8p=�H"�p������'����4K��vL�P]~�K�PhG�l����u��L�BE�0�m��:�67��
���s$<>������w\�|	~������/��F�uQ��n��A�1,��1"�H&��0�"#�3�Q�k����[�R�����������f�l!��Z�S�����f�f��;[�C�����%�,���_]&�gI/!��f:�l�
�.����b��3\�t	�E{^Bh������a�n����:��u���p�2�x����f�=U�`�
�����2��q!��	\��
.�E��C�-@�N��h�"�����;n��v�����p������2w�<�7CcE�P0����F�i���O��\����PU����4_�3.h��l]���^:i�![RBIY%V���aV ���<L|��
!TQ.�&������6�c��r�������fCU��Lwn��,��d����B��
{�ig(���m����j��^��2���V�Yi��l��r�l,"�Wz���H�rzO.��T#k���"!�(��H��V;��m:����g���6\6a�X�s����d0_���/w{3��E�SPi�CVc���Fa���1f����������n�X�����I�3�|'	�E#�E��^A0kM�m�����k��W�<���T]��S�B�Vj-�����Q���(V����;��	���;R�,�14�k"j��-Z[F���x.0��e��I�!D�4:�`����0�S���b�������Ae~�~N��-���N�2�h
��6���� ��\����
.�D��G������.b_���_����c%�
�E�Y�)��7|=��lis&�V��gT����8���;�H�syY�>s~���yee%�����wn��������� �/�����(3��i����6��@�Q��CEe������+3eEX>�W��R�P���<�.�yE�o}p\/3A��Q�}�=��w��:?�L2�;�����O����� #
9@�/�t�\�{�h��EK�>�2vGv&=2h��V�1�$��i���#��p5��-������1�IN���t,f���=1���)��v��v��%��A����fo���<�m%M|{y)�<��p��ZgN�Y�53R3���a��������u8��^'��w�A���G[������6�����56;f����5��B���R������s�p�L��Y�gt��9����|�m������g���e�s>����C��G���>���c7kF�����Q������|����<��c����p�=���+��k��������j$m"�p?��
+
��
���kT�&�v�F��@�i ��*G�4nk^ho:|Ilm��Cn��c�[MCC�����S��4��*��
@>����0�j��$�'�9�+����=�nX�Q,-d�+���L1�%�X1��[dp3����x���9����1��r����tl����2� ?3�n
����|A4���ev���h�c�iEt[��G8N����!T�#PQ�@��)B��^%�)h%T�CeQ�����/���oY��4VdY��5��m*���-����k���Sm\$J~�&�v�qC��zy��.�C$��=����K�
4.�I7�Q����g������e�C����U+0a~2|��IWV�q{
G�vV>���R,[�L^�ok0�?_�-_�\^���`��bE��}�����K�M-�([\��
.��
.[>�[�n-��=�.��R�r�-������!��^~�4�i�p{�G�^,�v�e�~]x�C��8��S�1�Q]�����_�7�gm��_�3M����
����(����*�2SR,)����@!��	�	D�f�:���]a���^�#���K]Qx�)��K=�4
t������
����*��L���6�=�]+4����,h�',�f�H��om�Z2j��NIx�s�em���]��W�]n,�o9�Fj���Z���������f���_�6�5n�)�q������j/U����r�x��!H<*�m�������[��{��=<Y��sTB��
�U�B;�^�f�Y��X^#��(�����TTT`�������9�����/_���G�����G���k'����NUTpq6*�8\�\���e��uw��q����;��3��s�=g�n8�gM��_}_n3>��7�<�y2��~�H#x3Q���D6�;�o�����.�z��8+�f�bM��+LNOo6����e��Jh	)R��M|����8���=dzB��Q�1F���7�r%&=w{��F��
��r|�MC�?&��_8G�N����K�e�X�c&��&>>q�9M#�����ai�#Vi-_�������I�R��UWc��e�V�I]b��5�0�x��>c�B��_10�^V�YV�A'f=7(��P�:������	{s�:�pc��k�|�������V��
�������y��a�[��[��!�U�/^,����g�>;��"��
.�Eg��K��������e�]����?8����Z>����7i�N��c���~o����K��Z��P����k��VU�h����pU��JJPZU(a��U�U�X�:����1��}Oa�j��n;��B�R�������-�,�h�����G�Q�����e���?���������HL�,_�D���P�������jA
{J�m�Z��_�r�y�y|���������"���*H
�^���ay����.�&�.{�l8�b� ��e	 �;��_j�e�\�Sp1��6M��:�6����P������\TpI>TpiB��Y#/f8�|���vm
l����4i�]�a���s�1o1�23����1�>x�4�v7���h��k��L�1i�1�������}d#c$E]>dy�����
!���i1ch�8���r#f���e���4��j)���5��/��f��������>3����=!����m��[i�M�g�]n�I�;����37�������B_��U�~z�d��lcZ�H��b�����yx���e����c�s���'��x
.]�W�x�3����k��5�$b��e��2�,�@g���sQ�����������9��C��G�w�\js�������%K��];�v�����PR��bD�#����V��~o����J�i������TmQ��,[�t����k����f�����Z�>\C6c��������uM���{}�����k
W�^f0��w�3K�3��$�2�

w�����1��������,�66������Sr76�I;$RY��&�EC�n0��>�{�����-���sQ������\TpI>TpiB~��G���.R��G�kk���BUU�<�6����x�������k�,<U�'rl)+�4���M�?�I#��b�V�����Y��fB���m�$f%�%��ZyDj�u�����LI��+��Q��'���Q$��l��
L=c�F�5������~���1\h56s�p�	k�|�&	U���?��U���N&cl����6��w���~����3Q������LTpI~Tp���X��T>���$�%��7��Y�x�
o��>�Ll��^��u��:�|�G�O%z�����1u����R�����U�������=I1"1]�4�SF�B��	�Ns�mwr�(�v3��%lH�*/�s�a�b�x�8b�MKV<�-���@�m�s�B	s}����.�=cd���TQq	JJK��S'������W�0{	��;������;&��a�H�|���
.�Eg���sQ�%�P��	���/�(�/�qff�.c����������`s���ZG�z"1b#���>vR��&n��2\�6�h<���.���1�J���0�L���Y>��2�By �6��������c��l���E7v���\#��x��7�������OU����Bv��v�R��/,/
��h���B��td��j�]z����
.���������k'@���	s�)�a����rA��E�-O���%�$��uy���q�%���w��Glx���i*�����[�E�ys�#��L[&LB��K�WB����FN#�n/�DdZ�/�o1�MQ���W�-�G�!���MaC�5��v�G���Li�d[�{Al~cHX\�S���/*��������A���c_�� e�"�,���-��������L��?��x�(� \�C�����n�+�'�����S�l��y9u�T\��Y�Dpa'2�Y��<}�tt��]�$A�&����������o�7�t�][����1q���2_~�?�6`rsm�l.�(�?i�/3b��4�Yc�^ f`JF���4�}������q~�r�����Y�A"	��6�Wb���F��]i�'b�j�p$*�>�{�!V�3H�%3l�9%��J�q�������0��`��V��h8��������//�6�����^�����+W��E��3�9����g����p������C������������?_�����Nr�9>R�������c���N�Kp���/�����������n�n��?Ej���]���(�Hv�}�~
K{���D1C<�W?N@U��`G6~^$C��Y���J�������(���-�{�M�|����c��Is��]�l�����g�-���W������o����� n�8���h-���s����i�M���>Gx\������L�=��}�ku�����ym�:���1�BC��=�9��
�6�o�n��s��p���NfTpiB.�	.|q���@)_�o����f4���h�q?4����'���@����->�\�l������4�-�K~������(��$����	[�r���������[v��&���*����8s��\3����K��e~.��-�7����
��	�HR����f�-�	�pU��Y$����ICl��a�%�P�e�����s�=���>{�=\��?O���?���qI��h&��y�����Y�|n����p��O�2LVZ�J�(�cw4�
7�`)Z���*�.��9�f�o���������O�g3�2b��YiA����`�|�-����lm��.?�����BT��-(G���Wi�f?�g��i��'a�h{�y$lm�:�3�fUlq��{����t�����,JVTpiB��B/{��~�][O������������j}l��?{sl�1�a}�{4����7C(�����c�N�0K��!4�>Lj�9R�p�
���P�����'=Qc�K�^PQEQ�d�/N�[5��K������-�5�c�:0a>����\cy��� �Un��6I��JGTBi�\K��fW��B�0Zg�����!h�����x0e�h�\��
�[����N�\;i�v�=r�w��/�M{�k����W#���g�����������h�����\X���H�ya�	m@S�������(���*l��}zAA�fy��T�����Y�f8����'���5��`�I&��P������%���h�p}"��bC����/Wg��\OG�x��I^�TQEQe[B�-������zK<�v`���R\\���<�v�Y��
���>Z�U���!G,.���q�6;�)&Z��2�F�h����{��h8����E��$�O��r�`��i��8X<E�������_P���;�O,���Y��l���o/�u([���AK� �U{4�I�;[��)��(��$�}�������S'|���vM
�}���Gu�����vA$P%����� a����C�z��g�^c�IO����XF��F����>�,����(��(���r��gJ���oKY���G�m���$���-2p��' �3kVVa���JK����@0�py)�33���!	�[f�1�GWz���m�m���N���:�eN:|�Y6�3��}�H�q���[H�p��G��C��#c�=w������>i��-$�Ut��
�
G���UlQEQEI���	���q�a����^�`��YI���|��[XTR�t����:P����-x�WQ��������`��u{b}�[��pI/5	_%����g%�[k\�r��IF���������K���c�����g�V�n�����(��(��z�l���QQQ��~�
������C=��/�X�6���X����"�m��%� ���yY��m�Z����p��A}D�!��[�.REQEQ
)�8������o"++��7���������^{�%��������s����&~$�*�j��"����e,[-db���mX��]G�5b��j��X��G�\��;q_�|n,������8��nN��,�6T44�.O������ �Gz�n�x3�Js�6�U�����P�
��,�=���g�EZ�^���f��o����\I ������-=����^o��(��(��E�������w�]��u��y��Ix/&(�����^QEQEQ�����b��q�����h�"IDy�M7�K�.��Mc����5c
�"i(^�L��/]�4�G�
2Dh	����F���T���Bep�=�(2TQn�����J�2oF��J��8��f��B���
PP��1!�Yk�"Qd����o�X��L��\��jA���|FZ�J�Nw�X$,���4��f��%�TEQEQ��
.[���<��S���bg�}6�?�x{��(��(��(u����WQ\n7b�(�\VN�8�Ma=
�4���+��(��(��
.��(��(����4�ax32ep��R�c��73�z�g�UlQEQEQEQEQEQ��AEQEQEQEQEQEQ�MDEQEQEQEQEQEQ�MDEQEQEQEQEQEQ�MDEQEQEQEQEQEQ�MDEQEQEQEQEQEQ�MDEQEQEQEQEQEQ�MDEQEQEQEQEQEQ�M$-n���Y�z5Z�l)�?��3F�)�J�0i�$<��C���8���p����s�dc��i���{�v�1x�`{��h���=wm�-[����k�����Cq���s�N �<�Y�f�k�����K���k�U�$K�.�y������}���kk��br�����h��=.�����8)�?��
�%���b�b��
&�(�M
�X����(bb+�"(�����>�<{ss��-����_�af���[v6��������Y�����_6�����V�������{���`S�w��>h���.��J���8q�<x�Y�t�9����I'����4{�l�{��>���M��l
���y�����e���T��t���hvz
��x���f��������������o&O�l���k�t�b�W���"/^ln��&�����zP�I�&��~�?�����'���������|�rs�G�K/���I[�{��Y���s���l���(
��u�L�2e�����M�-�v^z��m�v�j�#Blf+W�������4j��~W�X�Eq���O���z��T]��Cq%	�K���{5�������7�h
�Jr�������{������������z�����>��g���`s����m�q�q���,�Oq���������$�Y�����������c��]�~�o��v�6}�*��6m�i�����������g�}fj��a���ZS�~}w$�w�y����\��i��=��7���{�6��]��y������6�*UrG�<��sf��q9b�>��g��9��b}'���5�\�2�Ez{�1�=i)�6�|E(v?~�+EQ�%��_�_��y���3PT����9���^x�����g��<7�������F�{�]w�3�)��������V��g���%^�qgd�Wnc����qn����L���[��_K��}�Y.�����,Y26c�wF�Gy$��ZF���@&]u�U���>K�^|���s��ywF�T������L8��#���l�2W��������T�R%6�Yz����\-�|��;#����yn<�ug������;z�hW��^�z%E���c��&�������� ��e�����A�E����o�3P���n���r�QG�����UsG�����r��%����9r�}���i��u�k��=�����0w��X�R�r��x�F��\9�wF���*U����3�8����~������k���U�V��@��I��YYj��y��x�����K/E�7x�`w
S�����gW�
	��k����o�>��s��q���E�a�f��*������L��3���b,�e�+W�7����~�eZ�|����j��3����eQ7��9]�tI^���Q��=���_��������>p%	��s�-���\IVE��;�t%�3(\��s�=m���]I�&�T���7l�0[��b��E�ly��]I,6e�[��5d��<�w�y�$�9s���U�>�+V$��n��?���,[�z�-�4h`�'L��J�n\�Y��_~y�z�z����q��Jb���~������}X�n]W�A���a�\�	Q}�j��������e($\�<�A�������Z�vm[��S(�6l�`���'�[�til��9�E����/wg&n�\}�����bl�Z����l���$�|�o���+�*S�� �����
�W��J��(��s����v��5o��W�����+�~�5kV�������=����s%���h���J�)�o��Z���~��Jj��r}.|��9���Gm���7��[o����V7�x�-k���+A&�����V#MO����^��z����X(�]��go����\-��8x���U����K1��K�oM��G�lN>�������,�/���4i�J�z-}��w�$��O>���/?�W������Ef���O��.����-W%&L�u��q{���i���+�#���/o[�M�:�������5Y�~�+I����-&TO9�[��2n�8[��kWW�L�g�}����?A��O0�����-�$R�\�_|�+��^y�[�a*W�7}wp���-]��]G%\T�%��r���O]I,��iS[L��������|������_w%	�����Ns%($\�,�����D���t�H�z/���7������{����o����$��O���]	6�P�=���Z�Lw$�O�E%K|�F�<��,�����/���+A��>���Z�J��x��
�^jJ���\v�e�$��uS��{���o����_��`c3O�Z<�=������,�~�=�C���^r�%�$��l�_�g��e�i�����
6�����rz�>.��ZO��D}p*��c:tp%���m�����E������
�	E�n���n/�e�*�a��:�y���5Sk�?��>���K�6ml�z8��|����y��'��o�a�����c��� 3�A������JR[�d�=����w%Y|�ECy�W�(
p�B��.a��Kp%���%���Z�li�}p�?�Q8��R��%���V��������Z�����W^i���F�z�F���c�FR%���+[�n�+A�H�lY����$i^����C0��kn�������m�i��uZ&O��l�oF
��o��r�-�$q]�^���Q�������o�hUcNmGO�|#��5L�S�N�\5@
k���=�{/"3N:�$��>�zu�h(s�CdG���
��0��F��^�O>��+A&�1���Wc�0}������G�H�����{��p.%���������Z�����~�������^{�	%{���J���9��w�qG���&^���G�y������&~�%J�u�&w��52G��y���0=��V��>l�����9s���?W����>v=e��F��	v}�����d��j��{os���x��N�j���a�OD�	��4Ye�]v���Z���!��mk����x�i���v�]������rM�-�h�|���V�Zv?����kM��-Z��t���l���x��v"�(���������oo�����U]��
��0]��{��_�1�������W��.����=�l��6�B�
�$��l����3f����Wq�)��b>��#[�����T���>������7�t�����w��~�F����N��g�k����z�]��T�bE�p�BW�L�u�]�7�|c�x�	;�v*��O���u����k�xE>����w���N;�d�A���������z+�s��e�����_/���1�?w�=��� ���Ad��w�,m���+��������w(�����+����Wj��E��b����iD���s��@zH�#?���]G}��/E�E�Yg�e�]�t�k�(�Q*U���TY�v�m�^v>��B�Y|��6l�4h�}�u�-����#o.�/�7����+��R��]�)S����~>����g����_�~�f����m��o��������	%K&�V�D_�U0�t�����x�bW���kw����=z���@�r��6�9v������Y���7`����������/�~��A���'���O7���u�F��:�������E��
d<}����n/;��QP ;��W#(����(�F�i��Y�~�m�HA7�9�s�q��c�d���jD$���V�&ML�>}�^j��w}=��os;WJ�*��?!���l��~�z�����o��UW]e:w�l��Yco�^}����l<6i���u�r���5u����7�0��M3��~z�>��������7_��]�QD����l��������[o�k��
Q���_�6Xy����wq��M�1�
{S������?��Q�H�#�
����5j$o��h:��sl�E_�Gy�-���T��[^��uS
O-�?�x��eK��!/y]+ �+D�|��Y��E��A��Gy�� R��d�z(���f��1�R][Ud�{0�s}�>�3g������?��J���O�����U�{P�M����|+$���Vn�$�X�Z�j���������\�����I���}^6h�����,�����V�y��U+[%�����?4�������dA>_Q�����zQ��e���o�:U}W7���,Z|+���������������=�������t���������{��f�w��Y�z�Vw�Y��]��tlZ�nU�Z#o��d��{Y����U/��������7�E���-FR}�y��H(4���C��	�D�W��k^�w��/<���|��[��G�������n:�r��S'���{������Y����]�8�76�F�2������a���O	S]k%������g�����
z�s��r����A�����o=/���[H��O�p�����'�
������g��=���.W���������_&{.�^��!��Y���7�r�r;�������z�]p�9����VC�������^I�� y%�y�f	�bD-s%���n�j��������d�����&Lp�	�z��,G
���\�J���x���l�xu����/l��q���^K����{/9��Z�u�|�??�B���E!�kf���p����u�����l~����bQ�	��C��:���gwf�?�v��<.'�p�+Mx��������k�R�g�0q�Z�Dk�������[=II6}6��_��f�wo�s��|�9����C��y��7�
9S�vm���7��&�
�
����'������������	�wd��k�����g�i���]�6`8�E���������oJ���o��+�{�_/��s���~�k������OI�
'���i��z����,���]��[�����y��\���~�Q$����+�O��'�D���zJ��+����_r�[o�Z,����1�
�0?������O>�ku�>���l�M�,���{l�Z���o����SM��|P�V����i.��������>�n���~A����;�C��1S5ap�?���V�R�m�����QGe�A
,����T]���������=��5�0�37*Y��m������B�X�a~�x�9��������M����!���d�~@vJ��;6�����2�I(4l�h�'Ul���`l�������A����_�|hQ�j�R��M5�����,��g�!��M(\��r�i�������DQ���^QIN
	)~�tn�<����h��,5��{Oq��bz��g�����5|�P��Wl[�N�F�#�R�4k���_|�E���~�V��E�*)�)�dK�+����S�n
���Z�7�R[���A
l��4*��������TB�/���+������$u�~�����'`�|�TCX�E]d�Q���o�Yg�e�(|�Z��R���R�z�F�8�r?�u�]c���w�b�	�����[Q���+a�'^S/M4��=�������Q^?�16���>����/�'�5��(PUkm�H�=�N9����O>�^��I����&�z�]�v�l3�f�>/��=]����m-�{��)���G�(�����c��U�'�!�_?<x�]i�)���1�!J���G�I'�d�(�^�
z�^z�%���������Fr��w�1"2���o6}���=
�}�������#F�u�F���B
�������2G
��(I����G��!�&z��q��$��ou?P��>��y����0�/��0�|
�g�����R�Z���%�Fs%	�][�t�RW�������$^aq%�
:��{������6m���1c���Xl������#�p%	����v�+���������?v%	��/������+Ix���my���J*U�+W�\,�e�Jb�i���s5j�J�)������g�}�JN=�T[����+��l��Q�$A�I��V�r%����>k��u��J�A�-��Q��k�1c�+Ix���my�-\I,���o���.���$t���������$�={�-k���+Ix���m�W\�JP�N>�d��X���d9���������$��Q���s�����m�c�=�J���r}�z�~��-k���+I�w���JP�=��U����]i�z����9����&����$�s�=���;�t%(�V�^�|o-X���&\~�����Gu%���a�l�-���J��mk�?��CW�������$J�&M��_~���$�)S&���p�
�����s%	x�-�3g�+��2a���?��C�$��.�?��+I7n�-?��\I���^�z�$a����\�2d��_��{�����_o��=��o�����=��N;�������b�-�e��Ww%�$���E�i���?]I��IT�t�Be;vt%	�uU���/�����'�	�bF7���Qpr�}���U�f�����������&[m�U�}����O?�&��r�)���>�lwvB������v�i���z*v�A��f���3��gul��v�������+Wvg`Sz��W��������,W_}�=�������z��U��^}���1������������7�|�k��V@����s�ug%?�;w����_�V�Zv���owge�����
D��sO�����3�)�|�I�Z�f���?�Lfo�����,>�v�	'���H6l���r�%��c����������dNn	��+W&���w������n����aU�V��.��b{�A����{5��#���;�0{��?�x���o..[%[t]tw��!���������f�>U=i�����;���;+��;�l�)�Ql���o�a��-�2u�T{LK��=��j��e���ja�\��Sc��u��}�l�%\��%}>����[�L�<�����F�������?W	d�f��>��������%-GuT��<��=�b������:u���^��G2q���1�V�w��������Y��'�|�>��l���W����k���M7��������>]�t��G�������;�cjd���>�m���;���K1�d���1���F�K��_�(:t��_�������.��k����I_����E��<|��7�x��d7h���
?-z�{��U�O�i�����"�t]T�f�M�IDATa�����o���N����sN�su)�`EWK��]�d�Zf��Y�����Kc6lpgd�[�Eh*�������DM��
�z��V}(�z�}���k��������?WK�=�����%x�#>.[�|0V�|��5:��3�?��c���_r��$����c����o�F����?��!��e��5.��Q�y�(J�E�w�O��E7xS%P�80��W�1%n�Y3g��v�R-�Q�s_�>��}��7���������O����/y��)a��S�=���Z�����N�2eJ�����X���^u�U�
[0�RB��l4>��Lt���&��m��u���y d����E�v;/�{�6���nG�8��=�������6�����@�H����@�H����@�H����@�H����@�H����@�H����@�H��l�������s{���;�0�,^���$������3g�+��W_}�<���f�����x;v�8p��?�+I�����j]T
>�����n(�q��?���,����y��'�_��J��k{�13e�W��~��<x��i,X�J��y�������?w%���5��;q�DWR����{���^s{���p�-X��um@�K�.��+�4��Us%�-�:u2K�.u%����^2%J�0��w�����Yg���]
0�����\~��f����$��/�0�_��KP�_�* +��W�nZ�ha���;Wl^g�q����kL�5\I��z�\q��B�
���Q�F����:2AS���ci������mL�h�"w�hz�����w���~��������$L�>��k���%�7��������g�=�4��{�M�@Q@��P��r�]?���v�DL����^�G}d"����+)\�]v�]�l��<�����KQ��{w`,_���$�^�����_o�����;������$a�����EQ�����l��o?Wl>�[�1�����t���4�oT��C��o�m��~�]��o��,\��l��6�'�}��g��n;w���H�	5�A��a��Yc��d�N;�d���+W��n�:���OQ��;���[�N8����E��@�(�5��S%����s�9n/��o�5Gq��+\K�,1��-3�o��
���L����p��.��X�s�1��$��r������W/{n���]I�������/�9�y���Q�F�I�1c����>��%|��'f�m�5�J�r%������?���p�
7��+����l��n+A=�������$+�	��J�s5qQ��D�67.�R�zQW�(~(���>���U�V�(+,U�T����jE�o1���[�un��M�C�E������t������n�k���o���d��Ql���]{����!�+U�d��Q���5k�����(�����uqt�I'�����?�p�"@���U�����D��
����2~�xwf�4q�z����>�n�������V����i���m=��l���&w��c�w4#G�tg$���c��>s����?���;#'%tN�&M�������]v������.�_|�M����h8�9s���4i���u�]v�5�ZFi�ynDck_���Ad�?��C���`{������:v������]���[o���}��7f�}����r���'��{����=��J
��������i��=��#yn�Z�L�������������k /�����kj^���g��w�=Y�Tr����/_�=����Z:t�����k��e�;wN�i����k���N�
���d����k�uG�z�){Ls�����~x��0%1t����Zn��&[e��A�~����W������O{L��(.���x��v���V�^�a@|������>���S�7nl�}l�Ga��M�6���r�D�k�������(��2e���=���R�;��H��K/�k�\H�@P�jU����������
P���?s������>���
�w��cD-�D���kX,���[5i��<0����lw}Ul�( ��}=��O?=��d
@�Q=%R��)c����4�66��#�����~��>�o�����1��+H����c���'�|���S�Nv�m��Vv=`�����n���d�~V�#Q�G�_�E��]���Q�Q��
��E����m��f�/���M�����]z^��>�a�*����/��S0����J2i�6=�?�����:_���!C��������������F^J�(��B��k~
)������S�#�����`�r�!����Y���O�<�S���5�j����1c�fK����z��Gl}��
��D�����������o�S��_�Y���HS�*��/>q���z��)�Q^��b�X�;��1��8�s�XGsqj��t��]M���->�Q��]+������G��i��f���
����T����z~�����A��}�����	:�8Mq����k�F�@Mq^8>������:t�}N���j�8�6^{�5wf�����
��@��$�����tH����]) S���������;\iB���my���Jr��������Jr�V�Z,^�w{	Gyd�L�2n/o*T���k�q%	�W���+��X<�p�	�J���+�n/wk����CK�
b6lpGb���9yl����4�R��-���k]I�x��{���\I,��'��a�\i�x0`�=��3�$!^����@���b�&M�e���+I����{�q%����~k������4a��A�c����e��MK��[�M�2%y�?�f��������A����=��MW�]<H����+�Mk��������G������+�s�M��7�L~��k���&�����c��|*>��=Wu�T�6m�Z���Kh�������������k�N����.���z��]I���N��Jr��������7��&4k���_~���$����-�Y��+�r����cu��u%���9s���o���4������[n���$�(�����:��$:��_z���$[�b�-�2f�W���$���9������������&(�Sy���]I,���{���=��&O�����c���+�M�����'z�@��%���{q�^���s�s���g{��1{���V���Z�U~�����+��y���]i�ZZ�e�h�.O�0k��pY����{j�:�vpbD���1
g�I-E�D��\Eu�W�Q����'���I^�cy����=c<�F��%���+Aj%�2���Z��Z��i�
������hRu���N�]���GE,��P����/�����2a���%H����K4��z���z�����j���Yc������g�l����J�����z�+.�C��5���������+�:7~������z�7o��n{�1�P���s�]����z.IT����;���]{;v�=��b:����6.P��qv���Z���j�?���0(��c��iv��I&}���a��������o�o�v�!G�\��^�`���	'$���<yrr����%��.�E^	�`�(��k���w�y��=
����������S�{�u���?���Mvi�
+��0%pDC���a���a��s����������B����}��n+wS�N�7��������*��[P>��$A����Z���(�����
�;wn�C�����:�|�r�w�u��W^i�QI
u��������M�6v���u��6Q����+F����tcPH	(B4fnX~Z�y�%e�QB���.���I�y>���Wh5���4��&)LE7�%����8?��������<4����]{�����5��zk�1�������9�+W�mm
�r����N
�j�z}��[��;���S�DcD������	'r�����?��5�~��TR�#�^��~�z��^x�;�o ����.�&�n��L����o���FH�I�%j��u{[�z����jt��L�'�7���F����T�'P����F_>�	6������wF��Es���Rl�C��X�?F�H�@�!����a�4��O�hr�#F����]��L����mY�h�mx0�R� 	�(>�J%X����k�$������.�;w6o�����1�t����ojr�(y��L���j3jz}�7��p����;3;�gh4�+�:�o��k��=ztr�/
=�z�b��>���}����L��������b/?@a��R5@��n[�w���7�R���G�=���pR���1G%�
�|jh��~��t���6TBF�l�=�h
Q���+��L"�[U�E-���Y�����w_���z5q�:ulWt�)Q�d���j���s%�.�>HQw�t��i��L��-��#�<b���I��)0��������%K5����q�x�����"��VA�w�*!������7�Nz��m�a���o�l*��n�����T�^��.���q��8�{��e���?����:�/��b�QC���Q��-�G��O�������:t�������Jblj~6%���dW�!��C�W�[���m�%�T���?�����3m/�0���6.��\�����f��%K\i���qo��AtC_��4��Ad�T��m�s����Kv�VX��O���,X9����Q�����#�Z�Q��p�>}�]��o���H��o!�dJ^|0�Daw�u�]����P�>
]�IJ����c*���=J�����oo����]{�q|�1�W�}��������	�b���[�\
�t��� L�#��
U��|rD�)����_5j�����|}��+��������z���'�>�������\��z���0���1OAi7���yZ���P�~���y~�@|�]��@�� �����{���������������Z������-Z���
X�{��BA��w������g���tw�q���?����I����W_��D�|���?��z�h�,=����T���I��������$�j-���*��	&�9s��Hx=��v=`�s��7�9z�4���h[l��[�\r�%��������Q�KI+=v���_TRJ���o���6~��t�9��uT�	?�V0���k���x��g�Z=�?���[����FO��^{�6��J�h>���:�{����:X7�����#(��a�3�OH�������o|��'��WZ?���?j��n�}��^����Q�!%�4����j�I��j9A��i�c]7K5i������r��`s �E��7jN���b��m���
aJZH0 ���t)�~%B4��oe�3��w��5�e��-��(Y��KA�Q�&=�����VM����H�����;�rL�_�^�E	(U��T�]��
��=XR%R4V�h�y%T�����[���;�l�

s������nV7|?��Cz��4���j��������g�������b�'���R���k���6/�����uS�}��N8���X�@n|}W	�(~>���5����f	�4i�]��'�&a�C=�5t��1�����q����F�=�=�:w^��W���W,�:�v�l���~����5$���z<
6����rK�a�O��X$U���ZS\����]����K1��>J<)�Q#0�#|mg��e����m���BC=+F��R#.����^Q��0�cV2�F�A�7HS�5�����������@��U��=Z�(��k�������Z�2�T�n����K���M�6��V@j-��7��:�F����*����������J��a�l�E�%y�b{��������(%��BN����������R��2z��pja
�J��SO5�r�
�D=f�s
�<gY�����@��\���7�{��mo!5z��{jU��
`0��E�T�o�'z(�����������MP���u|���S'�����T��	�05DR,���U�U��\	��Pr�i�7�u�^�l�c� 4t�~g��}m��x��;���������Y�y5����Q��h4�Ix�_��O�7������]���)��s����K����~��=_���x|#�0=O���}��6>�|��a��H�c���\�A�U��M��cE%Y�5�&������jMI� %��{�(>�k'�cE
����g{�(�������SO�����A��R����6�����>������������������~���F=B4����T�F�)hSK������g�_L�R�a����{�S��V���ICk"y5�R���J	
%<��U��Q�FC5S�9<��c�������p�-�W���E�6h���l.�7E�v
�j�2 ��_~�����	�ix+�Y���3f�p�@jg�q�����r��Isw��N���/�M��.��W�	�Bi>��[����
?�#�[e�u�]g��O��4���O>i���>;�Iq��E��Nf
0�����<�( �[������Z���/�OD��2o�i:=��@(
:t�`������+)^f��mqmW\h�=����w%�y�p��\�R��P��(Q�m9�(jH����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H����@�H�(��{�9S�D	�\r�%�4Z��}��~��W�4�f����P}������j�*W���)S��'��|�4i��|����(
��Y������|�k��en��&w4����>y�6�lc^|�Ew��[�v��j������+W�#��y��7w%�5g��~�z����|������v{9�v�m�d���uS�T)��Ow�E�������-Z��&��=��];y��W^���@�E�@��=��ga���L([���2f���n+��/�����e��	lej��Q�����e����Ml�ZWR4-]�4�F�>��s��n����G��@�=��?�h��w_��������u������L*mNJx��W��9�t����q��&�}��g�5k�����W�~��i��f���^������o.��w@��X���9w��o�����Fx���^����o�����P�����C=����.�&���
�����~7�������3�4�W�6�*Ur%��A�=��c�T�b���Ns�	'�[n����/N�,YbV�X�������������M��^�z�X�����q�����J����4��������;�0�&]I�[&.
���1����f���SooZ���������K��32�j���%V�%��}��wn/�R,����k�NU���t�����f�m{����4�v�k�>���k�;���6m�}����(�~�]���o���_�����:�\u�Uf��e�xQ5o�<��+��A������!C������S���Ox)���������n�Cj�����fk�7t�P�������#��7�l��
t��y��E?@E�WS�n��4�|�9��DsD�q��5��_��������A���o�Q�|�I����E���'���/����Y�����Ff�=G�o��,���Y��hwF��d���z=\v�e�~��U+[�V����~�|������1E�J2�u��k���^z�<������(�s:s�L�#���>���tM��O��~{��S�+W�6����;��c�]x��v
`�B�@���i�iz�X3e�R����L���L�j�L��e����eN��3o�jwv���#�a��G��s�9��3�'Z������O6�_�nJ�������)Y�V|�����_�������rgn~J�4j��T�V�����f��
�H��c�����^k7nl��������j�E���7�|39d�ZD�%�Z+~��'�J��������/6�+W���c�=f�
=����	�C��q�v
��^C~5����^����^���R�l_�.Y�������G~pgf��h�����}��k��qG�S���+�p{�����3fz$��b����%��~�)Uu�d�bL	��3��%�:$N,~��';��n�+���[�z�s�16PTT�Q�,jD���F�F������
����M	 Qo� ��g�}��V�K�4%e�4d��{�m��{�(�H�H���1s�����[�1�������d|�J�|��u��'~r�����;������#�8�6Q��Q%��8�������
����:
��a�����0����*J���`waU���������	ROSuA��~�����c�����9��AJMS�d`�=��eM��5�������'��G�����9���;���*��a�E���H�x>���,a�����E��3����m��K�����*�;wn�Z���j���;�c�XA�n0�6�R�2e�Z-.�*o�=���WP��
�Z���������a5��1�Ki�w �T������g���J3C7�;u�d�}�3H7�%U�2��Woa_����S����YT�:�?O
�����
}�/_�����v�v����hn����SN9�����{���zX���b��U_5�&
7%*���R��f�qz%J�6%��2k~�����+-\>FQ�=LI��Q/�������x�	W��b��}��]Iv��{��S��Of{pm��O+���dO��1jt
G�	>F��������t�Iv��^����c%�'N��|�#F,��K/������5�-	iQ��reJ������U�R��iK���s\If(`������)���A�m��&A
6�����v��J��^����~k9A5k��C]}����z�����Gi��?��uk������{�t���Vv��zk;���xj���M[��Vq%i��I�NQ�~��x ��)Y��+��D��\�|e����]I��|*�n��0LA�_�e�,�t�^�2��#�*�����g��_i�`�*A*����o������]w�]����?l�&u��5u��1��zj�k���=���c����&�~�T��@nt���K���a����V,c9�,[��������D��^�j(5�����GB7��v�j�����v[��)��gO[�z�����v��<��#����;�h|�A���3�<�L�������7:t�1�b���z+Go~
������|�����c�j�����_g�������Z��_���&�z�+-<~�pOQ�(���NW�]�R� �gR�q�7gN"������(����k/�P���Fqq������o_���>�����Gu�MFF=V?�����QD	�0�C�����5�-G��v�������}�jlt[��K��'G�i�o��+�����e���L��j��/PO����������Y�s�����JW�����Qb�
O��m����c����h��}%@4^��x��D
F�3b�}��6Mn��/���zC(�Q�J�r�!v>�`K-�\9���m�Yx�)���a�|��yC4���pK��Q"G�A�E-�0I�Gu�Y��f���mK�+U���=�����������a�����>-M�����
�M��M�c�T�
���PA�Z�)����hH�~�����^�4�-Z�	�==8|��Q�l2@���g�&oW�F����.�z�������Q������aQ�~PQ-��U��^�
48��d�J���d��<��1-j	����~���\iN,��K��L7����O?��=�c:'�����u���F�m?��C7BtCQ�,�\_L^`�|=��*Y��R���,�o����BS�L�1�>F��[o�����[����
J?R!3]vjS�BV=S��5j�d��~G}�����Y�&��i�
m������D��*�Pl��N���Y|�x?,S���zz�7Dpb~%V�4	����,5pR��3f�9��c�=���?��<�����/VOc���^������
��c�N�<��������/���5�L�M����/�:w��B*W�l��:S�|EWh�|�@
����W������Yg�e�/���Fq�b�`/%Q�Y�,�Z
�M�.�NCZ��Q�����k^�C~��w�U��=N���
�tO@���=&���'7m�������z|i����F�6�KQ�@���=5�.�������,Y���)������#��)��1�OA��w�jjT����.�R��"�+�����������v�s� �	QBE���X�����Z{���*K�4|��v���&�W�MZ��K���m�&�V�%���
� ��Q�[�-U�|�E�T��N���
�����x��T�b�W���_���U��c#mX��T8�jS�U7W������+i��L�e�^,�*�"����`����c�,������'��������s�a�r����$[<������
����w�����Zy�U��g��W�n@��e-[���>����Q-��*�(^H�H���W���O0����o�3��������Q�6�lL��[�f���n%�R���o�	�5���:��~�:��
&6�@��?�L��	R�Wu-��A	���,���G����o��AjW����UF�=�plU��=�,���)Y.����8�d����%?����KI5��X��+M�z��j��[��R5���4"C�~�l�J�e��.��*�n���d/�w�}��TX�cJ��Q=>�+?�O���M��^��[�^*z��0T���C�(�Q\�j(7Qo$5�����a�5O��=�D�/��b?@�k(��	���4P�-Y�H�h8��]
z��Q�#�K���n]��Z���a?���*�a��B+��jU���fJ��r�������F��BRO	U��MX��g���yU|�z��o��m���^4A��)�����x���-� �������K�Re�;D������ITo��RRNTqV�U��Z�X�.�T��(����*���7PlD���.J�D%�|��`�Y�����N�lY�hQ2���a>�">�O��c�c�|�W����xl��1L�5���^����e�Kv�q��up~@5�R����Q�kH0%[��E7���Mx��t~���z�b���|����W�}�P��z���>~����~�o%
��X��0�V-Klh���X!_K���7�M�/�A����R�lX���}A�	���#��J��J��E
5r�z�������J���y��Y��zSL+�w)���Q�E	�p,�������'��h(q%[4�gx�L�7�b������!��{���M��+�z�+���lU�e���jT�
�� �[�-�v��b���k���n�w1;��>Np��
��_I:����:���_n�
W������pi��0�(R�3��Wk.
�t�a���(����?�_�m����m�0V7���}S?���
��3�j�3�j7,��M#;Q�m��[�\����� ��;��]�Yj�7e�ii*�����,�u��z�K���
Z57��j~"z�lR`���}r��T�^��P�j��^44���*�:��t�%c���k���s5$�sZ7DC��zyE�q��_���K��d���a��|{4�b�;����_D�
�-
���l�%������jW+g.�����q��c��5{��MI�����Z�QR��]���������F��}T��4�b
�z�9j��8G�+?qw*����������E��'K0���.S1L����'�jJ�TL/������u�q��|�t^�j���Q���s��>��o�^����b
���|.z~������^�Y��I�%���I�Ts"n,%u�qBA?�q^�Ct�^k�h��*a�{��W��wE����{��x�d���z���]��]G�g��`���!����7�0��3�T��I']a�>f�[��&x������b�J�Z��o�B��BA���S�x��WET�y�����Z�}��v���
���5����G�,QC����#>zs�C�i2}?�{Q����M������S�d�evjj�t���^��rH�O�O����S�mH�Tts_�g%����E�^��+����-���	�q��_Gu�I�&��d�^~��;wnd�H)
t��V�2L�3��P|1��Lks�$3����\��7:���`����s%�Gu���b��Q5D�zk��'�K
R�SX����^�y��'h��`�J7�U��-��4g�b}fjx\?|n��b�[F=c4�e�9=��F��.�cV"��Zt�1f�sM�2Y�������D��f����@P��,S%%�n��v��B��uR�mH��hX�/���H~G*	�Q)���0=n�F����	~1
-��t�l4h�}��iHr��4Wl�>}\iv��I��(V	R�#M��D�z!(�R@�����f�Z���U��R����>j��$[R�����G7U�R�-���^��`K��1b�m��[�+�P�L?��W����"��X���;�����O=��])����s��������EsL,<F����*m*���+��}����":t����d��u��SW}�I��F�=��R�,T����7v�����P���7�����;�d�z���Q7R
?��_
���B����y���=�����,Q4���xs�{����=���^7x�P	O��i�u��4��g�W��E������F�n������&�W�n*�o��z����gEsPHp>� 
k�:xns&E�/`bk���J���uf����E]If����/�h�qQn���t���6z��d��gE���Q���u�E��F��T�Eo����5��w�O�(I�*�"AC��:z��wL�
H�[ .�6�k�_��f���f���6p��r�z�x�Z���z�����m9/~�"���n�j�:	J���������jm{�g�J��9&L�|�^~���8�~�+����
=�,�Mg
[n�R�������A�lX��1S7�����eL�j���W�jJn���C~���}"�0C�4%��B���Mjz����V�E�������j!�a��xQ���p���P�3�����7
���j��!D-0��_]���������a5.��?����h2P��h8�G;�c�<VO�5k�����t�:�nC�<x�^�����yU�l��!�s�K���A~b|��a���oD������)����{����u�[7�5���%Kl��r�@\�;<��z�|��G6��[������;�*�e��
+g�/������g�K�2��������bH�C:{���'O��Ro��0����O�r�>��z�z=�9V�h[1��-���i�e����TS1J��)���Ew�q�M�c%C���q����Wo���~���F�P��G����E�?��"�%���J wo}�w�����{��X�������O�o�.vG3g�����*^�q%	���-�m��\IB<x��_��+�����g�*U��U������/�u�.]����g��<^�F�X�f���Ol����o<PI�������Z�2�|��5��w�u�=G���/[��MWR��[0+����c�8)�����E��[>��������W�J���CW��f������xW���>H^������>�~MU�����G�>|xl����x[�b�;#����}�����J�t��=���*U*�\�A�;;��[oM�w���P��]�6��=z�+�[�^��?��r����#��.�����=&����*v�+��/�Y/,L���+W.V�Z5W������7o�+�����?[v�����X��GI�����������;/��I���������]�vr���wg%�p�	�c�8�����ee���m��6��e��}�����=�P[�t�RWR�mX�,�lT����Z��{Ll���������"�q���Y-��7}��7�������d�XE������;����(�~�����o�m�#F���;�������������~�J.\�<����/�&Mr?���1#Y����iSw�-A�����o�pP�i��n����j���+5v<c�J���{��X�@P=!�zL]�E�~��E��i�r�zQk|���Dx���'c
��.�_}��mav���d�4��zH�����Z/��Ck�}
�y)��I������������U��[:�?�u�|:z~5�B^�
\s�h���9Ot��M\�W���zQy���(���_��s�}�j� ���bjM�+�*�Z�i8
]� �So�_����5��/(����9\l�T�i���z8��@�{[�nm�qT��)vP�S=�x�]4,�b�7��M�0��������T�V�E�w��=T�
�+?H�1k�)�N����s_C�j�==F�f�z������R��������������yrT�>��3m��TC+v��V,�L�aT�F�hhj�8
�J!*�������Q/��4G��s�b�Qh$�-��9\H�(2H�(N�4��pH	�4�pH	�4�pH	�4�pH	�4�pH	�4�pH	�4�pH	�4�pH	�4�pH	�4�pH	�4��p�X�����*]������|��n���8�������Z�j���.��4l���]���@&�,Y��_��t�����=��h��n��w���k��v���f���v2�\�rf��qf��Qv?��l����9���^�z��e�u���`��Rl��un6����_�z����#���/�I��;.i"��&.i"��&.i"��&.i"��&.i"���BK��u�]�D��|���Ztn��m�O3m����k��sgwQ���k�7onJ�,i��)cv�ygs�%������3����(~)U��i���������?Y�R��Y�~�;�0�)��~�i���)]���U�����������@�Wh	�5k�����������+�:��kd���������o6����bf��uf�����g�5;�������;@�
6�u0F�m����-�?�-_�<Y_�t�M� �[o���)#G�43f�������<��Sf�v0���sg�[�E\(p�|����k��q��-��w�m
����wKA���Z��m%(���^y�s�����h�_������P|��������������eK������k�����4h���i����_�`�����M�
����>�v9G�����!C����k����x8p�Y�x�+5vX�%K��c�����������g�}�����#���WF�j���&������Xj|�a������J�l�j��m������9��cM�f��s�=�m8d
16o�<�OK�>��q�n�!�),W���K�~��c�o��f�dN�\s�5���v{(�'O6{����3�S�N��Gq{����^���~r{�&fj������'c��X��m�����u�]g������y��7���>��#s�����U�5w�(hRw��n��\y�����2e��
�c����I�H�O?��>���K��w������/mO�����>�j����;3�z�l��V��[�^=;�WA���?n+��SN��l=oAQs��)�a{�<��C���n2��r����[�!��S��Z:t���`_?�(����
7�����
��d�9s��[|���'��#	j�����h�s��8��rLZ��wo��Q��9+V�����-��r�E�-Z��x�����;�m%h��XS����{wE���^pG����~���_|�;�%Ld;'�}���x���,h����=��c�H����;�9c��uG��z���U�V��x`����>���l���x�����S��&N�9rd��/�t�Y^y��l�����sG��������_~�;������;��_��$��5+������og��(��O�v��s�qG�|��������;���s�=7y�R�J��^z)��Z���9sb*T�q<�L�0��[X��s�l�(�2��E��
�T�Rn+!<�H<8���<���?�����4	*[�����1�q�S���k�����G�l�2;6q^j�����G-�:� ��U+s����,ja��wkjy+h�"���)�	���u���h'�t��J�(��Hs]����p<����M����c���
��C�i��f��Yno����F'��GK��m��������3���K/u[	�Z�����W_����C>`R ���/&��O?�Jo���XC�5&Lp{�t���m���"���~��������;�V�&����	��[gN>�dwd�P������.����w�n��]k���oL�/c��q�	�[�v[G1S������i84


wK���R�Jn+o�[%��J��klaOc��7no��<*>`�x�z��^�Cz
V�-�.����eK�g��>��6�����9������V�Z��@z��zk��?��s��J�|+��l�5����nvS���|5�s�7�z����Z4Gf��}��1��77}��q{�|���nk�����R�J��7��j�%\
2�{�K8x����/�!�2!�*�I�&v=m�4��.��B����;�t[	?����*�#Fd�@����n@aX�t����5k��-c{���L�p/�L���>v�pc�7����0�&������J���?�4$��?����%K������>P�e,���c��5���n���7�0��z�;3o?����J��X�kSQ/�v���eQo�������h9����.�*�����������_���?��?�����E�Q4�V~�N7���_D=���C?H�bj�3��y	6e��U0Rm��y����
��=�\w$��f���n(�2�pQ��#�<2YQ.J��
:��[�i�(�%�9�R%}�=w4������{�.�����8q�;�lQ�_������v{�l��6f��9�j���@~���������_�p�k����y�p]A6lp[�ih�t$~
�~�j����o'��2}��;�n�_������W^��t���m�S�.�.����:z�.j1��e�_;�����.<T��e�{��m����_�T�\���?�������6s����O(�t�Ae��q[	����k�� ��(�'�4������hi����S��;;o����������hhh/�x��6�.q�i�����	�5����S�^N� '��������*h
'b��������V`Q���Y������l=[�:�(3a��`s'6�z�-�����?��r
'B����p`y��a��K�~����~�hQ������s�����f������(�6@qV,.��t�u���,��z���z���w�}��6�z��u�]n+�UW]e:t�`�u�f�}�Y��+?��m��ww{	�<��M��7.�������lz���S��������������SQ�"(����I����S�� �'a�=��m��y:_|�E�x�bw$w
6L�Z�������������/��1�j�r[@�T,.j��Y3�g��3L�z������������:L�����	E��n����J�"�7v{�b�1�5����O?mz��e.�����jQZ�n���h�2�5��r��q�g��^��]�V�Yg�e�����'��>}���v���V���~��7x�`���O����~��n��$P��=�^"�R���}������??Gl�J��m�VB���-��b��|���s����P�����[���5����E[��X�b�s������n��IM4�2��<`'�/���Q����9�S�F
�J-�8�>��m�m��n+����4=�Ps�)����G}�6S�Av�e���C~)�Ro���k����/�e�I�7V�WK�=L�J���j��A��U�t�����.�HJ65o���i���$(#�����.�I��;L�.�@��Y3r�)]��>+8�pT���W^q[Y�����w�qG�%8qe�yZd���n+'
+�[A.��E�U�����rw�}����+�5�_�'���	��1�b��$�o����y�����=�����:�me�?~r������1Y8�
����J���,_�����N�:�.]��������9��3�^45�c.l	J����Q�Fz���mBc���2��-3�_�m��_�Ks�����|���]�v�hv�tRO��v�m7�5_��g���`�=
���o.��R���?�}%f��ooh���k��Q���K���W����BM=U��������nsG���)(�0bJ�H�R�l/��~8�x��y������#��+?�����I�&�������n*W�l���{����
��:��=��3�=���T��ZI���;���G�6'�x�;��1c���Aj���5O���������j�^����l�G��p�I'��D�G=��,Z������b
���odK&����v���c-(
w��{�ls���z���_��BK�d���a�������&@��{u���R���kF��>��3���c�=���Q4l��v����+s��=�M���E��z���z�����i���u�����^�9\q��e��v.I�/Q�j���n+!���m����(P�9s��Kv��W��,F���^�������3����MGC_~��n/A�}i8�Y�f�I�&���=
���=P<����~�r��0M�?}�t��^���sL<���)S�.����G�bH1QK0�i5	e�&�'�`s�7o�9��c�^4����-$[�����p	�\-���mV�dIS�re;�~�f��P4�[��!������36l���[�je�[�b�p(JhN�&.i"��&.i"��&.i"��&.i"��&.i"��&.i"��&.i"��c���U��!IEND�B`�
#64Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Yuya Watari (#63)
Re: [PoC] Reducing planning time when tables have many partitions

Hi Yuya,

On Fri, Aug 25, 2023 at 1:09 PM Yuya Watari <watari.yuya@gmail.com> wrote:

3. Future works

3.1. Redundant memory allocation of Lists

When we need child EquivalenceMembers in a loop over ec_members, v20
adds them to the list. However, since we cannot modify the ec_members,
v20 always copies it. In most cases, there are only one or two child
members, so this behavior is a waste of memory and time and not a good
idea. I didn't address this problem in v20 because doing so could add
much complexity to the code, but it is one of the major future works.

I suspect that the degradation of Queries A and B is due to this
problem. The difference between 'make installcheck' and Queries A and
B is whether there are partitioned tables. Most of the tests in 'make
installcheck' do not have partitions, so find_relids_top_parents()
could immediately determine the given Relids are already top-level and
keep degradation very small. However, since Queries A and B have
partitions, too frequent allocations of Lists may have caused the
regression. I hope we can reduce the degradation by avoiding these
memory allocations. I will continue to investigate and fix this
problem.

3.2. em_relids and pull_varnos

I'm sorry that v20 did not address your 1st concern regarding
em_relids and pull_varnos. I will try to look into this.

3.3. Indexes for RestrictInfos

Indexes for RestrictInfos are still in RangeTblEntry in v20-0002. I
will also investigate this issue.

3.4. Correctness

v20 has passed all regression tests in my environment, but I'm not so
sure if v20 is correct.

4. Conclusion

I wrote v20 based on a new idea. It may have a lot of problems, but it
has advantages. At least it solves your 3rd concern. Since we iterate
Lists instead of Bitmapsets, we don't have to introduce an iterator
mechanism. My experiment showed that the 'make installcheck'
degradation was very small. For the 2nd concern, v20 no longer adds
child EquivalenceMembers to ec_members. I'm sorry if this is not what
you intended, but it effectively worked. Again, v20 is a new proof of
concept. I hope the v20-based approach will be a good alternative
solution if we can overcome several problems, including what I
mentioned above.

It seems that you are still investigating and fixing issues. But the
CF entry is marked as "needs review". I think a better status is
"WoA". Do you agree with that?

--
Best Wishes,
Ashutosh Bapat

#65Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#63)
Re: [PoC] Reducing planning time when tables have many partitions

On 25/8/2023 14:39, Yuya Watari wrote:

Hello,

On Wed, Aug 9, 2023 at 8:54 PM David Rowley <dgrowleyml@gmail.com> wrote:

I think the best way to move this forward is to explore not putting
partitioned table partitions in EMs and instead see if we can
translate to top-level parent before lookups. This might just be too
complex to translate the Exprs all the time and it may add overhead
unless we can quickly determine somehow that we don't need to attempt
to translate the Expr when the given Expr is already from the
top-level parent. If that can't be made to work, then maybe that shows
the current patch has merit.

Based on your suggestion, I have experimented with not putting child
EquivalenceMembers in an EquivalenceClass. I have attached a new
patch, v20, to this email. The following is a summary of v20.

Working on self-join removal in the thread [1]/messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru nearby, I stuck into the
problem, which made an additional argument to work in this new direction
than a couple of previous ones.
With indexing positions in the list of equivalence members, we make some
optimizations like join elimination more complicated - it may need to
remove some clauses and equivalence class members.
For changing lists of derives or ec_members, we should go through all
the index lists and fix them, which is a non-trivial operation.

[1]: /messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru
/messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru

--
regards,
Andrey Lepikhov
Postgres Professional

#66Yuya Watari
watari.yuya@gmail.com
In reply to: Andrey Lepikhov (#65)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Ashutosh and Andrey,

Thank you for your email, and I really apologize for my late response.

On Thu, Sep 7, 2023 at 3:43 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

It seems that you are still investigating and fixing issues. But the
CF entry is marked as "needs review". I think a better status is
"WoA". Do you agree with that?

Yes, I am now investigating and fixing issues. I agree with you and
changed the entry's status to "Waiting on Author". Thank you for your
advice.

On Tue, Sep 19, 2023 at 5:21 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

Working on self-join removal in the thread [1] nearby, I stuck into the
problem, which made an additional argument to work in this new direction
than a couple of previous ones.
With indexing positions in the list of equivalence members, we make some
optimizations like join elimination more complicated - it may need to
remove some clauses and equivalence class members.
For changing lists of derives or ec_members, we should go through all
the index lists and fix them, which is a non-trivial operation.

Thank you for looking into this and pointing that out. I understand
that this problem will occur somewhere like your patch [1]/messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru quoted
below because we need to modify RelOptInfo->eclass_child_members in
addition to ec_members. Is my understanding correct? (Of course, I
know ec_[no]rel_members, but I doubt we need them.)

=====
+static void
+update_eclass(EquivalenceClass *ec, int from, int to)
+{
+   List       *new_members = NIL;
+   ListCell   *lc;
+
+   foreach(lc, ec->ec_members)
+   {
+       EquivalenceMember  *em = lfirst_node(EquivalenceMember, lc);
+       bool                is_redundant = false;
+
        ...
+
+       if (!is_redundant)
+           new_members = lappend(new_members, em);
+   }
+
+   list_free(ec->ec_members);
+   ec->ec_members = new_members;
=====

I think we may be able to remove the eclass_child_members field by
making child members on demand. v20 makes child members at
add_[child_]join_rel_equivalences() and adds them into
RelOptInfo->eclass_child_members. Instead of doing that, if we
translate on demand when child members are requested,
RelOptInfo->eclass_child_members is no longer necessary. After that,
there is only ec_members, which consists of parent members, so
removing clauses will still be simple. Do you think this idea will
solve your problem? If so, I will experiment with this and share a new
patch version. The main concern with this idea is that the same child
member will be created many times, wasting time and memory. Some
techniques like caching might solve this.

[1]: /messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru

--
Best regards,
Yuya Watari

#67Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Yuya Watari (#66)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, Sep 20, 2023 at 3:35 PM Yuya Watari <watari.yuya@gmail.com> wrote:

I think we may be able to remove the eclass_child_members field by
making child members on demand. v20 makes child members at
add_[child_]join_rel_equivalences() and adds them into
RelOptInfo->eclass_child_members. Instead of doing that, if we
translate on demand when child members are requested,
RelOptInfo->eclass_child_members is no longer necessary. After that,
there is only ec_members, which consists of parent members, so
removing clauses will still be simple. Do you think this idea will
solve your problem? If so, I will experiment with this and share a new
patch version. The main concern with this idea is that the same child
member will be created many times, wasting time and memory. Some
techniques like caching might solve this.

While working on RestrictInfo translations patch I was thinking on
these lines. [1]/messages/by-id/CAExHW5u0Yyyr2mwvLrvVy_QnLd65kpc9u-bO0Ox7bgLkgbac8A@mail.gmail.com uses hash table for storing translated RestrictInfo.
An EC can have a hash table to store ec_member translations. The same
patchset also has some changes in the code which generates
RestrictInfo clauses from ECs. I think that code will be simplified by
your approach.

[1]: /messages/by-id/CAExHW5u0Yyyr2mwvLrvVy_QnLd65kpc9u-bO0Ox7bgLkgbac8A@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

#68Lepikhov Andrei
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#66)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, Sep 20, 2023, at 5:04 PM, Yuya Watari wrote:

On Tue, Sep 19, 2023 at 5:21 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

Working on self-join removal in the thread [1] nearby, I stuck into the
problem, which made an additional argument to work in this new direction
than a couple of previous ones.
With indexing positions in the list of equivalence members, we make some
optimizations like join elimination more complicated - it may need to
remove some clauses and equivalence class members.
For changing lists of derives or ec_members, we should go through all
the index lists and fix them, which is a non-trivial operation.

Thank you for looking into this and pointing that out. I understand
that this problem will occur somewhere like your patch [1] quoted
below because we need to modify RelOptInfo->eclass_child_members in
addition to ec_members. Is my understanding correct? (Of course, I
know ec_[no]rel_members, but I doubt we need them.)

It is okay if we talk about the self-join-removal feature specifically because joins are removed before an inheritance expansion.
But ec_source_indexes and ec_derive_indexes point to specific places in eq_sources and eq_derives lists. If I removed an EquivalenceClass or a restriction during an optimisation, I would arrange all indexes, too.
Right now, I use a workaround here and remove the index link without removing the element from the list. But I'm not sure how good this approach can be in perspective.
So, having eq_sources and eq_derives localised in EC could make such optimisations a bit more simple.

--
Regards,
Andrei Lepikhov

#69Yuya Watari
watari.yuya@gmail.com
In reply to: Lepikhov Andrei (#68)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Ashutosh and Andrey,

On Wed, Sep 20, 2023 at 8:03 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

While working on RestrictInfo translations patch I was thinking on
these lines. [1] uses hash table for storing translated RestrictInfo.
An EC can have a hash table to store ec_member translations. The same
patchset also has some changes in the code which generates
RestrictInfo clauses from ECs. I think that code will be simplified by
your approach.

Thank you for sharing this. I agree that we have to avoid adding
complexity to existing or future codes through my patch. As you say,
this approach mentioned in the last email is helpful to simplify the
code, so I will try it.

On Fri, Sep 22, 2023 at 12:49 PM Lepikhov Andrei
<a.lepikhov@postgrespro.ru> wrote:

It is okay if we talk about the self-join-removal feature specifically because joins are removed before an inheritance expansion.
But ec_source_indexes and ec_derive_indexes point to specific places in eq_sources and eq_derives lists. If I removed an EquivalenceClass or a restriction during an optimisation, I would arrange all indexes, too.
Right now, I use a workaround here and remove the index link without removing the element from the list. But I'm not sure how good this approach can be in perspective.
So, having eq_sources and eq_derives localised in EC could make such optimisations a bit more simple.

Thank you for pointing it out. The ec_source_indexes and
ec_derive_indexes are just picked up from the previous patch, and I
have not changed their design. I think a similar approach to
EquivalenceMembers may be applied to RestrictInfos. I will experiment
with them and share a new patch.

--
Best regards,
Yuya Watari

#70Alena Rybakina
lena.ribackina@yandex.ru
In reply to: Yuya Watari (#69)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hi, all!

While I was reviewing the patches, I noticed that they needed some
rebasing, and in one of the patches
(Introduce-indexes-for-RestrictInfo.patch) there was a conflict with the
recently added self-join-removal feature [1]. So, I rebased patches and
resolved the conflicts. While I was doing this, I found a problem that I
also fixed:

1. Due to the lack of ec_source_indexes, ec_derive_indexes, we could
catch an error during the execution of atomic functions such as:

ERROR: unrecognized token: ")"
Context: внедрённая в код SQL-функция "shobj_description"

I fixed it.

We save the current reading context before reading the field name, then
check whether the field has been read and, if not, restore the context
to allow the next macro reads the field name correctly.

I added the solution to the bug_related_atomic_function.diff file.

2. I added the solution to the conflict to the
solved_conflict_with_self_join_removal.diff file.

All diff files have already been added to
v21-0002-Introduce-indexes-for-RestrictInfo patch.

1.
/messages/by-id/CAPpHfduLxYm4biJrTbjBxTAW6vkxBswuQ2B=gXU+c37QJd6+Ow@mail.gmail.com

--
Regards,
Alena Rybakina

Attachments:

solved_conflict_with_self_join_removal.difftext/x-patch; charset=UTF-8; name=solved_conflict_with_self_join_removal.diffDownload
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e59f38b6ce0..329419c38d9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,8 +38,6 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
 										Oid datatype);
-static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
-						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
@@ -78,6 +76,49 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
 											Relids relids2);
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 
 /*
  * process_equivalence
@@ -561,51 +602,6 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
-/*
- * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
- */
-static void
-add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
-{
-	int			source_idx = list_length(root->eq_sources);
-	int			i;
-
-	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
-	root->eq_sources = lappend(root->eq_sources, rinfo);
-
-	i = -1;
-	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
-	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
-
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
-	}
-}
-
-/*
- * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
- */
-static void
-add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
-{
-	int			derive_idx = list_length(root->eq_derives);
-	int			i;
-
-	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
-	root->eq_derives = lappend(root->eq_derives, rinfo);
-
-	i = -1;
-	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
-	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
-
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
-	}
-}
-
-
 /*
  * get_eclass_for_sort_expr
  *	  Given an expression and opfamily/collation info, find an existing
@@ -3398,18 +3394,6 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
 	}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
 	/* bitwise-AND to leave only the ones for this EquivalenceClass */
 	return bms_int_members(rel_esis, ec->ec_source_indexes);
 }
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 0298c26166f..92dcbbdf2e0 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -578,7 +578,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -663,7 +663,7 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid, int ojrelid)
 {
 	ListCell   *lc;
 	int			i;
@@ -1530,6 +1530,24 @@ replace_relid(Relids relids, int oldId, int newId)
 	return relids;
 }
 
+void
+del_eq_derives(PlannerInfo *root, EquivalenceClass *ec)
+{
+	int i = -1;
+
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But add a nuke to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
+}
+
+
 /*
  * Update EC members to point to the remaining relation instead of the removed
  * one, removing duplicates.
@@ -1553,10 +1571,11 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclass(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
+	int			i = -1;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1595,16 +1614,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_members = lappend(new_members, em);
 	}
 
-	list_free(ec->ec_members);
-	ec->ec_members = new_members;
-
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	del_eq_derives(root, ec);
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo   *rinfo = list_nth_node(RestrictInfo,
+											   root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
@@ -1638,8 +1654,21 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_sources = lappend(new_sources, rinfo);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	i=-1;
+
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
+	{
+		list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = NULL;
+
+	foreach(lc, new_sources)
+	{
+		RestrictInfo   *rinfo = lfirst_node(RestrictInfo, lc);
+		add_eq_source(root, ec, rinfo);
+	}
+
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
 }
 
@@ -1817,7 +1846,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclass(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 469ca518571..83695e2f66f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -111,6 +111,9 @@ extern bool have_join_order_restriction(PlannerInfo *root,
 extern bool have_dangerous_phv(PlannerInfo *root,
 							   Relids outer_relids, Relids inner_params);
 extern void mark_dummy_rel(RelOptInfo *rel);
+extern void del_eq_derives(PlannerInfo *root, EquivalenceClass *ec);
+extern void add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo);
+
 
 /*
  * equivclass.c
bug_related_to_atomic_function.difftext/x-patch; charset=UTF-8; name=bug_related_to_atomic_function.diffDownload
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 813eda3e739..314736d989b 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
+
+/*
+ * pg_strtok_save_context -
+ *	  Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+	*pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ *	  Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+	pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7b..9a7b4e9be58 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+	int save_length = length; \
+	const char *context; \
+	pg_strtok_save_context(&context); \
+	token = pg_strtok(&length); \
+	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+	{ \
+		/* "fldname" field was not found - fill it and restore context. */ \
+		local_node->fldname = NULL; \
+		pg_strtok_restore_context(context); \
+		length = save_length; \
+	} \
+	else \
+	{ \
+		local_node->fldname = _readBitmapset(); \
+	} \
+}
+
 
 /*
  * _readBitmapset
@@ -574,6 +594,9 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index cba6f0be75a..2fa68e59cdf 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
v21-0001-PATCH-v20-1-2-Speed-up-searches-for-child-Equivalenc.patchtext/x-patch; charset=UTF-8; name=v21-0001-PATCH-v20-1-2-Speed-up-searches-for-child-Equivalenc.patchDownload
From a9a485a91b19c425a7f965990855e8b6ecb26160 Mon Sep 17 00:00:00 2001
From: Alena Rybakina <a.rybakina@postgrespro.ru>
Date: Fri, 17 Nov 2023 23:23:00 +0300
Subject: [PATCH] [PATCH v20 1/2] Speed up searches for child
 EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c       |  28 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 414 ++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |  40 ++-
 src/backend/optimizer/path/pathkeys.c     |   9 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +-
 src/backend/optimizer/plan/createplan.c   |  57 +--
 src/backend/optimizer/util/inherit.c      |  14 +
 src/backend/optimizer/util/relnode.c      |  88 +++++
 src/include/nodes/pathnodes.h             |  26 ++
 src/include/optimizer/pathnode.h          |  18 +
 src/include/optimizer/paths.h             |   9 +-
 12 files changed, 603 insertions(+), 116 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6de2bec3b7b..947e0366ea8 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7690,11 +7690,26 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Relids		top_parent_rel_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
 
-	foreach(lc, ec->ec_members)
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_rel_relids))
+			add_child_rel_equivalences_to_list(root, ec, em,
+											   rel->relids,
+											   &members, &modified);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7705,6 +7720,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -7758,9 +7775,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e66a99247e4..8318c710355 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,6 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_norel_members);
+	WRITE_NODE_FIELD(ec_rel_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e25..7873548b257 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,12 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  EquivalenceMember *parent_em,
+										  RelOptInfo *child_rel,
+										  List **list,
+										  bool *modified);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -342,6 +352,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_norel_members = list_concat(ec1->ec_norel_members,
+											ec2->ec_norel_members);
+		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
+										  ec2->ec_rel_members);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -355,6 +369,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_norel_members = NIL;
+		ec2->ec_rel_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -374,7 +390,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +407,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,6 +428,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_norel_members = NIL;
+		ec->ec_rel_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -423,9 +441,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +529,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +547,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,8 +569,34 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/*
+	 * The exact set of relids in the expr for non-child EquivalenceMembers
+	 * as what is given to us in 'relids' should be the same as the relids
+	 * mentioned in the expression.  See add_child_rel_equivalences.
+	 */
+	/* XXX We need PlannerInfo to use the following assertion */
+	/* Assert(bms_equal(pull_varnos(root, (Node *) expr), relids)); */
+	if (bms_is_empty(relids))
+		ec->ec_norel_members = lappend(ec->ec_norel_members, em);
+	else
+		ec->ec_rel_members = lappend(ec->ec_rel_members, em);
+
 	return em;
 }
 
@@ -599,6 +648,17 @@ 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.
@@ -617,7 +677,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * When we have to see child EquivalenceMembers, we get and add them to
+		 * 'members'. We cannot use foreach() because the 'members' may be
+		 * modified during iteration.
+		 */
+		members = cur_ec->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them.
+			 */
+			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+												   &members, &modified);
 
 			/*
 			 * Ignore child members unless they match the request.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +735,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		if (unlikely(modified))
+			list_free(members);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -671,6 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_norel_members = NIL;
+	newec->ec_rel_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -691,7 +776,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +842,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -779,6 +873,12 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -796,6 +896,8 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -828,11 +930,20 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -843,6 +954,12 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -876,6 +993,8 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -939,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1138,7 +1257,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_norel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
@@ -1222,7 +1341,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_rel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
@@ -1559,7 +1678,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1695,19 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_rel_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+			bms_is_subset(cur_em->em_relids, top_parent_join_relids))
+			add_child_rel_equivalences_to_list(root, ec, cur_em, join_relids,
+											   &members, &modified);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1589,6 +1724,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1606,6 +1743,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1680,6 +1818,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -2177,7 +2316,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2285,7 +2424,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_rel_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
 			Assert(!coal_em->em_is_child);	/* no children yet */
@@ -2330,7 +2469,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2385,6 +2524,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
+			/* XXX performance of list_delete_ptr()?? */
+			cur_ec->ec_rel_members = list_delete_ptr(cur_ec->ec_rel_members,
+													 coal_em);
 			return true;
 		}
 
@@ -2455,8 +2598,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2523,13 +2666,13 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		foreach(lc2, ec->ec_members)
+		foreach(lc2, ec->ec_rel_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2626,6 +2769,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2638,7 +2782,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2651,15 +2794,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2672,8 +2809,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2689,6 +2826,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_rel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
@@ -2749,6 +2898,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2770,7 +2920,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2783,15 +2932,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2800,8 +2943,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2816,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2846,9 +2990,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_joinrel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2856,6 +3012,106 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the list.
+ *
+ * This function is expected to be called only from
+ * add_child_rel_equivalences_to_list().
+ */
+static void
+add_transformed_child_version(PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel,
+							  List **list,
+							  bool *modified)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the given list has been modified, we need to
+		 * make a copy of it.
+		 */
+		if (!*modified)
+		{
+			*list = list_copy(*list);
+			*modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		*list = lappend(*list, child_em);
+	}
+}
+
+/*
+ * add_child_rel_equivalences_to_list
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the list.
+ *
+ * The transformation is done in add_transformed_child_version().
+ *
+ * This function will not change the original *list but will always make a copy
+ * of it when we need to add transformed members. *modified will be true if the
+ * *list is replaced with a newly allocated one. In such a case, the caller can
+ * (or must) free the *list.
+ */
+void
+add_child_rel_equivalences_to_list(PlannerInfo *root,
+								   EquivalenceClass *ec,
+								   EquivalenceMember *parent_em,
+								   Relids child_relids,
+								   List **list,
+								   bool *modified)
+{
+	int		i;
+	Relids	matching_relids;
+
+	/* The given EquivalenceMember should be parent */
+	Assert(!parent_em->em_is_child);
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		RelOptInfo *child_rel = root->simple_rel_array[i];
+
+		Assert(child_rel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_rel,
+									  list, modified);
+	}
+	bms_free(matching_relids);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_joinrel,
+									  list, modified);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2889,7 +3145,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids, top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2898,6 +3154,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2910,6 +3169,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,15 +3193,25 @@ 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.)
 		 */
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = cur_ec->ec_rel_members;
+		modified = false;
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		for (j = 0; j < list_length(members); j++)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = list_nth_node(EquivalenceMember, members, j);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel_relids))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel->relids,
+												   &members, &modified);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		if (!cur_em)
 			continue;
@@ -2954,8 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3173,8 +3445,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 03a5fbdc6dc..9fa99e32929 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3088,7 +3092,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3114,33 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = pathkey->pk_eclass->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember, members, i);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+				bms_equal(member->em_relids, top_parent_index_relids))
+				add_child_rel_equivalences_to_list(root, pathkey->pk_eclass, member,
+												   index->rel->relids,
+												   &members, &modified);
+
 			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
+			if (member->em_is_child ||
+				!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
@@ -3145,6 +3169,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index fdb60aaa8d2..cd1be06b5c0 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -952,8 +952,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1479,8 +1479,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b9be19a687f..bb4ad600849 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -578,7 +578,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -663,7 +663,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
 
@@ -687,7 +688,14 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
 			if (bms_is_empty(cur_em->em_relids))
+			{
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				/* XXX performance of list_delete_ptr()?? */
+				ec->ec_norel_members = list_delete_ptr(ec->ec_norel_members,
+													   cur_em);
+				ec->ec_rel_members = list_delete_ptr(ec->ec_rel_members,
+													 cur_em);
+			}
 		}
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 34ca6d4ac21..d67d549b1c6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4498,7 +4502,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4512,7 +4516,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6133,7 +6137,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6200,7 +6204,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6244,7 +6248,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6315,7 +6319,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6324,7 +6329,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6350,8 +6355,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6360,7 +6366,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6717,7 +6723,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6780,7 +6787,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index f9d3ff1e7ac..d826f072c77 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -461,6 +461,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -568,6 +569,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5d83f60eb9a..698ddfd67c1 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -122,11 +122,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -147,6 +150,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -174,11 +198,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids are
+		 * top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -232,6 +268,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -607,6 +644,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -718,6 +761,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -914,6 +958,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1475,6 +1520,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1515,6 +1561,48 @@ 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 == 0)
+			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 ed85dc7414b..bc3e1562fcc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * append_rel_array is the same length as simple_rel_array and holds the
+	 * top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -936,6 +944,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1368,6 +1387,10 @@ 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_norel_members;	/* list of EquivalenceMembers whose
+									 * em_relids is empty */
+	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
+								 * em_relids is not empty */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1422,6 +1445,9 @@ 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);
 } EquivalenceMember;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 6e557bebc44..cae45708234 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -323,6 +323,24 @@ 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) \
+	(likely((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 9e7408c7ecf..5953a0cc26b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -173,6 +174,12 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
 											AppendRelInfo **appinfos,
 											RelOptInfo *parent_joinrel,
 											RelOptInfo *child_joinrel);
+extern void add_child_rel_equivalences_to_list(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   EquivalenceMember *parent_em,
+											   Relids child_relids,
+											   List **list,
+											   bool *modified);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
-- 
2.34.1

v21-0002-PATCH-1-2-Introduce-indexes-for-RestrictInfo-This-ch.patchtext/x-patch; charset=UTF-8; name=v21-0002-PATCH-1-2-Introduce-indexes-for-RestrictInfo-This-ch.patchDownload
From 3446f549f37e13f6793ac60138481ab78ec4436f Mon Sep 17 00:00:00 2001
From: Alena Rybakina <a.rybakina@postgrespro.ru>
Date: Fri, 17 Nov 2023 23:47:26 +0300
Subject: [PATCH] [PATCH 1/2] Introduce indexes for RestrictInfo This change
 was picked up from the previous patch, v20.

---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/read.c                  |  20 ++
 src/backend/nodes/readfuncs.c             |  22 ++
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 322 +++++++++++++++++++---
 src/backend/optimizer/plan/analyzejoins.c |  65 +++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  47 +++-
 src/include/nodes/readfuncs.h             |   2 +
 src/include/optimizer/paths.h             |  18 +-
 13 files changed, 454 insertions(+), 68 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e66a99247e4..0bc0228ab2c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -567,6 +567,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 813eda3e739..314736d989b 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
+
+/*
+ * pg_strtok_save_context -
+ *	  Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+	*pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ *	  Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+	pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7b..0915c0e661d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+	int save_length = length; \
+	const char *context; \
+	pg_strtok_save_context(&context); \
+	token = pg_strtok(&length); \
+	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+	{ \
+		/* "fldname" field was not found - fill it and restore context. */ \
+		local_node->fldname = NULL; \
+		pg_strtok_restore_context(context); \
+		length = save_length; \
+	} \
+	else \
+	{ \
+		local_node->fldname = _readBitmapset(); \
+	} \
+}
+
 
 /*
  * _readBitmapset
@@ -574,6 +594,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d6ceafd51c5..fcda83ad2c4 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5755,7 +5755,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e25..329419c38d9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,6 +38,8 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
 										Oid datatype);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -74,6 +76,49 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
 											Relids relids2);
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 
 /*
  * process_equivalence
@@ -311,7 +356,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -322,6 +366,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -342,8 +388,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -355,10 +403,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -369,13 +416,13 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, NULL, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -386,13 +433,13 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -403,6 +450,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -412,8 +461,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -435,6 +484,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -551,7 +602,6 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
-
 /*
  * get_eclass_for_sort_expr
  *	  Given an expression and opfamily/collation info, find an existing
@@ -671,8 +721,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1026,7 +1076,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1115,6 +1165,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1124,9 +1175,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1184,9 +1235,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1196,7 +1247,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1262,7 +1314,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1318,11 +1370,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1363,11 +1416,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1732,12 +1785,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1749,12 +1806,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1816,10 +1873,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1830,9 +1888,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1843,9 +1904,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1903,7 +1968,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2578,16 +2643,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3063,7 +3131,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3151,7 +3219,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3304,3 +3372,167 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b9be19a687f..92dcbbdf2e0 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -578,7 +578,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -663,9 +663,10 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid, int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -692,9 +693,10 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -704,7 +706,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1528,6 +1530,24 @@ replace_relid(Relids relids, int oldId, int newId)
 	return relids;
 }
 
+void
+del_eq_derives(PlannerInfo *root, EquivalenceClass *ec)
+{
+	int i = -1;
+
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But add a nuke to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
+}
+
+
 /*
  * Update EC members to point to the remaining relation instead of the removed
  * one, removing duplicates.
@@ -1551,10 +1571,11 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclass(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
+	int			i = -1;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1593,16 +1614,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_members = lappend(new_members, em);
 	}
 
-	list_free(ec->ec_members);
-	ec->ec_members = new_members;
-
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	del_eq_derives(root, ec);
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo   *rinfo = list_nth_node(RestrictInfo,
+											   root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
@@ -1636,8 +1654,21 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_sources = lappend(new_sources, rinfo);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	i=-1;
+
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
+	{
+		list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = NULL;
+
+	foreach(lc, new_sources)
+	{
+		RestrictInfo   *rinfo = lfirst_node(RestrictInfo, lc);
+		add_eq_source(root, ec, rinfo);
+	}
+
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
 }
 
@@ -1815,7 +1846,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclass(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe14..773d703c881 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -646,6 +646,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 73ff40721c9..1cce43a94e0 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index f9d3ff1e7ac..3d7671e2e85 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -482,6 +482,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e494309da8d..475a65475e6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,6 +1194,12 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index ed85dc7414b..339e09e6abf 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -310,6 +310,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1350,6 +1356,41 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * TODO: We should update the following comments.
+ *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1368,8 +1409,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index cba6f0be75a..2fa68e59cdf 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9e7408c7ecf..83695e2f66f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -111,6 +111,9 @@ extern bool have_join_order_restriction(PlannerInfo *root,
 extern bool have_dangerous_phv(PlannerInfo *root,
 							   Relids outer_relids, Relids inner_params);
 extern void mark_dummy_rel(RelOptInfo *rel);
+extern void del_eq_derives(PlannerInfo *root, EquivalenceClass *ec);
+extern void add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo);
+
 
 /*
  * equivclass.c
@@ -162,7 +165,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -188,6 +192,18 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
-- 
2.34.1

#71John Naylor
johncnaylorls@gmail.com
In reply to: Alena Rybakina (#70)
Re: [PoC] Reducing planning time when tables have many partitions

On Sat, Nov 18, 2023 at 4:04 AM Alena Rybakina <lena.ribackina@yandex.ru> wrote:

All diff files have already been added to v21-0002-Introduce-indexes-for-RestrictInfo patch.

Unfortunately, the patch tester is too smart for its own good, and
will try to apply .diff files as well. Since
bug_related_to_atomic_function.diff is first in the alphabet, it comes
first, which is the reason for the current CI failure.

#72Tom Lane
tgl@sss.pgh.pa.us
In reply to: John Naylor (#71)
Re: [PoC] Reducing planning time when tables have many partitions

John Naylor <johncnaylorls@gmail.com> writes:

On Sat, Nov 18, 2023 at 4:04 AM Alena Rybakina <lena.ribackina@yandex.ru> wrote:

All diff files have already been added to v21-0002-Introduce-indexes-for-RestrictInfo patch.

Unfortunately, the patch tester is too smart for its own good, and
will try to apply .diff files as well.

Yeah --- see documentation here:

https://wiki.postgresql.org/wiki/Cfbot

That suggests using a .txt extension for anything you don't want to
be taken as part of the patch set.

regards, tom lane

#73Alena Rybakina
lena.ribackina@yandex.ru
In reply to: Tom Lane (#72)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On 18.11.2023 05:45, John Naylor wrote:

On Sat, Nov 18, 2023 at 4:04 AM Alena Rybakina <lena.ribackina@yandex.ru> wrote:

All diff files have already been added to v21-0002-Introduce-indexes-for-RestrictInfo patch.

Unfortunately, the patch tester is too smart for its own good, and
will try to apply .diff files as well. Since
bug_related_to_atomic_function.diff is first in the alphabet, it comes
first, which is the reason for the current CI failure.

On 18.11.2023 06:13, Tom Lane wrote:

John Naylor <johncnaylorls@gmail.com> writes:

On Sat, Nov 18, 2023 at 4:04 AM Alena Rybakina <lena.ribackina@yandex.ru> wrote:

All diff files have already been added to v21-0002-Introduce-indexes-for-RestrictInfo patch.

Unfortunately, the patch tester is too smart for its own good, and
will try to apply .diff files as well.

Yeah --- see documentation here:

https://wiki.postgresql.org/wiki/Cfbot

That suggests using a .txt extension for anything you don't want to
be taken as part of the patch set.

regards, tom lane

Thank you for explanation. I fixed it.

I have attached the previous diff files as txt so that they will not
applied (they are already applied in the second patch
"v21-0002-PATCH-PATCH-1-2-Introduce-indexes-for-RestrictInfo-T.patch").
Also, the previous time I missed the fact that the files conflict with
each other - I fixed it too and everything seems to work fine now.

Attachments:

v21-0001-PATCH-PATCH-1-2-Speed-up-searches-for-child-Equivale.patchtext/x-patch; charset=UTF-8; name=v21-0001-PATCH-PATCH-1-2-Speed-up-searches-for-child-Equivale.patchDownload
From a6173dd35d3d467ecd11a0b6b5b3f49d2f7cd24e Mon Sep 17 00:00:00 2001
From: Alena Rybakina <a.rybakina@postgrespro.ru>
Date: Sun, 19 Nov 2023 02:22:15 +0300
Subject: [PATCH 1/2] [PATCH] [PATCH 1/2] Speed up searches for child
 EquivalenceMembers.

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.

This change was picked up from the previous patch, v20.
---
 contrib/postgres_fdw/postgres_fdw.c       |  28 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 414 ++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |  40 ++-
 src/backend/optimizer/path/pathkeys.c     |   9 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +-
 src/backend/optimizer/plan/createplan.c   |  57 +--
 src/backend/optimizer/util/inherit.c      |  14 +
 src/backend/optimizer/util/relnode.c      |  88 +++++
 src/include/nodes/pathnodes.h             |  26 ++
 src/include/optimizer/pathnode.h          |  18 +
 src/include/optimizer/paths.h             |   9 +-
 12 files changed, 603 insertions(+), 116 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6de2bec3b7b..947e0366ea8 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7690,11 +7690,26 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
+	Relids		top_parent_rel_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
 
-	foreach(lc, ec->ec_members)
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_rel_relids))
+			add_child_rel_equivalences_to_list(root, ec, em,
+											   rel->relids,
+											   &members, &modified);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7705,6 +7720,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -7758,9 +7775,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e66a99247e4..8318c710355 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,6 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_norel_members);
+	WRITE_NODE_FIELD(ec_rel_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e25..7873548b257 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,12 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  EquivalenceMember *parent_em,
+										  RelOptInfo *child_rel,
+										  List **list,
+										  bool *modified);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -342,6 +352,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_norel_members = list_concat(ec1->ec_norel_members,
+											ec2->ec_norel_members);
+		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
+										  ec2->ec_rel_members);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -355,6 +369,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_norel_members = NIL;
+		ec2->ec_rel_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -374,7 +390,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +407,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,6 +428,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_norel_members = NIL;
+		ec->ec_rel_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -423,9 +441,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +529,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +547,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,8 +569,34 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/*
+	 * The exact set of relids in the expr for non-child EquivalenceMembers
+	 * as what is given to us in 'relids' should be the same as the relids
+	 * mentioned in the expression.  See add_child_rel_equivalences.
+	 */
+	/* XXX We need PlannerInfo to use the following assertion */
+	/* Assert(bms_equal(pull_varnos(root, (Node *) expr), relids)); */
+	if (bms_is_empty(relids))
+		ec->ec_norel_members = lappend(ec->ec_norel_members, em);
+	else
+		ec->ec_rel_members = lappend(ec->ec_rel_members, em);
+
 	return em;
 }
 
@@ -599,6 +648,17 @@ 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.
@@ -617,7 +677,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * When we have to see child EquivalenceMembers, we get and add them to
+		 * 'members'. We cannot use foreach() because the 'members' may be
+		 * modified during iteration.
+		 */
+		members = cur_ec->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them.
+			 */
+			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+												   &members, &modified);
 
 			/*
 			 * Ignore child members unless they match the request.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +735,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		if (unlikely(modified))
+			list_free(members);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -671,6 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_norel_members = NIL;
+	newec->ec_rel_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -691,7 +776,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +842,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -779,6 +873,12 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -796,6 +896,8 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -828,11 +930,20 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -843,6 +954,12 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -876,6 +993,8 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -939,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1138,7 +1257,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_norel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
@@ -1222,7 +1341,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_rel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
@@ -1559,7 +1678,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1695,19 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_rel_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+			bms_is_subset(cur_em->em_relids, top_parent_join_relids))
+			add_child_rel_equivalences_to_list(root, ec, cur_em, join_relids,
+											   &members, &modified);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1589,6 +1724,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1606,6 +1743,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1680,6 +1818,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -2177,7 +2316,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2285,7 +2424,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_rel_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
 			Assert(!coal_em->em_is_child);	/* no children yet */
@@ -2330,7 +2469,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2385,6 +2524,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
+			/* XXX performance of list_delete_ptr()?? */
+			cur_ec->ec_rel_members = list_delete_ptr(cur_ec->ec_rel_members,
+													 coal_em);
 			return true;
 		}
 
@@ -2455,8 +2598,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2523,13 +2666,13 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		foreach(lc2, ec->ec_members)
+		foreach(lc2, ec->ec_rel_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2626,6 +2769,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2638,7 +2782,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2651,15 +2794,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2672,8 +2809,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2689,6 +2826,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_rel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
@@ -2749,6 +2898,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2770,7 +2920,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2783,15 +2932,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2800,8 +2943,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2816,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2846,9 +2990,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_joinrel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2856,6 +3012,106 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the list.
+ *
+ * This function is expected to be called only from
+ * add_child_rel_equivalences_to_list().
+ */
+static void
+add_transformed_child_version(PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel,
+							  List **list,
+							  bool *modified)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the given list has been modified, we need to
+		 * make a copy of it.
+		 */
+		if (!*modified)
+		{
+			*list = list_copy(*list);
+			*modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		*list = lappend(*list, child_em);
+	}
+}
+
+/*
+ * add_child_rel_equivalences_to_list
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the list.
+ *
+ * The transformation is done in add_transformed_child_version().
+ *
+ * This function will not change the original *list but will always make a copy
+ * of it when we need to add transformed members. *modified will be true if the
+ * *list is replaced with a newly allocated one. In such a case, the caller can
+ * (or must) free the *list.
+ */
+void
+add_child_rel_equivalences_to_list(PlannerInfo *root,
+								   EquivalenceClass *ec,
+								   EquivalenceMember *parent_em,
+								   Relids child_relids,
+								   List **list,
+								   bool *modified)
+{
+	int		i;
+	Relids	matching_relids;
+
+	/* The given EquivalenceMember should be parent */
+	Assert(!parent_em->em_is_child);
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		RelOptInfo *child_rel = root->simple_rel_array[i];
+
+		Assert(child_rel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_rel,
+									  list, modified);
+	}
+	bms_free(matching_relids);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_joinrel,
+									  list, modified);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2889,7 +3145,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids, top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2898,6 +3154,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2910,6 +3169,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,15 +3193,25 @@ 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.)
 		 */
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = cur_ec->ec_rel_members;
+		modified = false;
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		for (j = 0; j < list_length(members); j++)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = list_nth_node(EquivalenceMember, members, j);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel_relids))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel->relids,
+												   &members, &modified);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		if (!cur_em)
 			continue;
@@ -2954,8 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3173,8 +3445,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 03a5fbdc6dc..9fa99e32929 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3088,7 +3092,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3114,33 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = pathkey->pk_eclass->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember, members, i);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+				bms_equal(member->em_relids, top_parent_index_relids))
+				add_child_rel_equivalences_to_list(root, pathkey->pk_eclass, member,
+												   index->rel->relids,
+												   &members, &modified);
+
 			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
+			if (member->em_is_child ||
+				!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
@@ -3145,6 +3169,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index fdb60aaa8d2..cd1be06b5c0 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -952,8 +952,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1479,8 +1479,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b9be19a687f..bb4ad600849 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -578,7 +578,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -663,7 +663,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
 
@@ -687,7 +688,14 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
 			if (bms_is_empty(cur_em->em_relids))
+			{
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				/* XXX performance of list_delete_ptr()?? */
+				ec->ec_norel_members = list_delete_ptr(ec->ec_norel_members,
+													   cur_em);
+				ec->ec_rel_members = list_delete_ptr(ec->ec_rel_members,
+													 cur_em);
+			}
 		}
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 34ca6d4ac21..d67d549b1c6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4498,7 +4502,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4512,7 +4516,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6133,7 +6137,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6200,7 +6204,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6244,7 +6248,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6315,7 +6319,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6324,7 +6329,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6350,8 +6355,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6360,7 +6366,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6717,7 +6723,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6780,7 +6787,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index f9d3ff1e7ac..d826f072c77 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -461,6 +461,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -568,6 +569,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5d83f60eb9a..698ddfd67c1 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -122,11 +122,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -147,6 +150,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -174,11 +198,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids are
+		 * top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -232,6 +268,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -607,6 +644,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -718,6 +761,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -914,6 +958,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1475,6 +1520,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1515,6 +1561,48 @@ 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 == 0)
+			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 ed85dc7414b..bc3e1562fcc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * append_rel_array is the same length as simple_rel_array and holds the
+	 * top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -936,6 +944,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1368,6 +1387,10 @@ 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_norel_members;	/* list of EquivalenceMembers whose
+									 * em_relids is empty */
+	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
+								 * em_relids is not empty */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1422,6 +1445,9 @@ 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);
 } EquivalenceMember;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 6e557bebc44..cae45708234 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -323,6 +323,24 @@ 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) \
+	(likely((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 9e7408c7ecf..5953a0cc26b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -173,6 +174,12 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
 											AppendRelInfo **appinfos,
 											RelOptInfo *parent_joinrel,
 											RelOptInfo *child_joinrel);
+extern void add_child_rel_equivalences_to_list(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   EquivalenceMember *parent_em,
+											   Relids child_relids,
+											   List **list,
+											   bool *modified);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
-- 
2.34.1

v21-0002-PATCH-PATCH-1-2-Introduce-indexes-for-RestrictInfo-T.patchtext/x-patch; charset=UTF-8; name=v21-0002-PATCH-PATCH-1-2-Introduce-indexes-for-RestrictInfo-T.patchDownload
From 394dca18a55bca3fa6fcbc6488eabe6f610f122d Mon Sep 17 00:00:00 2001
From: Alena Rybakina <a.rybakina@postgrespro.ru>
Date: Sun, 19 Nov 2023 02:37:32 +0300
Subject: [PATCH 2/2] [PATCH] [PATCH 1/2] Introduce indexes for RestrictInfo
 This change  was picked up from the previous patch, v20.

---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/read.c                  |  20 ++
 src/backend/nodes/readfuncs.c             |  22 ++
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 322 +++++++++++++++++++---
 src/backend/optimizer/plan/analyzejoins.c |  62 +++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  47 +++-
 src/include/nodes/readfuncs.h             |   2 +
 src/include/optimizer/paths.h             |  18 +-
 13 files changed, 452 insertions(+), 67 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8318c710355..1904a53acbd 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,8 +465,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_members);
 	WRITE_NODE_FIELD(ec_norel_members);
 	WRITE_NODE_FIELD(ec_rel_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -569,6 +569,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 813eda3e739..314736d989b 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
+
+/*
+ * pg_strtok_save_context -
+ *	  Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+	*pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ *	  Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+	pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7b..0915c0e661d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+	int save_length = length; \
+	const char *context; \
+	pg_strtok_save_context(&context); \
+	token = pg_strtok(&length); \
+	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+	{ \
+		/* "fldname" field was not found - fill it and restore context. */ \
+		local_node->fldname = NULL; \
+		pg_strtok_restore_context(context); \
+		length = save_length; \
+	} \
+	else \
+	{ \
+		local_node->fldname = _readBitmapset(); \
+	} \
+}
+
 
 /*
  * _readBitmapset
@@ -574,6 +594,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d6ceafd51c5..fcda83ad2c4 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5755,7 +5755,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7873548b257..9771ab9da1a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,8 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -84,6 +86,49 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
 											Relids relids2);
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 
 /*
  * process_equivalence
@@ -321,7 +366,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -332,6 +376,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -356,8 +402,10 @@ process_equivalence(PlannerInfo *root,
 											ec2->ec_norel_members);
 		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
 										  ec2->ec_rel_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -371,10 +419,9 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_members = NIL;
 		ec2->ec_norel_members = NIL;
 		ec2->ec_rel_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -385,13 +432,13 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -402,13 +449,13 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -419,6 +466,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -430,8 +479,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_members = NIL;
 		ec->ec_norel_members = NIL;
 		ec->ec_rel_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -453,6 +502,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -600,7 +651,6 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
-
 /*
  * get_eclass_for_sort_expr
  *	  Given an expression and opfamily/collation info, find an existing
@@ -756,8 +806,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_members = NIL;
 	newec->ec_norel_members = NIL;
 	newec->ec_rel_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1145,7 +1195,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1234,6 +1284,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1243,9 +1294,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1303,9 +1354,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1315,7 +1366,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1381,7 +1433,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1437,11 +1489,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1482,11 +1535,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1871,12 +1924,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1888,12 +1945,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1955,10 +2012,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1969,9 +2027,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1982,9 +2043,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2042,7 +2107,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2721,16 +2786,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3335,7 +3403,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3423,7 +3491,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3576,3 +3644,167 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index bb4ad600849..a478d76f34d 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -663,10 +663,10 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
-					   int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid, int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -700,9 +700,10 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -712,7 +713,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1536,6 +1537,24 @@ replace_relid(Relids relids, int oldId, int newId)
 	return relids;
 }
 
+void
+del_eq_derives(PlannerInfo *root, EquivalenceClass *ec)
+{
+	int i = -1;
+
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But add a nuke to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
+}
+
+
 /*
  * Update EC members to point to the remaining relation instead of the removed
  * one, removing duplicates.
@@ -1559,10 +1578,11 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclass(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
+	int			i = -1;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1601,16 +1621,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_members = lappend(new_members, em);
 	}
 
-	list_free(ec->ec_members);
-	ec->ec_members = new_members;
-
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	del_eq_derives(root, ec);
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo   *rinfo = list_nth_node(RestrictInfo,
+											   root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
@@ -1644,8 +1661,21 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_sources = lappend(new_sources, rinfo);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	i=-1;
+
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
+	{
+		list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = NULL;
+
+	foreach(lc, new_sources)
+	{
+		RestrictInfo   *rinfo = lfirst_node(RestrictInfo, lc);
+		add_eq_source(root, ec, rinfo);
+	}
+
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
 }
 
@@ -1823,7 +1853,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclass(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe14..773d703c881 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -646,6 +646,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 73ff40721c9..1cce43a94e0 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index d826f072c77..b75e92b2e15 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,6 +483,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e494309da8d..475a65475e6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,6 +1194,12 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index bc3e1562fcc..7c94ece3326 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -318,6 +318,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1369,6 +1375,41 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * TODO: We should update the following comments.
+ *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1391,8 +1432,10 @@ typedef struct EquivalenceClass
 									 * em_relids is empty */
 	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
 								 * em_relids is not empty */
-	List	   *ec_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index cba6f0be75a..2fa68e59cdf 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 5953a0cc26b..f528a6a1bda 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -111,6 +111,9 @@ extern bool have_join_order_restriction(PlannerInfo *root,
 extern bool have_dangerous_phv(PlannerInfo *root,
 							   Relids outer_relids, Relids inner_params);
 extern void mark_dummy_rel(RelOptInfo *rel);
+extern void del_eq_derives(PlannerInfo *root, EquivalenceClass *ec);
+extern void add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo);
+
 
 /*
  * equivclass.c
@@ -163,7 +166,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -195,6 +199,18 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
-- 
2.34.1

bug_related_to_atomic_function.txttext/plain; charset=UTF-8; name=bug_related_to_atomic_function.txtDownload
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 813eda3e739..314736d989b 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
+
+/*
+ * pg_strtok_save_context -
+ *	  Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+	*pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ *	  Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+	pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7b..9a7b4e9be58 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+	int save_length = length; \
+	const char *context; \
+	pg_strtok_save_context(&context); \
+	token = pg_strtok(&length); \
+	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+	{ \
+		/* "fldname" field was not found - fill it and restore context. */ \
+		local_node->fldname = NULL; \
+		pg_strtok_restore_context(context); \
+		length = save_length; \
+	} \
+	else \
+	{ \
+		local_node->fldname = _readBitmapset(); \
+	} \
+}
+
 
 /*
  * _readBitmapset
@@ -574,6 +594,9 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index cba6f0be75a..2fa68e59cdf 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
solved_conflict_with_self_join_removal.txttext/plain; charset=UTF-8; name=solved_conflict_with_self_join_removal.txtDownload
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e59f38b6ce0..329419c38d9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,8 +38,6 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
 										Oid datatype);
-static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
-						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
@@ -78,6 +76,49 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
 											Relids relids2);
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
 
 /*
  * process_equivalence
@@ -561,51 +602,6 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
-/*
- * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
- */
-static void
-add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
-{
-	int			source_idx = list_length(root->eq_sources);
-	int			i;
-
-	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
-	root->eq_sources = lappend(root->eq_sources, rinfo);
-
-	i = -1;
-	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
-	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
-
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
-	}
-}
-
-/*
- * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
- */
-static void
-add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
-{
-	int			derive_idx = list_length(root->eq_derives);
-	int			i;
-
-	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
-	root->eq_derives = lappend(root->eq_derives, rinfo);
-
-	i = -1;
-	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
-	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
-
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
-	}
-}
-
-
 /*
  * get_eclass_for_sort_expr
  *	  Given an expression and opfamily/collation info, find an existing
@@ -3398,18 +3394,6 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
 	}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
 	/* bitwise-AND to leave only the ones for this EquivalenceClass */
 	return bms_int_members(rel_esis, ec->ec_source_indexes);
 }
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 0298c26166f..92dcbbdf2e0 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -578,7 +578,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -663,7 +663,7 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid, int ojrelid)
 {
 	ListCell   *lc;
 	int			i;
@@ -1530,6 +1530,24 @@ replace_relid(Relids relids, int oldId, int newId)
 	return relids;
 }
 
+void
+del_eq_derives(PlannerInfo *root, EquivalenceClass *ec)
+{
+	int i = -1;
+
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But add a nuke to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
+}
+
+
 /*
  * Update EC members to point to the remaining relation instead of the removed
  * one, removing duplicates.
@@ -1553,10 +1571,11 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclass(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
+	int			i = -1;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1595,16 +1614,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_members = lappend(new_members, em);
 	}
 
-	list_free(ec->ec_members);
-	ec->ec_members = new_members;
-
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	del_eq_derives(root, ec);
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo   *rinfo = list_nth_node(RestrictInfo,
+											   root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
@@ -1638,8 +1654,21 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			new_sources = lappend(new_sources, rinfo);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	i=-1;
+
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) > 0)
+	{
+		list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = NULL;
+
+	foreach(lc, new_sources)
+	{
+		RestrictInfo   *rinfo = lfirst_node(RestrictInfo, lc);
+		add_eq_source(root, ec, rinfo);
+	}
+
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
 }
 
@@ -1817,7 +1846,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclass(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 469ca518571..83695e2f66f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -111,6 +111,9 @@ extern bool have_join_order_restriction(PlannerInfo *root,
 extern bool have_dangerous_phv(PlannerInfo *root,
 							   Relids outer_relids, Relids inner_params);
 extern void mark_dummy_rel(RelOptInfo *rel);
+extern void del_eq_derives(PlannerInfo *root, EquivalenceClass *ec);
+extern void add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo);
+
 
 /*
  * equivclass.c
#74Andrei Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Yuya Watari (#69)
Re: [PoC] Reducing planning time when tables have many partitions

On 27/9/2023 14:28, Yuya Watari wrote:

Thank you for pointing it out. The ec_source_indexes and
ec_derive_indexes are just picked up from the previous patch, and I
have not changed their design. I think a similar approach to
EquivalenceMembers may be applied to RestrictInfos. I will experiment
with them and share a new patch.

During the work on committing the SJE feature [1]/messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru, Alexander Korotkov
pointed out the silver lining in this work [2]/messages/by-id/CAPpHfdsnAbg8CaK+NJ8AkiG_+_Tt07eCStkb1LOa50f0UsT5RQ@mail.gmail.com: he proposed that we
shouldn't remove RelOptInfo from simple_rel_array at all but replace it
with an 'Alias', which will refer the kept relation. It can simplify
further optimizations on removing redundant parts of the query.

[1]: /messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru
/messages/by-id/64486b0b-0404-e39e-322d-0801154901f3@postgrespro.ru
[2]: /messages/by-id/CAPpHfdsnAbg8CaK+NJ8AkiG_+_Tt07eCStkb1LOa50f0UsT5RQ@mail.gmail.com
/messages/by-id/CAPpHfdsnAbg8CaK+NJ8AkiG_+_Tt07eCStkb1LOa50f0UsT5RQ@mail.gmail.com

--
regards,
Andrei Lepikhov
Postgres Professional

#75Yuya Watari
watari.yuya@gmail.com
In reply to: Andrei Lepikhov (#74)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Alena, Andrei, and all,

Thank you for reviewing this patch. I really apologize for not
updating this thread for a while.

On Sat, Nov 18, 2023 at 6:04 AM Alena Rybakina <lena.ribackina@yandex.ru> wrote:

Hi, all!

While I was reviewing the patches, I noticed that they needed some rebasing, and in one of the patches (Introduce-indexes-for-RestrictInfo.patch) there was a conflict with the recently added self-join-removal feature [1]. So, I rebased patches and resolved the conflicts. While I was doing this, I found a problem that I also fixed:

Thank you very much for rebasing these patches and fixing the issue.
The bug seemed to be caused because these indexes were in
RangeTblEntry, and the handling of their serialization was not
correct. Thank you for fixing it.

On Mon, Nov 20, 2023 at 1:45 PM Andrei Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

During the work on committing the SJE feature [1], Alexander Korotkov
pointed out the silver lining in this work [2]: he proposed that we
shouldn't remove RelOptInfo from simple_rel_array at all but replace it
with an 'Alias', which will refer the kept relation. It can simplify
further optimizations on removing redundant parts of the query.

Thank you for sharing this information. I think the idea suggested by
Alexander Korotkov is also helpful for our patch. As mentioned above,
the indexes are in RangeTblEntry in the current implementation.
However, I think RangeTblEntry is not the best place to store them. An
'alias' relids may help solve this and simplify fixing the above bug.
I will try this approach soon.

Unfortunately, I've been busy due to work, so I won't be able to
respond for several weeks. I'm really sorry for not being able to see
the patches. As soon as I'm not busy, I will look at them, consider
the above approach, and reply to this thread. If there is no
objection, I will move this CF entry forward to next CF.

Again, thank you very much for looking at this thread, and I'm sorry
for my late work.

--
Best regards,
Yuya Watari

#76Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#75)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Nov 22, 2023 at 2:32 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Unfortunately, I've been busy due to work, so I won't be able to
respond for several weeks. I'm really sorry for not being able to see
the patches. As soon as I'm not busy, I will look at them, consider
the above approach, and reply to this thread. If there is no
objection, I will move this CF entry forward to next CF.

Since the end of this month is approaching, I moved this CF entry to
the next CF (January CF). I will reply to this thread in a few weeks.
Again, I appreciate your kind reviews and patches.

--
Best regards,
Yuya Watari

#77Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#76)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Alena, Andrei, and all,

I am sorry for my late response. I found that the current patches do
not apply to the master, so I have rebased those patches. I have
attached v22. For this later discussion, I separated the rebasing and
bug fixing that Alena did in v21 into separate commits, v22-0003 and
v22-0004. I will merge these commits after the discussion.

1. v22-0003 (solved_conflict_with_self_join_removal.txt)

Thank you for your rebase. Looking at your rebasing patch, I thought
we could do this more simply. Your patch deletes (more precisely, sets
to null) non-redundant members from the root->eq_sources list and
re-adds them to the same list. However, this approach seems a little
waste of memory. Instead, we can update
EquivalenceClass->ec_source_indexes directly. Then, we can reuse the
members in root->eq_sources and don't need to extend root->eq_sources.
I did this in v22-0003. What do you think of this approach?

The main concern with this idea is that it does not fix
RangeTblEntry->eclass_source_indexes. The current code works fine even
if we don't fix the index because get_ec_source_indexes() always does
bms_intersect() for eclass_source_indexes and ec_source_indexes. If we
guaranteed this behavior of doing bms_intersect, then simply modifying
ec_source_indexes would be fine. Fortunately, such a guarantee is not
so difficult.

And your patch removes the following assertion code from the previous
patch. May I ask why you removed this code? I think this assertion is
helpful for sanity checks. Of course, I know that this kind of
assertion will slow down regression tests or assert-enabled builds.
So, we may have to discuss which assertions to keep and which to
discard.

=====
-#ifdef USE_ASSERT_CHECKING
- /* verify the results look sane */
- i = -1;
- while ((i = bms_next_member(rel_esis, i)) >= 0)
- {
- RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
- i);
-
- Assert(bms_overlap(relids, rinfo->clause_relids));
- }
-#endif
=====

Finally, your patch changes the name of the following function. I
understand the need for this change, but it has nothing to do with our
patches, so we should not include it and discuss it in another thread.

=====
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclass(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
=====

2. v22-0004 (bug_related_to_atomic_function.txt)

Thank you for fixing the bug. As I wrote in the previous mail:

On Wed, Nov 22, 2023 at 2:32 PM Yuya Watari <watari.yuya@gmail.com> wrote:

On Mon, Nov 20, 2023 at 1:45 PM Andrei Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

During the work on committing the SJE feature [1], Alexander Korotkov
pointed out the silver lining in this work [2]: he proposed that we
shouldn't remove RelOptInfo from simple_rel_array at all but replace it
with an 'Alias', which will refer the kept relation. It can simplify
further optimizations on removing redundant parts of the query.

Thank you for sharing this information. I think the idea suggested by
Alexander Korotkov is also helpful for our patch. As mentioned above,
the indexes are in RangeTblEntry in the current implementation.
However, I think RangeTblEntry is not the best place to store them. An
'alias' relids may help solve this and simplify fixing the above bug.
I will try this approach soon.

I think that the best way to solve this issue is to move these indexes
from RangeTblEntry to RelOptInfo. Since they are related to planning
time, they should be in RelOptInfo. The reason why I put these indexes
in RangeTblEntry is because some RelOptInfos can be null and we cannot
store the indexes. This problem is similar to an issue regarding
'varno 0' Vars. I hope an alias RelOptInfo would help solve this
issue. I have attached the current proof of concept I am considering
as poc-alias-reloptinfo.txt. To test this patch, please follow the
procedure below.

1. Apply all *.patch files,
2. Apply Alexander Korotkov's alias_relids.patch [1]/messages/by-id/CAPpHfdseB13zJJPZuBORevRnZ0vcFyUaaJeSGfAysX7S5er+EQ@mail.gmail.com, and
3. Apply poc-alias-reloptinfo.txt, which is attached to this email.

My patch creates a dummy (or an alias) RelOptInfo to store indexes if
the corresponding RelOptInfo is null. The following is the core change
in my patch.

=====
@@ -627,9 +627,19 @@ add_eq_source(PlannerInfo *root, EquivalenceClass
*ec, RestrictInfo *rinfo)
    i = -1;
    while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
    {
-       RangeTblEntry *rte = root->simple_rte_array[i];
+       RelOptInfo *rel = root->simple_rel_array[i];
-       rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+       /*
+        * If the corresponding RelOptInfo does not exist, we create a 'dummy'
+        * RelOptInfo for storing EquivalenceClass indexes.
+        */
+       if (rel == NULL)
+       {
+           rel = root->simple_rel_array[i] = makeNode(RelOptInfo);
+           rel->eclass_source_indexes = NULL;
+           rel->eclass_derive_indexes = NULL;
+       }
+       rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
                                                    source_idx);
    }
=====

At this point, I'm not sure if this approach is correct. It seems to
pass the regression tests, but we should doubt its correctness. I will
continue to experiment with this idea.

[1]: /messages/by-id/CAPpHfdseB13zJJPZuBORevRnZ0vcFyUaaJeSGfAysX7S5er+EQ@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

v22-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v22-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From 677c951139abe98e851de523ba28d38a16a02b29 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v22 2/4] Introduce indexes for RestrictInfo

This change was picked up from v19.

Author: David Rowley <dgrowley@gmail.com> and me
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 338 +++++++++++++++++++---
 src/backend/optimizer/plan/analyzejoins.c |   8 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  47 ++-
 src/include/optimizer/paths.h             |  15 +-
 11 files changed, 383 insertions(+), 53 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8318c71035..1904a53acb 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,8 +465,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_members);
 	WRITE_NODE_FIELD(ec_norel_members);
 	WRITE_NODE_FIELD(ec_rel_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -569,6 +569,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7..7610447ad3 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -574,6 +574,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d6ceafd51c..fcda83ad2c 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5755,7 +5755,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7873548b25..813a365e63 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -321,7 +325,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -332,6 +335,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -356,8 +361,10 @@ process_equivalence(PlannerInfo *root,
 											ec2->ec_norel_members);
 		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
 										  ec2->ec_rel_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -371,10 +378,9 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_members = NIL;
 		ec2->ec_norel_members = NIL;
 		ec2->ec_rel_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -385,13 +391,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -402,13 +409,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -419,6 +427,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -430,8 +440,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_members = NIL;
 		ec->ec_norel_members = NIL;
 		ec->ec_rel_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -453,6 +463,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -600,6 +612,50 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -756,8 +812,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_members = NIL;
 	newec->ec_norel_members = NIL;
 	newec->ec_rel_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1145,7 +1201,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1234,6 +1290,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1243,9 +1300,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1303,9 +1360,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1315,7 +1372,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1381,7 +1439,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1437,11 +1495,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1482,11 +1541,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1871,12 +1930,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1888,12 +1951,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1955,10 +2018,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1969,9 +2033,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1982,9 +2049,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2042,7 +2113,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2721,16 +2792,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3335,7 +3409,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3423,7 +3497,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3576,3 +3650,179 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index bb4ad60084..4ea0cfd803 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -667,6 +667,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -700,9 +701,10 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -712,7 +714,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe1..773d703c88 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -646,6 +646,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 73ff40721c..1cce43a94e 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index d826f072c7..b75e92b2e1 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,6 +483,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e494309da8..475a65475e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,6 +1194,12 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index bc3e1562fc..7c94ece332 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -318,6 +318,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1369,6 +1375,41 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * TODO: We should update the following comments.
+ *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1391,8 +1432,10 @@ typedef struct EquivalenceClass
 									 * em_relids is empty */
 	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
 								 * em_relids is not empty */
-	List	   *ec_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 5953a0cc26..97c19d0068 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -163,7 +163,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -195,6 +196,18 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
-- 
2.42.0.windows.2

v22-0003-Solve-conflict-with-self-join-removal.patchapplication/octet-stream; name=v22-0003-Solve-conflict-with-self-join-removal.patchDownload
From bd767c17d3cc4836867c1a2e1dc039b5ce1fad82 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 11 Dec 2023 12:19:55 +0900
Subject: [PATCH v22 3/4] Solve conflict with self join removal

Written by Alena Rybakina <lena.ribackina@yandex.ru> [1] with
modifications by me.

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/optimizer/plan/analyzejoins.c | 49 ++++++++++++++++-------
 1 file changed, 35 insertions(+), 14 deletions(-)

diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 4ea0cfd803..31f0c717a2 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1561,10 +1561,12 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1606,18 +1608,28 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But set a nuke to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1628,9 +1640,10 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach(lc1, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
-			RestrictInfo *other = lfirst_node(RestrictInfo, lc1);
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
 
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
@@ -1642,12 +1655,20 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_sources indexes. But set a nuke to detect potential problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
 }
 
@@ -1825,7 +1846,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
-- 
2.42.0.windows.2

v22-0004-Fix-a-bug-related-to-atomic-function.patchapplication/octet-stream; name=v22-0004-Fix-a-bug-related-to-atomic-function.patchDownload
From e6e38cb16c793efdb17f36078a7ff9fe83610953 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 11 Dec 2023 12:20:20 +0900
Subject: [PATCH v22 4/4] Fix a bug related to atomic function

Author: Alena Rybakina <lena.ribackina@yandex.ru>
https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/read.c      | 20 ++++++++++++++++++++
 src/backend/nodes/readfuncs.c | 24 ++++++++++++++++++++++--
 src/include/nodes/readfuncs.h |  2 ++
 3 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 813eda3e73..314736d989 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
+
+/*
+ * pg_strtok_save_context -
+ *	  Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+	*pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ *	  Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+	pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 7610447ad3..0915c0e661 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+	int save_length = length; \
+	const char *context; \
+	pg_strtok_save_context(&context); \
+	token = pg_strtok(&length); \
+	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+	{ \
+		/* "fldname" field was not found - fill it and restore context. */ \
+		local_node->fldname = NULL; \
+		pg_strtok_restore_context(context); \
+		length = save_length; \
+	} \
+	else \
+	{ \
+		local_node->fldname = _readBitmapset(); \
+	} \
+}
+
 
 /*
  * _readBitmapset
@@ -574,8 +594,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index cba6f0be75..2fa68e59cd 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
-- 
2.42.0.windows.2

poc-alias-reloptinfo.txttext/plain; charset=US-ASCII; name=poc-alias-reloptinfo.txtDownload
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1904a53acb..241be97920 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -569,8 +569,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 314736d989..813eda3e73 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,23 +514,3 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
-
-/*
- * pg_strtok_save_context -
- *	  Save context initialized by stringToNode.
- */
-void
-pg_strtok_save_context(const char **pcontext)
-{
-	*pcontext = pg_strtok_ptr;
-}
-
-/*
- * pg_strtok_restore_context -
- *	  Resore saved context.
- */
-void
-pg_strtok_restore_context(const char *context)
-{
-	pg_strtok_ptr = context;
-}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 0915c0e661..cc2021c1f7 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,26 +191,6 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
-/* Read an equivalence field (anything written as ":fldname %u") and check it */
-#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
-{ \
-	int save_length = length; \
-	const char *context; \
-	pg_strtok_save_context(&context); \
-	token = pg_strtok(&length); \
-	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
-	{ \
-		/* "fldname" field was not found - fill it and restore context. */ \
-		local_node->fldname = NULL; \
-		pg_strtok_restore_context(context); \
-		length = save_length; \
-	} \
-	else \
-	{ \
-		local_node->fldname = _readBitmapset(); \
-	} \
-}
-
 
 /*
  * _readBitmapset
@@ -594,8 +574,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
-	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 813a365e63..7e0a0c45f1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -627,9 +627,19 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		RelOptInfo *rel = root->simple_rel_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+		/*
+		 * If the corresponding RelOptInfo does not exist, we create a 'dummy'
+		 * RelOptInfo for storing EquivalenceClass indexes.
+		 */
+		if (rel == NULL)
+		{
+			rel = root->simple_rel_array[i] = makeNode(RelOptInfo);
+			rel->eclass_source_indexes = NULL;
+			rel->eclass_derive_indexes = NULL;
+		}
+		rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
 													source_idx);
 	}
 }
@@ -649,9 +659,19 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		RelOptInfo *rel = root->simple_rel_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+		/*
+		 * If the corresponding RelOptInfo does not exist, we create a 'dummy'
+		 * RelOptInfo for storing EquivalenceClass indexes.
+		 */
+		if (rel == NULL)
+		{
+			rel = root->simple_rel_array[i] = makeNode(RelOptInfo);
+			rel->eclass_source_indexes = NULL;
+			rel->eclass_derive_indexes = NULL;
+		}
+		rel->eclass_derive_indexes = bms_add_member(rel->eclass_derive_indexes,
 													derive_idx);
 	}
 }
@@ -3667,9 +3687,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		RelOptInfo *rel = root->simple_rel_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, rel->eclass_source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3705,7 +3725,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		RelOptInfo *rel = root->simple_rel_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3714,12 +3734,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 rel->eclass_source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			rel = root->simple_rel_array[i];
+			esis = bms_int_members(esis, rel->eclass_source_indexes);
 		}
 	}
 
@@ -3756,9 +3776,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		RelOptInfo *rel = root->simple_rel_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, rel->eclass_derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3794,7 +3814,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		RelOptInfo *rel = root->simple_rel_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3803,12 +3823,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 rel->eclass_derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			rel = root->simple_rel_array[i];
+			edis = bms_int_members(edis, rel->eclass_derive_indexes);
 		}
 	}
 
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index b75e92b2e1..d826f072c7 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,13 +483,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 698ddfd67c..d3bd24bbb3 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -264,6 +264,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->tuples = 0;
 	rel->allvisfrac = 0;
 	rel->eclass_indexes = NULL;
+	rel->eclass_source_indexes = NULL;
+	rel->eclass_derive_indexes = NULL;
 	rel->subroot = NULL;
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -757,6 +759,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
@@ -955,6 +959,8 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->tuples = 0;
 	joinrel->allvisfrac = 0;
 	joinrel->eclass_indexes = NULL;
+	joinrel->eclass_source_indexes = NULL;
+	joinrel->eclass_derive_indexes = NULL;
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 475a65475e..e494309da8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,12 +1194,6 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 7c94ece332..5571585355 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -943,6 +943,19 @@ typedef struct RelOptInfo
 	double		allvisfrac;
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_sources list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_source_indexes;
+
+	/*
+	 * Indexes in PlannerInfo's eq_derives list for RestrictInfos that mention
+	 * this relation.
+	 */
+	Bitmapset  *eclass_derive_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index 2fa68e59cd..cba6f0be75 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,8 +29,6 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
-extern void pg_strtok_save_context(const char **pcontext);
-extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
v22-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v22-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 03b82c4f3964ead680b60cdc53905ae4cd8c3309 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v22 1/4] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c       |  29 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 414 ++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |  40 ++-
 src/backend/optimizer/path/pathkeys.c     |   9 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +-
 src/backend/optimizer/plan/createplan.c   |  57 +--
 src/backend/optimizer/util/inherit.c      |  14 +
 src/backend/optimizer/util/relnode.c      |  88 +++++
 src/include/nodes/pathnodes.h             |  26 ++
 src/include/optimizer/pathnode.h          |  18 +
 src/include/optimizer/paths.h             |   9 +-
 12 files changed, 603 insertions(+), 117 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e9144beb62..3c3b2cdb34 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7773,13 +7773,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	Relids		top_parent_rel_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_rel_relids))
+			add_child_rel_equivalences_to_list(root, ec, em,
+											   rel->relids,
+											   &members, &modified);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7791,6 +7805,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -7844,9 +7860,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e66a99247e..8318c71035 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,6 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_norel_members);
+	WRITE_NODE_FIELD(ec_rel_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e2..7873548b25 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,12 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  EquivalenceMember *parent_em,
+										  RelOptInfo *child_rel,
+										  List **list,
+										  bool *modified);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -342,6 +352,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_norel_members = list_concat(ec1->ec_norel_members,
+											ec2->ec_norel_members);
+		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
+										  ec2->ec_rel_members);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -355,6 +369,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_norel_members = NIL;
+		ec2->ec_rel_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -374,7 +390,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +407,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,6 +428,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_norel_members = NIL;
+		ec->ec_rel_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -423,9 +441,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +529,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +547,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,8 +569,34 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/*
+	 * The exact set of relids in the expr for non-child EquivalenceMembers
+	 * as what is given to us in 'relids' should be the same as the relids
+	 * mentioned in the expression.  See add_child_rel_equivalences.
+	 */
+	/* XXX We need PlannerInfo to use the following assertion */
+	/* Assert(bms_equal(pull_varnos(root, (Node *) expr), relids)); */
+	if (bms_is_empty(relids))
+		ec->ec_norel_members = lappend(ec->ec_norel_members, em);
+	else
+		ec->ec_rel_members = lappend(ec->ec_rel_members, em);
+
 	return em;
 }
 
@@ -599,6 +648,17 @@ 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.
@@ -617,7 +677,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * When we have to see child EquivalenceMembers, we get and add them to
+		 * 'members'. We cannot use foreach() because the 'members' may be
+		 * modified during iteration.
+		 */
+		members = cur_ec->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them.
+			 */
+			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+												   &members, &modified);
 
 			/*
 			 * Ignore child members unless they match the request.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +735,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		if (unlikely(modified))
+			list_free(members);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -671,6 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_norel_members = NIL;
+	newec->ec_rel_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -691,7 +776,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +842,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -779,6 +873,12 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -796,6 +896,8 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -828,11 +930,20 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -843,6 +954,12 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -876,6 +993,8 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -939,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1138,7 +1257,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_norel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
@@ -1222,7 +1341,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_rel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
@@ -1559,7 +1678,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1695,19 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_rel_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+			bms_is_subset(cur_em->em_relids, top_parent_join_relids))
+			add_child_rel_equivalences_to_list(root, ec, cur_em, join_relids,
+											   &members, &modified);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1589,6 +1724,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1606,6 +1743,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1680,6 +1818,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -2177,7 +2316,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2285,7 +2424,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_rel_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
 			Assert(!coal_em->em_is_child);	/* no children yet */
@@ -2330,7 +2469,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2385,6 +2524,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
+			/* XXX performance of list_delete_ptr()?? */
+			cur_ec->ec_rel_members = list_delete_ptr(cur_ec->ec_rel_members,
+													 coal_em);
 			return true;
 		}
 
@@ -2455,8 +2598,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2523,13 +2666,13 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		foreach(lc2, ec->ec_members)
+		foreach(lc2, ec->ec_rel_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2626,6 +2769,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2638,7 +2782,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2651,15 +2794,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2672,8 +2809,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2689,6 +2826,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_rel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
@@ -2749,6 +2898,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2770,7 +2920,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2783,15 +2932,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2800,8 +2943,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2816,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2846,9 +2990,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_joinrel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2856,6 +3012,106 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the list.
+ *
+ * This function is expected to be called only from
+ * add_child_rel_equivalences_to_list().
+ */
+static void
+add_transformed_child_version(PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel,
+							  List **list,
+							  bool *modified)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the given list has been modified, we need to
+		 * make a copy of it.
+		 */
+		if (!*modified)
+		{
+			*list = list_copy(*list);
+			*modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		*list = lappend(*list, child_em);
+	}
+}
+
+/*
+ * add_child_rel_equivalences_to_list
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the list.
+ *
+ * The transformation is done in add_transformed_child_version().
+ *
+ * This function will not change the original *list but will always make a copy
+ * of it when we need to add transformed members. *modified will be true if the
+ * *list is replaced with a newly allocated one. In such a case, the caller can
+ * (or must) free the *list.
+ */
+void
+add_child_rel_equivalences_to_list(PlannerInfo *root,
+								   EquivalenceClass *ec,
+								   EquivalenceMember *parent_em,
+								   Relids child_relids,
+								   List **list,
+								   bool *modified)
+{
+	int		i;
+	Relids	matching_relids;
+
+	/* The given EquivalenceMember should be parent */
+	Assert(!parent_em->em_is_child);
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		RelOptInfo *child_rel = root->simple_rel_array[i];
+
+		Assert(child_rel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_rel,
+									  list, modified);
+	}
+	bms_free(matching_relids);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_joinrel,
+									  list, modified);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2889,7 +3145,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids, top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2898,6 +3154,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2910,6 +3169,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,15 +3193,25 @@ 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.)
 		 */
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = cur_ec->ec_rel_members;
+		modified = false;
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		for (j = 0; j < list_length(members); j++)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = list_nth_node(EquivalenceMember, members, j);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel_relids))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel->relids,
+												   &members, &modified);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		if (!cur_em)
 			continue;
@@ -2954,8 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3173,8 +3445,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 03a5fbdc6d..9fa99e3292 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3088,7 +3092,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3114,33 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = pathkey->pk_eclass->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember, members, i);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+				bms_equal(member->em_relids, top_parent_index_relids))
+				add_child_rel_equivalences_to_list(root, pathkey->pk_eclass, member,
+												   index->rel->relids,
+												   &members, &modified);
+
 			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
+			if (member->em_is_child ||
+				!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
@@ -3145,6 +3169,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index fdb60aaa8d..cd1be06b5c 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -952,8 +952,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1479,8 +1479,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b9be19a687..bb4ad60084 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -578,7 +578,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -663,7 +663,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
 
@@ -687,7 +688,14 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
 			if (bms_is_empty(cur_em->em_relids))
+			{
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				/* XXX performance of list_delete_ptr()?? */
+				ec->ec_norel_members = list_delete_ptr(ec->ec_norel_members,
+													   cur_em);
+				ec->ec_rel_members = list_delete_ptr(ec->ec_rel_members,
+													 cur_em);
+			}
 		}
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 34ca6d4ac2..d67d549b1c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4498,7 +4502,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4512,7 +4516,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6133,7 +6137,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6200,7 +6204,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6244,7 +6248,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6315,7 +6319,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6324,7 +6329,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6350,8 +6355,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6360,7 +6366,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6717,7 +6723,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6780,7 +6787,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index f9d3ff1e7a..d826f072c7 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -461,6 +461,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -568,6 +569,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5d83f60eb9..698ddfd67c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -122,11 +122,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -147,6 +150,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -174,11 +198,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids are
+		 * top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -232,6 +268,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -607,6 +644,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -718,6 +761,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -914,6 +958,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1475,6 +1520,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1515,6 +1561,48 @@ 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 == 0)
+			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 ed85dc7414..bc3e1562fc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * append_rel_array is the same length as simple_rel_array and holds the
+	 * top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -936,6 +944,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1368,6 +1387,10 @@ 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_norel_members;	/* list of EquivalenceMembers whose
+									 * em_relids is empty */
+	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
+								 * em_relids is not empty */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1422,6 +1445,9 @@ 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);
 } EquivalenceMember;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 6e557bebc4..cae4570823 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -323,6 +323,24 @@ 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) \
+	(likely((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 9e7408c7ec..5953a0cc26 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -173,6 +174,12 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
 											AppendRelInfo **appinfos,
 											RelOptInfo *parent_joinrel,
 											RelOptInfo *child_joinrel);
+extern void add_child_rel_equivalences_to_list(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   EquivalenceMember *parent_em,
+											   Relids child_relids,
+											   List **list,
+											   bool *modified);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
-- 
2.42.0.windows.2

#78Alena Rybakina
lena.ribackina@yandex.ru
In reply to: Yuya Watari (#77)
Re: [PoC] Reducing planning time when tables have many partitions

Hi!
On 13.12.2023 09:21, Yuya Watari wrote:

Hello Alena, Andrei, and all,

I am sorry for my late response. I found that the current patches do
not apply to the master, so I have rebased those patches. I have
attached v22. For this later discussion, I separated the rebasing and
bug fixing that Alena did in v21 into separate commits, v22-0003 and
v22-0004. I will merge these commits after the discussion.

1. v22-0003 (solved_conflict_with_self_join_removal.txt)

Thank you!

Thank you for your rebase. Looking at your rebasing patch, I thought
we could do this more simply. Your patch deletes (more precisely, sets
to null) non-redundant members from the root->eq_sources list and
re-adds them to the same list. However, this approach seems a little
waste of memory. Instead, we can update
EquivalenceClass->ec_source_indexes directly. Then, we can reuse the
members in root->eq_sources and don't need to extend root->eq_sources.
I did this in v22-0003. What do you think of this approach?

I thought about this earlier and was worried that the index links of the
equivalence classes might not be referenced correctly for outer joins,
so I decided to just overwrite them and reset the previous ones.

The main concern with this idea is that it does not fix
RangeTblEntry->eclass_source_indexes. The current code works fine even
if we don't fix the index because get_ec_source_indexes() always does
bms_intersect() for eclass_source_indexes and ec_source_indexes. If we
guaranteed this behavior of doing bms_intersect, then simply modifying
ec_source_indexes would be fine. Fortunately, such a guarantee is not
so difficult.

And your patch removes the following assertion code from the previous
patch. May I ask why you removed this code? I think this assertion is
helpful for sanity checks. Of course, I know that this kind of
assertion will slow down regression tests or assert-enabled builds.
So, we may have to discuss which assertions to keep and which to
discard.

=====
-#ifdef USE_ASSERT_CHECKING
- /* verify the results look sane */
- i = -1;
- while ((i = bms_next_member(rel_esis, i)) >= 0)
- {
- RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
- i);
-
- Assert(bms_overlap(relids, rinfo->clause_relids));
- }
-#endif
=====

this is due to the fact that I explained before: we zeroed the values
indicated by the indexes,
then this check is not correct either - since the zeroed value indicated
by the index is correct.
That's why I removed this check.

Finally, your patch changes the name of the following function. I
understand the need for this change, but it has nothing to do with our
patches, so we should not include it and discuss it in another thread.

=====
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclass(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
=====

I agree.

2. v22-0004 (bug_related_to_atomic_function.txt)

Thank you for fixing the bug. As I wrote in the previous mail:

On Wed, Nov 22, 2023 at 2:32 PM Yuya Watari<watari.yuya@gmail.com> wrote:

On Mon, Nov 20, 2023 at 1:45 PM Andrei Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

During the work on committing the SJE feature [1], Alexander Korotkov
pointed out the silver lining in this work [2]: he proposed that we
shouldn't remove RelOptInfo from simple_rel_array at all but replace it
with an 'Alias', which will refer the kept relation. It can simplify
further optimizations on removing redundant parts of the query.

Thank you for sharing this information. I think the idea suggested by
Alexander Korotkov is also helpful for our patch. As mentioned above,
the indexes are in RangeTblEntry in the current implementation.
However, I think RangeTblEntry is not the best place to store them. An
'alias' relids may help solve this and simplify fixing the above bug.
I will try this approach soon.

I think that the best way to solve this issue is to move these indexes
from RangeTblEntry to RelOptInfo. Since they are related to planning
time, they should be in RelOptInfo. The reason why I put these indexes
in RangeTblEntry is because some RelOptInfos can be null and we cannot
store the indexes. This problem is similar to an issue regarding
'varno 0' Vars. I hope an alias RelOptInfo would help solve this
issue. I have attached the current proof of concept I am considering
as poc-alias-reloptinfo.txt. To test this patch, please follow the
procedure below.

1. Apply all *.patch files,
2. Apply Alexander Korotkov's alias_relids.patch [1], and
3. Apply poc-alias-reloptinfo.txt, which is attached to this email.

My patch creates a dummy (or an alias) RelOptInfo to store indexes if
the corresponding RelOptInfo is null. The following is the core change
in my patch.

=====
@@ -627,9 +627,19 @@ add_eq_source(PlannerInfo *root, EquivalenceClass
*ec, RestrictInfo *rinfo)
i = -1;
while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
{
-       RangeTblEntry *rte = root->simple_rte_array[i];
+       RelOptInfo *rel = root->simple_rel_array[i];
-       rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+       /*
+        * If the corresponding RelOptInfo does not exist, we create a 'dummy'
+        * RelOptInfo for storing EquivalenceClass indexes.
+        */
+       if (rel == NULL)
+       {
+           rel = root->simple_rel_array[i] = makeNode(RelOptInfo);
+           rel->eclass_source_indexes = NULL;
+           rel->eclass_derive_indexes = NULL;
+       }
+       rel->eclass_source_indexes = bms_add_member(rel->eclass_source_indexes,
source_idx);
}
=====

At this point, I'm not sure if this approach is correct. It seems to
pass the regression tests, but we should doubt its correctness. I will
continue to experiment with this idea.

[1]/messages/by-id/CAPpHfdseB13zJJPZuBORevRnZ0vcFyUaaJeSGfAysX7S5er+EQ@mail.gmail.com

Yes, I also thought in this direction before and I agree that this is
the best way to develop the patch.

--
Regards,
Alena Rybakina
Postgres Professional:http://www.postgrespro.com
The Russian Postgres Company

#79Yuya Watari
watari.yuya@gmail.com
In reply to: Alena Rybakina (#78)
7 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Alena,

Thank you for your quick response, and I'm sorry for my delayed reply.

On Sun, Dec 17, 2023 at 12:41 AM Alena Rybakina
<lena.ribackina@yandex.ru> wrote:

I thought about this earlier and was worried that the index links of the equivalence classes might not be referenced correctly for outer joins,
so I decided to just overwrite them and reset the previous ones.

Thank you for pointing this out. I have investigated this problem and
found a potential bug place. The code quoted below modifies
RestrictInfo's clause_relids. Here, our indexes, namely
eclass_source_indexes and eclass_derive_indexes, are based on
clause_relids, so they should be adjusted after the modification.
However, my patch didn't do that, so it may have missed some
references. The same problem occurs in places other than the quoted
one.

=====
/*
* Walker function for replace_varno()
*/
static bool
replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
{
...
else if (IsA(node, RestrictInfo))
{
RestrictInfo *rinfo = (RestrictInfo *) node;
...

if (bms_is_member(ctx->from, rinfo->clause_relids))
{
replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
rinfo->clause_relids = replace_relid(rinfo->clause_relids,
ctx->from, ctx->to);
...
}
...
}
...
}
=====

I have attached a new version of the patch, v23, to fix this problem.
v23-0006 adds a helper function called update_clause_relids(). This
function modifies RestrictInfo->clause_relids while adjusting its
related indexes. I have also attached a sanity check patch
(sanity-check.txt) to this email. This sanity check patch verifies
that there are no missing references between RestrictInfos and our
indexes. I don't intend to commit this patch, but it helps find
potential bugs. v23 passes this sanity check, but the v21 you
submitted before does not. This means that the adjustment by
update_clause_relids() is needed to prevent missing references after
modifying clause_relids. I'd appreciate your letting me know if v23
doesn't solve your concern.

One of the things I don't think is good about my approach is that it
adds some complexity to the code. In my approach, all modifications to
clause_relids must be done through the update_clause_relids()
function, but enforcing this rule is not so easy. In this sense, my
patch may need to be simplified more.

this is due to the fact that I explained before: we zeroed the values indicated by the indexes,
then this check is not correct either - since the zeroed value indicated by the index is correct.
That's why I removed this check.

Thank you for letting me know. I fixed this in v23-0005 to adjust the
indexes in update_eclasses(). With this change, the assertion check
will be correct.

--
Best regards,
Yuya Watari

Attachments:

sanity-check.txttext/plain; charset=US-ASCII; name=sanity-check.txtDownload
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 2da2a6c14e..334ea8d3bb 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1751,6 +1751,48 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 	bms_free(ec->ec_source_indexes);
 	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * Sanity check:
+	 * Verify that there are no missing references between RestrictInfos and
+	 * our indexes, namely eclass_source_indexes and eclass_derive_indexes.
+	 */
+
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+#endif
 }
 
 /*
v23-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v23-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From a95566fa43f01a4589cc300fe1985bc86abab6fd Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v23 1/6] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c       |  29 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 414 ++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |  40 ++-
 src/backend/optimizer/path/pathkeys.c     |   9 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +-
 src/backend/optimizer/plan/createplan.c   |  57 +--
 src/backend/optimizer/util/inherit.c      |  14 +
 src/backend/optimizer/util/relnode.c      |  88 +++++
 src/include/nodes/pathnodes.h             |  26 ++
 src/include/optimizer/pathnode.h          |  18 +
 src/include/optimizer/paths.h             |   9 +-
 12 files changed, 603 insertions(+), 117 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 142dcfc995..dbaefab1f6 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7773,13 +7773,27 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	Relids		top_parent_rel_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_rel_relids))
+			add_child_rel_equivalences_to_list(root, ec, em,
+											   rel->relids,
+											   &members, &modified);
 
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7791,6 +7805,8 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -7844,9 +7860,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 296ba84518..97ab5d3770 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,6 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_FIELD(ec_norel_members);
+	WRITE_NODE_FIELD(ec_rel_members);
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e86dfeaecd..d9eb4610ce 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,12 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  EquivalenceMember *parent_em,
+										  RelOptInfo *child_rel,
+										  List **list,
+										  bool *modified);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -342,6 +352,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
+		ec1->ec_norel_members = list_concat(ec1->ec_norel_members,
+											ec2->ec_norel_members);
+		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
+										  ec2->ec_rel_members);
 		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
 		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
@@ -355,6 +369,8 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
+		ec2->ec_norel_members = NIL;
+		ec2->ec_rel_members = NIL;
 		ec2->ec_sources = NIL;
 		ec2->ec_derives = NIL;
 		ec2->ec_relids = NULL;
@@ -374,7 +390,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +407,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -412,6 +428,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
+		ec->ec_norel_members = NIL;
+		ec->ec_rel_members = NIL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives = NIL;
 		ec->ec_relids = NULL;
@@ -423,9 +441,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +529,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +547,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,8 +569,34 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/*
+	 * The exact set of relids in the expr for non-child EquivalenceMembers
+	 * as what is given to us in 'relids' should be the same as the relids
+	 * mentioned in the expression.  See add_child_rel_equivalences.
+	 */
+	/* XXX We need PlannerInfo to use the following assertion */
+	/* Assert(bms_equal(pull_varnos(root, (Node *) expr), relids)); */
+	if (bms_is_empty(relids))
+		ec->ec_norel_members = lappend(ec->ec_norel_members, em);
+	else
+		ec->ec_rel_members = lappend(ec->ec_rel_members, em);
+
 	return em;
 }
 
@@ -599,6 +648,17 @@ 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.
@@ -617,7 +677,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +694,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * When we have to see child EquivalenceMembers, we get and add them to
+		 * 'members'. We cannot use foreach() because the 'members' may be
+		 * modified during iteration.
+		 */
+		members = cur_ec->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them.
+			 */
+			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel,
+												   &members, &modified);
 
 			/*
 			 * Ignore child members unless they match the request.
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +735,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		if (unlikely(modified))
+			list_free(members);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -671,6 +754,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
+	newec->ec_norel_members = NIL;
+	newec->ec_rel_members = NIL;
 	newec->ec_sources = NIL;
 	newec->ec_derives = NIL;
 	newec->ec_relids = NULL;
@@ -691,7 +776,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +842,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		Expr	   *emexpr;
 
 		/*
@@ -779,6 +873,12 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -796,6 +896,8 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -828,11 +930,20 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *em = list_nth_node(EquivalenceMember, members, i);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -843,6 +954,12 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+			bms_is_subset(em->em_relids, top_parent_relids))
+			add_child_rel_equivalences_to_list(root, ec, em, relids,
+											   &members, &modified);
+
 		/*
 		 * Ignore child members unless they belong to the requested rel.
 		 */
@@ -876,6 +993,8 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	return NULL;
 }
@@ -939,7 +1058,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1138,7 +1257,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_norel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
@@ -1222,7 +1341,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	foreach(lc, ec->ec_rel_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
@@ -1559,7 +1678,13 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	List	   *members;
+	bool		modified;
+	int			i;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1695,19 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	members = ec->ec_rel_members;
+	modified = false;
+	for (i = 0; i < list_length(members); i++)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember, members, i);
+
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+			bms_is_subset(cur_em->em_relids, top_parent_join_relids))
+			add_child_rel_equivalences_to_list(root, ec, cur_em, join_relids,
+											   &members, &modified);
 
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1589,6 +1724,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	if (unlikely(modified))
+		list_free(members);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1606,6 +1743,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1680,6 +1818,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -2177,7 +2316,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2285,7 +2424,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * the COALESCE arguments?
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_rel_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
 			Assert(!coal_em->em_is_child);	/* no children yet */
@@ -2330,7 +2469,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		foreach(lc2, cur_ec->ec_norel_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
@@ -2385,6 +2524,10 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
+			/* XXX performance of list_delete_ptr()?? */
+			cur_ec->ec_rel_members = list_delete_ptr(cur_ec->ec_rel_members,
+													 coal_em);
 			return true;
 		}
 
@@ -2455,8 +2598,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2523,13 +2666,13 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			continue;
 		/* Note: it seems okay to match to "broken" eclasses here */
 
-		foreach(lc2, ec->ec_members)
+		foreach(lc2, ec->ec_rel_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2626,6 +2769,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2638,7 +2782,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2651,15 +2794,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2672,8 +2809,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2689,6 +2826,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2718,9 +2856,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_rel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
@@ -2749,6 +2898,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2770,7 +2920,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2783,15 +2932,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_rel_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2800,8 +2943,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2816,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2846,9 +2990,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_joinrel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2856,6 +3012,106 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the list.
+ *
+ * This function is expected to be called only from
+ * add_child_rel_equivalences_to_list().
+ */
+static void
+add_transformed_child_version(PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel,
+							  List **list,
+							  bool *modified)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the given list has been modified, we need to
+		 * make a copy of it.
+		 */
+		if (!*modified)
+		{
+			*list = list_copy(*list);
+			*modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		*list = lappend(*list, child_em);
+	}
+}
+
+/*
+ * add_child_rel_equivalences_to_list
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the list.
+ *
+ * The transformation is done in add_transformed_child_version().
+ *
+ * This function will not change the original *list but will always make a copy
+ * of it when we need to add transformed members. *modified will be true if the
+ * *list is replaced with a newly allocated one. In such a case, the caller can
+ * (or must) free the *list.
+ */
+void
+add_child_rel_equivalences_to_list(PlannerInfo *root,
+								   EquivalenceClass *ec,
+								   EquivalenceMember *parent_em,
+								   Relids child_relids,
+								   List **list,
+								   bool *modified)
+{
+	int		i;
+	Relids	matching_relids;
+
+	/* The given EquivalenceMember should be parent */
+	Assert(!parent_em->em_is_child);
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		RelOptInfo *child_rel = root->simple_rel_array[i];
+
+		Assert(child_rel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_rel,
+									  list, modified);
+	}
+	bms_free(matching_relids);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(root, ec, parent_em, child_joinrel,
+									  list, modified);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2889,7 +3145,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids, top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2898,6 +3154,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2910,6 +3169,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2931,15 +3193,25 @@ 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.)
 		 */
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = cur_ec->ec_rel_members;
+		modified = false;
 		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		for (j = 0; j < list_length(members); j++)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			cur_em = list_nth_node(EquivalenceMember, members, j);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+				bms_equal(cur_em->em_relids, top_parent_rel_relids))
+				add_child_rel_equivalences_to_list(root, cur_ec, cur_em, rel->relids,
+												   &members, &modified);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		if (!cur_em)
 			continue;
@@ -2954,8 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3173,8 +3445,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 32c6a8bbdc..1f7438a619 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3088,7 +3092,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		List	   *members;
+		bool		modified;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3114,33 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		members = pathkey->pk_eclass->ec_members;
+		modified = false;
+		for (i = 0; i < list_length(members); i++)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember, members, i);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+				bms_equal(member->em_relids, top_parent_index_relids))
+				add_child_rel_equivalences_to_list(root, pathkey->pk_eclass, member,
+												   index->rel->relids,
+												   &members, &modified);
+
 			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
+			if (member->em_is_child ||
+				!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
@@ -3145,6 +3169,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		if (unlikely(modified))
+			list_free(members);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index ca94a31f71..aac64c4124 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -952,8 +952,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1479,8 +1479,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 7dcb74572a..f0735f3c41 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -61,7 +61,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -580,7 +580,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -665,7 +665,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
 
@@ -689,7 +690,14 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
 			if (bms_is_empty(cur_em->em_relids))
+			{
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				/* XXX performance of list_delete_ptr()?? */
+				ec->ec_norel_members = list_delete_ptr(ec->ec_norel_members,
+													   cur_em);
+				ec->ec_rel_members = list_delete_ptr(ec->ec_rel_members,
+													 cur_em);
+			}
 		}
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ca619eab94..dce42ada81 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4498,7 +4502,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4512,7 +4516,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6133,7 +6137,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6200,7 +6204,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6244,7 +6248,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6315,7 +6319,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6324,7 +6329,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6350,8 +6355,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6360,7 +6366,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6717,7 +6723,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6780,7 +6787,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 5c7acf8a90..3501fbbeed 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -461,6 +461,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -568,6 +569,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 22d01cef5b..b8185d0dda 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,11 +199,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids are
+		 * top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -233,6 +269,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -621,6 +658,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -732,6 +775,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +972,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1492,6 +1537,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1532,6 +1578,48 @@ 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 == 0)
+			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 b9713ec9aa..8c55a2bcf0 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * append_rel_array is the same length as simple_rel_array and holds the
+	 * top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -936,6 +944,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1368,6 +1387,10 @@ 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_norel_members;	/* list of EquivalenceMembers whose
+									 * em_relids is empty */
+	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
+								 * em_relids is not empty */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1422,6 +1445,9 @@ 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);
 } EquivalenceMember;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index c43d97b48a..e61f2e3529 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -324,6 +324,24 @@ 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) \
+	(likely((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 efd4abc28f..9bc0c36b93 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -173,6 +174,12 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
 											AppendRelInfo **appinfos,
 											RelOptInfo *parent_joinrel,
 											RelOptInfo *child_joinrel);
+extern void add_child_rel_equivalences_to_list(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   EquivalenceMember *parent_em,
+											   Relids child_relids,
+											   List **list,
+											   bool *modified);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
-- 
2.42.0.windows.2

v23-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v23-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From f4f0c7ade55cc5f0d3fc2bc62934710b8e12fa8d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v23 2/6] Introduce indexes for RestrictInfo

This change was picked up from v19.

Author: David Rowley <dgrowley@gmail.com> and me
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 338 +++++++++++++++++++---
 src/backend/optimizer/plan/analyzejoins.c |   8 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  47 ++-
 src/include/optimizer/paths.h             |  15 +-
 11 files changed, 383 insertions(+), 53 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 97ab5d3770..98f824ac53 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,8 +465,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_members);
 	WRITE_NODE_FIELD(ec_norel_members);
 	WRITE_NODE_FIELD(ec_rel_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -569,6 +569,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1624b34581..deebfaf994 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -574,6 +574,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 8b76e98529..ae64b169f8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5784,7 +5784,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index d9eb4610ce..d1019315ea 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -321,7 +325,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -332,6 +335,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -356,8 +361,10 @@ process_equivalence(PlannerInfo *root,
 											ec2->ec_norel_members);
 		ec1->ec_rel_members = list_concat(ec1->ec_rel_members,
 										  ec2->ec_rel_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -371,10 +378,9 @@ process_equivalence(PlannerInfo *root,
 		ec2->ec_members = NIL;
 		ec2->ec_norel_members = NIL;
 		ec2->ec_rel_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -385,13 +391,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -402,13 +409,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -419,6 +427,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -430,8 +440,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_members = NIL;
 		ec->ec_norel_members = NIL;
 		ec->ec_rel_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -453,6 +463,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -600,6 +612,50 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -756,8 +812,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_members = NIL;
 	newec->ec_norel_members = NIL;
 	newec->ec_rel_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1145,7 +1201,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1234,6 +1290,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1243,9 +1300,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1303,9 +1360,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1315,7 +1372,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1381,7 +1439,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1437,11 +1495,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1482,11 +1541,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1871,12 +1930,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1888,12 +1951,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1955,10 +2018,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1969,9 +2033,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1982,9 +2049,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2042,7 +2113,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2721,16 +2792,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3335,7 +3409,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3423,7 +3497,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3576,3 +3650,179 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index f0735f3c41..38328d945a 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -669,6 +669,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -702,9 +703,10 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -714,7 +716,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 667723b675..9420259e4b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -646,6 +646,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aa83dd3636..9fc0b69580 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3501fbbeed..a65a76ace9 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,6 +483,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b3181f34ae..d365bb5c72 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,6 +1194,12 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 8c55a2bcf0..1c5d7bc0c1 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -318,6 +318,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1369,6 +1375,41 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * TODO: We should update the following comments.
+ *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1391,8 +1432,10 @@ typedef struct EquivalenceClass
 									 * em_relids is empty */
 	List	   *ec_rel_members;	/* list of EquivalenceMembers whose
 								 * em_relids is not empty */
-	List	   *ec_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9bc0c36b93..d071f6d0fc 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -163,7 +163,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -195,6 +196,18 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
-- 
2.42.0.windows.2

v23-0003-Solve-conflict-with-self-join-removal.patchapplication/octet-stream; name=v23-0003-Solve-conflict-with-self-join-removal.patchDownload
From c7525241e4d3c1adac3328420a61ada5aa799107 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 11 Dec 2023 12:19:55 +0900
Subject: [PATCH v23 3/6] Solve conflict with self join removal

Written by Alena Rybakina <lena.ribackina@yandex.ru> [1] with
modifications by me.

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/optimizer/plan/analyzejoins.c | 49 ++++++++++++++++-------
 1 file changed, 35 insertions(+), 14 deletions(-)

diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 38328d945a..49f3c17057 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1589,10 +1589,12 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1634,18 +1636,28 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But set a nuke to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1656,9 +1668,10 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach(lc1, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
-			RestrictInfo *other = lfirst_node(RestrictInfo, lc1);
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
 
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
@@ -1670,12 +1683,20 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_sources indexes. But set a nuke to detect potential problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
 }
 
@@ -1855,7 +1876,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
-- 
2.42.0.windows.2

v23-0004-Fix-a-bug-related-to-atomic-function.patchapplication/octet-stream; name=v23-0004-Fix-a-bug-related-to-atomic-function.patchDownload
From 914045526469d8010becaa4e3ebebff23167681a Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Mon, 11 Dec 2023 12:20:20 +0900
Subject: [PATCH v23 4/6] Fix a bug related to atomic function

Author: Alena Rybakina <lena.ribackina@yandex.ru>
https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/read.c      | 20 ++++++++++++++++++++
 src/backend/nodes/readfuncs.c | 24 ++++++++++++++++++++++--
 src/include/nodes/readfuncs.h |  2 ++
 3 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 969d0ec199..39c5a39fa7 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -514,3 +514,23 @@ nodeRead(const char *token, int tok_len)
 
 	return (void *) result;
 }
+
+/*
+ * pg_strtok_save_context -
+ *	  Save context initialized by stringToNode.
+ */
+void
+pg_strtok_save_context(const char **pcontext)
+{
+	*pcontext = pg_strtok_ptr;
+}
+
+/*
+ * pg_strtok_restore_context -
+ *	  Resore saved context.
+ */
+void
+pg_strtok_restore_context(const char *context)
+{
+	pg_strtok_ptr = context;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index deebfaf994..0b713838dd 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -191,6 +191,26 @@ nullable_string(const char *token, int length)
 	return debackslash(token, length);
 }
 
+/* Read an equivalence field (anything written as ":fldname %u") and check it */
+#define READ_EQ_BITMAPSET_FIELD_CHECK(fldname) \
+{ \
+	int save_length = length; \
+	const char *context; \
+	pg_strtok_save_context(&context); \
+	token = pg_strtok(&length); \
+	if (length > 0 && strncmp(token, ":"#fldname, strlen(":"#fldname))) \
+	{ \
+		/* "fldname" field was not found - fill it and restore context. */ \
+		local_node->fldname = NULL; \
+		pg_strtok_restore_context(context); \
+		length = save_length; \
+	} \
+	else \
+	{ \
+		local_node->fldname = _readBitmapset(); \
+	} \
+}
+
 
 /*
  * _readBitmapset
@@ -574,8 +594,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_source_indexes);
+	READ_EQ_BITMAPSET_FIELD_CHECK(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index 8466038ed0..0dc2ed5112 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -29,6 +29,8 @@ extern PGDLLIMPORT bool restore_location_fields;
 extern const char *pg_strtok(int *length);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
+extern void pg_strtok_save_context(const char **pcontext);
+extern void pg_strtok_restore_context(const char *context);
 
 /*
  * prototypes for functions in readfuncs.c
-- 
2.42.0.windows.2

v23-0005-Remove-all-references-between-RestrictInfo-and-i.patchapplication/octet-stream; name=v23-0005-Remove-all-references-between-RestrictInfo-and-i.patchDownload
From 0c61c44e693e12ca918bdd24ab266423b0758395 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 11 Jan 2024 16:18:36 +0900
Subject: [PATCH v23 5/6] Remove all references between RestrictInfo and its
 relating RangeTblEntry

This commit adds code to adjust eclass_derive_indexes and
eclass_source_indexes during self-join elimination.

Note that this commit fails some regression tests. The failures will be
fixed in the next commit. See its commit message for more details.
---
 src/backend/optimizer/plan/analyzejoins.c | 34 +++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 49f3c17057..4081827e3a 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1639,9 +1639,25 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 	i = -1;
 	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * RangeTblEntry.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			RangeTblEntry *rte = root->simple_rte_array[j];
+
+			Assert(bms_is_member(i, rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes, i);
+		}
+
 		/*
 		 * Can't delete the element because we would need to rebuild all
-		 * the eq_derives indexes. But set a nuke to detect potential problems.
+		 * the eq_derives indexes. But set a null to detect potential problems.
 		 */
 		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
 	}
@@ -1685,9 +1701,23 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 
 		if (is_redundant)
 		{
+			/*
+			 * Remove all references between this RestrictInfo and its relating
+			 * RangeTblEntry.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				RangeTblEntry *rte = root->simple_rte_array[j];
+
+				Assert(bms_is_member(i, rte->eclass_source_indexes));
+				rte->eclass_source_indexes =
+					bms_del_member(rte->eclass_source_indexes, i);
+			}
+
 			/*
 			 * Can't delete the element because we would need to rebuild all
-			 * the eq_sources indexes. But set a nuke to detect potential problems.
+			 * the eq_sources indexes. But set a null to detect potential problems.
 			 */
 			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
 		}
-- 
2.42.0.windows.2

v23-0006-Introduce-update_clause_relids-for-updating-Rest.patchapplication/octet-stream; name=v23-0006-Introduce-update_clause_relids-for-updating-Rest.patchDownload
From 6bcce92df6dc597e050b7161e5555f3bfd7da841 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Thu, 11 Jan 2024 16:18:38 +0900
Subject: [PATCH v23 6/6] Introduce update_clause_relids() for updating
 RestrictInfo->clause_relids

Originally, the update for RestrictInfo->clause_relids was done by
directly setting the field. However, this failed to update the
corresponding indexes, such as eclass_source_indexes and
eclass_derive_indexes.

This commit provides a helper function to update the field with fixing
the indexes.
---
 src/backend/optimizer/path/equivclass.c   | 120 ++++++++++++++++++++++
 src/backend/optimizer/plan/analyzejoins.c |  81 +++++++++------
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/pathnodes.h             |  11 +-
 src/include/optimizer/paths.h             |   2 +
 6 files changed, 191 insertions(+), 30 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index d1019315ea..3f4df1a8c7 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -623,6 +623,8 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 
 	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
 	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
 
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
@@ -645,6 +647,8 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 
 	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
 	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
 
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
@@ -656,6 +660,122 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	}
 }
 
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+		}
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 4081827e3a..2da2a6c14e 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -39,6 +39,7 @@
  */
 typedef struct
 {
+	PlannerInfo *root;
 	int			from;
 	int			to;
 } ReplaceVarnoContext;
@@ -59,7 +60,8 @@ static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 
 static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
 static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
@@ -76,7 +78,7 @@ static bool is_innerrel_unique_for(PlannerInfo *root,
 								   List *restrictlist,
 								   List **extra_clauses);
 static Bitmapset *replace_relid(Relids relids, int oldId, int newId);
-static void replace_varno(Node *node, int from, int to);
+static void replace_varno(PlannerInfo *root, Node *node, int from, int to);
 static bool replace_varno_walker(Node *node, ReplaceVarnoContext *ctx);
 static int	self_join_candidates_cmp(const void *a, const void *b);
 
@@ -395,7 +397,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 		}
 
 		/* Update lateral references. */
-		replace_varno((Node *) otherrel->lateral_vars, relid, subst);
+		replace_varno(root, (Node *) otherrel->lateral_vars, relid, subst);
 	}
 
 	/*
@@ -432,7 +434,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 		sjinf->commute_below_l = replace_relid(sjinf->commute_below_l, ojrelid, subst);
 		sjinf->commute_below_r = replace_relid(sjinf->commute_below_r, ojrelid, subst);
 
-		replace_varno((Node *) sjinf->semi_rhs_exprs, relid, subst);
+		replace_varno(root, (Node *) sjinf->semi_rhs_exprs, relid, subst);
 	}
 
 	/*
@@ -477,7 +479,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 			phv->phrels = replace_relid(phv->phrels, relid, subst);
 			phv->phrels = replace_relid(phv->phrels, ojrelid, subst);
 			Assert(!bms_is_empty(phv->phrels));
-			replace_varno((Node *) phv->phexpr, relid, subst);
+			replace_varno(root, (Node *) phv->phexpr, relid, subst);
 			Assert(phv->phnullingrels == NULL); /* no need to adjust */
 		}
 	}
@@ -551,7 +553,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -608,16 +610,24 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
+	/*
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of them.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
 	/*
-	 * The clause_relids probably aren't shared with anything else, but let's
+	 * The required_relids probably aren't shared with anything else, but let's
 	 * copy them just to be sure.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -642,14 +652,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -708,7 +718,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	{
 		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -1448,13 +1458,14 @@ is_innerrel_unique_for(PlannerInfo *root,
  * Replace each occurrence of removing relid with the keeping one
  */
 static void
-replace_varno(Node *node, int from, int to)
+replace_varno(PlannerInfo *root, Node *node, int from, int to)
 {
 	ReplaceVarnoContext ctx;
 
 	if (to <= 0)
 		return;
 
+	ctx.root = root;
 	ctx.from = from;
 	ctx.to = to;
 	replace_varno_walker(node, &ctx);
@@ -1498,9 +1509,9 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
 
 		if (bms_is_member(ctx->from, rinfo->clause_relids))
 		{
-			replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
-			replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
-			rinfo->clause_relids = replace_relid(rinfo->clause_relids, ctx->from, ctx->to);
+			replace_varno(ctx->root, (Node *) rinfo->clause, ctx->from, ctx->to);
+			replace_varno(ctx->root, (Node *) rinfo->orclause, ctx->from, ctx->to);
+			update_clause_relids(ctx->root, rinfo, replace_relid(rinfo->clause_relids, ctx->from, ctx->to));
 			rinfo->left_relids = replace_relid(rinfo->left_relids, ctx->from, ctx->to);
 			rinfo->right_relids = replace_relid(rinfo->right_relids, ctx->from, ctx->to);
 		}
@@ -1613,7 +1624,7 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 		em->em_jdomain->jd_relids = replace_relid(em->em_jdomain->jd_relids, from, to);
 
 		/* We only process inner joins */
-		replace_varno((Node *) em->em_expr, from, to);
+		replace_varno(root, (Node *) em->em_expr, from, to);
 
 		foreach(lc1, new_members)
 		{
@@ -1660,6 +1671,12 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 		 * the eq_derives indexes. But set a null to detect potential problems.
 		 */
 		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
 	}
 	bms_free(ec->ec_derive_indexes);
 	ec->ec_derive_indexes = NULL;
@@ -1677,7 +1694,7 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 			continue;
 		}
 
-		replace_varno((Node *) rinfo, from, to);
+		replace_varno(root, (Node *) rinfo, from, to);
 
 		/*
 		 * After switching the clause to the remaining relation, check it for
@@ -1720,6 +1737,12 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 			 * the eq_sources indexes. But set a null to detect potential problems.
 			 */
 			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources, we
+			 * must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
 		}
 		else
 			new_source_indexes = bms_add_member(new_source_indexes, i);
@@ -1782,7 +1805,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	int			i;
 	List	   *jinfo_candidates = NIL;
 	List	   *binfo_candidates = NIL;
-	ReplaceVarnoContext ctx = {.from = toRemove->relid,.to = toKeep->relid};
+	ReplaceVarnoContext ctx = {.root = root,.from = toRemove->relid,.to = toKeep->relid};
 
 	Assert(toKeep->relid != -1);
 
@@ -1798,7 +1821,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
 		remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
-		replace_varno((Node *) rinfo, toRemove->relid, toKeep->relid);
+		replace_varno(root, (Node *) rinfo, toRemove->relid, toKeep->relid);
 
 		if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
 			jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1818,7 +1841,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
-		replace_varno((Node *) rinfo, toRemove->relid, toKeep->relid);
+		replace_varno(root, (Node *) rinfo, toRemove->relid, toKeep->relid);
 
 		if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
 			jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1918,7 +1941,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		Node	   *node = lfirst(lc);
 
-		replace_varno(node, toRemove->relid, toKeep->relid);
+		replace_varno(root, node, toRemove->relid, toKeep->relid);
 		if (!list_member(toKeep->reltarget->exprs, node))
 			toKeep->reltarget->exprs = lappend(toKeep->reltarget->exprs, node);
 	}
@@ -1970,9 +1993,9 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
 
 	/* At last, replace varno in root targetlist and HAVING clause */
-	replace_varno((Node *) root->processed_tlist,
+	replace_varno(root, (Node *) root->processed_tlist,
 				  toRemove->relid, toKeep->relid);
-	replace_varno((Node *) root->processed_groupClause,
+	replace_varno(root, (Node *) root->processed_groupClause,
 				  toRemove->relid, toKeep->relid);
 	replace_relid(root->all_result_relids, toRemove->relid, toKeep->relid);
 	replace_relid(root->leaf_result_relids, toRemove->relid, toKeep->relid);
@@ -2049,7 +2072,7 @@ split_selfjoin_quals(PlannerInfo *root, List *joinquals, List **selfjoinquals,
 		 * when we have cast of the same var to different (but compatible)
 		 * types.
 		 */
-		replace_varno(rightexpr, bms_singleton_member(rinfo->right_relids),
+		replace_varno(root, rightexpr, bms_singleton_member(rinfo->right_relids),
 					  bms_singleton_member(rinfo->left_relids));
 
 		if (equal(leftexpr, rightexpr))
@@ -2090,7 +2113,7 @@ match_unique_clauses(PlannerInfo *root, RelOptInfo *outer, List *uclauses,
 			   bms_is_empty(rinfo->right_relids));
 
 		clause = (Expr *) copyObject(rinfo->clause);
-		replace_varno((Node *) clause, relid, outer->relid);
+		replace_varno(root, (Node *) clause, relid, outer->relid);
 
 		iclause = bms_is_empty(rinfo->left_relids) ? get_rightop(clause) :
 			get_leftop(clause);
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 51fdeace7d..27e4b093d7 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -491,6 +491,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0b406e9334..59ba503ecb 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -247,6 +247,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -403,6 +406,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1c5d7bc0c1..b856722533 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2622,7 +2622,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2737,6 +2742,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d071f6d0fc..c23b177f65 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -128,6 +128,8 @@ extern bool process_equivalence(PlannerInfo *root,
 extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
-- 
2.42.0.windows.2

#80Alena Rybakina
lena.ribackina@yandex.ru
In reply to: Yuya Watari (#79)
Re: [PoC] Reducing planning time when tables have many partitions

Hi! Sorry my delayed reply too.

On 17.01.2024 12:33, Yuya Watari wrote:

Hello Alena,

Thank you for your quick response, and I'm sorry for my delayed reply.

On Sun, Dec 17, 2023 at 12:41 AM Alena Rybakina
<lena.ribackina@yandex.ru> wrote:

I thought about this earlier and was worried that the index links of the equivalence classes might not be referenced correctly for outer joins,
so I decided to just overwrite them and reset the previous ones.

Thank you for pointing this out. I have investigated this problem and
found a potential bug place. The code quoted below modifies
RestrictInfo's clause_relids. Here, our indexes, namely
eclass_source_indexes and eclass_derive_indexes, are based on
clause_relids, so they should be adjusted after the modification.
However, my patch didn't do that, so it may have missed some
references. The same problem occurs in places other than the quoted
one.

=====
/*
* Walker function for replace_varno()
*/
static bool
replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
{
...
else if (IsA(node, RestrictInfo))
{
RestrictInfo *rinfo = (RestrictInfo *) node;
...

if (bms_is_member(ctx->from, rinfo->clause_relids))
{
replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
rinfo->clause_relids = replace_relid(rinfo->clause_relids,
ctx->from, ctx->to);
...
}
...
}
...
}
=====

I have attached a new version of the patch, v23, to fix this problem.
v23-0006 adds a helper function called update_clause_relids(). This
function modifies RestrictInfo->clause_relids while adjusting its
related indexes. I have also attached a sanity check patch
(sanity-check.txt) to this email. This sanity check patch verifies
that there are no missing references between RestrictInfos and our
indexes. I don't intend to commit this patch, but it helps find
potential bugs. v23 passes this sanity check, but the v21 you
submitted before does not. This means that the adjustment by
update_clause_relids() is needed to prevent missing references after
modifying clause_relids. I'd appreciate your letting me know if v23
doesn't solve your concern.

One of the things I don't think is good about my approach is that it
adds some complexity to the code. In my approach, all modifications to
clause_relids must be done through the update_clause_relids()
function, but enforcing this rule is not so easy. In this sense, my
patch may need to be simplified more.

this is due to the fact that I explained before: we zeroed the values indicated by the indexes,
then this check is not correct either - since the zeroed value indicated by the index is correct.
That's why I removed this check.

Thank you for letting me know. I fixed this in v23-0005 to adjust the
indexes in update_eclasses(). With this change, the assertion check
will be correct.

Yes, it is working correctly now with the assertion check. I suppose
it's better to add this code with an additional comment and a
recommendation for other developers
to use it for checking in case of manipulations with the list of
equivalences.

--
Regards,
Alena Rybakina
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#81Yuya Watari
watari.yuya@gmail.com
In reply to: Alena Rybakina (#80)
3 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Tue, Feb 13, 2024 at 6:19 AM Alena Rybakina <lena.ribackina@yandex.ru> wrote:

Yes, it is working correctly now with the assertion check. I suppose
it's better to add this code with an additional comment and a
recommendation for other developers
to use it for checking in case of manipulations with the list of
equivalences.

Thank you for your reply and advice. I have added this assertion so
that other developers can use it in the future.

I also merged recent changes and attached a new version, v24. Since
this thread is getting long, I will summarize the patches.

1. v24-0001

This patch is one of the main parts of my optimization. Traditionally,
EquivalenceClass has both parent and child members. However, this
leads to high iteration costs when there are many child partitions. In
v24-0001, EquivalenceClasses no longer have child members. If we need
to iterate over child EquivalenceMembers, we use the
EquivalenceChildMemberIterator and access the children through the
iterator. For more details, see [1]/messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com (please note that there are a few
design changes from [1]/messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com).

2. v24-0002

This patch was made in the previous work with David. Like
EquivalenceClass, there are many RestrictInfos in highly partitioned
cases. This patch introduces an indexing mechanism to speed up
searches for RestrictInfos.

3. v24-0003

v24-0002 adds its indexes to RangeTblEntry, but this is not a good
idea. RelOptInfo is the best place. This problem is a workaround
because some RelOptInfos can be NULL, so we cannot store indexes to
such RelOptInfos. v24-0003 moves the indexes from RangeTblEntry to
PlannerInfo. This is still a workaround, and I think it should be
reconsidered.

[1]: /messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

v24-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v24-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From daad7514b257868dc48f156ca1244974660c13dc Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v24 1/3] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c       |  22 +-
 src/backend/optimizer/path/equivclass.c   | 389 ++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c     |  35 +-
 src/backend/optimizer/path/pathkeys.c     |   9 +-
 src/backend/optimizer/plan/analyzejoins.c |   7 +-
 src/backend/optimizer/plan/createplan.c   |  57 ++--
 src/backend/optimizer/util/inherit.c      |  14 +
 src/backend/optimizer/util/relnode.c      |  88 +++++
 src/include/nodes/pathnodes.h             |  55 +++
 src/include/optimizer/pathnode.h          |  18 +
 src/include/optimizer/paths.h             |  12 +-
 src/tools/pgindent/typedefs.list          |   1 +
 12 files changed, 592 insertions(+), 115 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 142dcfc995..36fc39733d 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7773,13 +7773,21 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	Relids		top_parent_rel_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -7791,6 +7799,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7844,9 +7853,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 4bd60a09c6..1931b01cbc 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -374,7 +383,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +400,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -423,9 +432,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +520,14 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. Note that child
+ * EquivalenceMembers should not be added to its parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +538,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,11 +560,24 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -599,6 +626,17 @@ 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.
@@ -617,7 +655,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator	it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +671,30 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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 (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				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(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -665,6 +718,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				return cur_ec;
 			}
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -702,7 +756,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -768,19 +822,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -790,6 +850,11 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -807,6 +872,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -839,11 +905,17 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -854,6 +926,11 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -887,6 +964,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -950,7 +1028,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1570,7 +1648,12 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1581,9 +1664,15 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -1600,6 +1689,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1617,6 +1707,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1691,6 +1782,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1698,7 +1790,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2396,6 +2488,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2466,8 +2559,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2539,8 +2632,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2637,6 +2730,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2649,7 +2743,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2662,15 +2755,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2683,8 +2770,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2700,6 +2787,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2729,9 +2817,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_rel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
@@ -2760,6 +2859,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2781,7 +2881,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2794,15 +2893,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2811,8 +2904,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2827,6 +2920,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2857,9 +2951,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * We save the knowledge that 'child_em' can be translated from
+				 * 'child_joinrel'. This knowledge is useful for
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2867,6 +2973,143 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified,
+		 * we need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * iterate_child_rel_equivalences
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the iterator.
+ *
+ * The transformation is done in add_transformed_child_version().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	int		i;
+	Relids	matching_relids;
+
+	/* The given EquivalenceMember should be parent */
+	Assert(!parent_em->em_is_child);
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		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);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2900,7 +3143,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids, top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2909,6 +3152,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2921,6 +3167,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -2942,15 +3189,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -2965,8 +3216,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3184,8 +3435,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 32c6a8bbdc..b06c7cb389 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -184,7 +184,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -980,7 +980,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3071,12 +3071,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3088,7 +3092,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3108,15 +3113,30 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (member->em_is_child ||
+				!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
@@ -3145,6 +3165,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index da6f457a3b..c32be3f8d2 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1168,8 +1168,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1695,8 +1695,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 4978758f8e..4e72741997 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -52,7 +52,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -570,7 +570,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -655,7 +655,8 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 610f4a56d6..59250f98f1 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,7 +260,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -269,9 +271,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -293,7 +297,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1280,7 +1284,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1324,7 +1328,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1465,7 +1469,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1496,7 +1500,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1975,7 +1979,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2194,7 +2198,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2218,7 +2222,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2287,7 +2291,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4507,7 +4511,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4521,7 +4525,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6142,7 +6146,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6209,7 +6213,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6237,7 +6241,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6253,7 +6257,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6324,7 +6328,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6333,7 +6338,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6359,8 +6364,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6369,7 +6375,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6726,7 +6732,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6789,7 +6796,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 5c7acf8a90..3501fbbeed 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -461,6 +461,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -568,6 +569,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index e5f4062bfb..9973326e90 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,11 +199,23 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids are
+		 * top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -234,6 +270,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -622,6 +659,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -734,6 +777,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -931,6 +975,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1495,6 +1540,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1535,6 +1581,48 @@ 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 == 0)
+			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 534692bee1..c359505f6a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -245,6 +245,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * append_rel_array is the same length as simple_rel_array and holds the
+	 * top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -938,6 +946,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1424,10 +1443,46 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;		/* current index within 'ec_members'. */
+	bool		modified;	/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;	/* parent and child members*/
+} EquivalenceChildMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index c43d97b48a..e61f2e3529 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -324,6 +324,24 @@ 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) \
+	(likely((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 0e8a9c94ba..5f6a08821e 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -174,6 +175,15 @@ extern void add_child_join_rel_equivalences(PlannerInfo *root,
 											AppendRelInfo **appinfos,
 											RelOptInfo *parent_joinrel,
 											RelOptInfo *child_joinrel);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index fc8b15d0cf..8f86dd864e 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -661,6 +661,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
 EquivalenceMember
 ErrorContextCallback
-- 
2.42.0.windows.2

v24-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v24-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From c2f94809f72a8c89b67326a000e5bbb6db52ffbf Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v24 2/3] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 519 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c | 175 ++++++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/optimizer/paths.h             |  21 +-
 13 files changed, 693 insertions(+), 98 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2c30bba212..c61110c200 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -463,8 +463,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -567,6 +567,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index b1e2f2b440..e3518c602b 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -431,6 +431,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 8b76e98529..ae64b169f8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5784,7 +5784,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 1931b01cbc..17fffc4087 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -320,7 +324,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -331,6 +334,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -351,8 +356,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -364,10 +371,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -378,13 +384,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -395,13 +402,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -412,6 +420,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -421,8 +431,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -444,6 +454,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -578,6 +590,170 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+		}
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -736,8 +912,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1115,7 +1291,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1204,6 +1380,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1213,9 +1390,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1273,9 +1450,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1285,7 +1462,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1351,7 +1529,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1407,11 +1585,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1452,11 +1631,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1835,12 +2014,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1852,12 +2035,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1919,10 +2102,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1933,9 +2117,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1946,9 +2133,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2006,7 +2197,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2682,16 +2873,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3325,7 +3519,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3413,7 +3607,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3566,3 +3760,240 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely eclass_source_indexes and
+ *		eclass_derive_indexes. If you modify these indexes, you should check
+ *		them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * eclass_source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * eclass_derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 4e72741997..13e987493b 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -50,7 +50,8 @@ static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 
 static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
 static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
@@ -66,7 +67,7 @@ static bool is_innerrel_unique_for(PlannerInfo *root,
 								   JoinType jointype,
 								   List *restrictlist,
 								   List **extra_clauses);
-static void replace_varno(Node *node, int from, int to);
+static void replace_varno(PlannerInfo *root, Node *node, int from, int to);
 static Bitmapset *replace_relid(Relids relids, int oldId, int newId);
 static int	self_join_candidates_cmp(const void *a, const void *b);
 
@@ -385,7 +386,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 		}
 
 		/* Update lateral_vars list. */
-		replace_varno((Node *) otherrel->lateral_vars, relid, subst);
+		replace_varno(root, (Node *) otherrel->lateral_vars, relid, subst);
 	}
 
 	/*
@@ -422,7 +423,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 		sjinf->commute_below_l = replace_relid(sjinf->commute_below_l, ojrelid, subst);
 		sjinf->commute_below_r = replace_relid(sjinf->commute_below_r, ojrelid, subst);
 
-		replace_varno((Node *) sjinf->semi_rhs_exprs, relid, subst);
+		replace_varno(root, (Node *) sjinf->semi_rhs_exprs, relid, subst);
 	}
 
 	/*
@@ -467,7 +468,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 			phv->phrels = replace_relid(phv->phrels, relid, subst);
 			phv->phrels = replace_relid(phv->phrels, ojrelid, subst);
 			Assert(!bms_is_empty(phv->phrels));
-			replace_varno((Node *) phv->phexpr, relid, subst);
+			replace_varno(root, (Node *) phv->phexpr, relid, subst);
 			Assert(phv->phnullingrels == NULL); /* no need to adjust */
 		}
 	}
@@ -541,7 +542,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -598,16 +599,24 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
+	/*
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of them.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
 	/*
-	 * The clause_relids probably aren't shared with anything else, but let's
+	 * The required_relids probably aren't shared with anything else, but let's
 	 * copy them just to be sure.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -632,14 +641,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -659,6 +668,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -685,11 +695,12 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -697,7 +708,7 @@ remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1436,6 +1447,7 @@ is_innerrel_unique_for(PlannerInfo *root,
 
 typedef struct
 {
+	PlannerInfo	   *root;
 	int			from;
 	int			to;
 	int			sublevels_up;
@@ -1494,10 +1506,10 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
 
 		if (bms_is_member(ctx->from, rinfo->clause_relids))
 		{
-			replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
-			replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
-			rinfo->clause_relids =
-				replace_relid(rinfo->clause_relids, ctx->from, ctx->to);
+			replace_varno(ctx->root, (Node *) rinfo->clause, ctx->from, ctx->to);
+			replace_varno(ctx->root, (Node *) rinfo->orclause, ctx->from, ctx->to);
+			update_clause_relids(ctx->root, rinfo,
+				replace_relid(rinfo->clause_relids, ctx->from, ctx->to));
 			rinfo->left_relids =
 				replace_relid(rinfo->left_relids, ctx->from, ctx->to);
 			rinfo->right_relids =
@@ -1547,13 +1559,14 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
 }
 
 static void
-replace_varno(Node *node, int from, int to)
+replace_varno(PlannerInfo *root, Node *node, int from, int to)
 {
 	ReplaceVarnoContext ctx;
 
 	if (to <= 0)
 		return;
 
+	ctx.root = root;
 	ctx.from = from;
 	ctx.to = to;
 	ctx.sublevels_up = 0;
@@ -1614,10 +1627,12 @@ replace_relid(Relids relids, int oldId, int newId)
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 	ListCell   *lc;
 	ListCell   *lc1;
 
@@ -1636,7 +1651,7 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		em->em_jdomain->jd_relids = replace_relid(em->em_jdomain->jd_relids, from, to);
 
 		/* We only process inner joins */
-		replace_varno((Node *) em->em_expr, from, to);
+		replace_varno(root, (Node *) em->em_expr, from, to);
 
 		foreach(lc1, new_members)
 		{
@@ -1659,31 +1674,64 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * RangeTblEntry.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			RangeTblEntry *rte = root->simple_rte_array[j];
+
+			Assert(bms_is_member(i, rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes, i);
+		}
+
+		/*
+		 * Can't delete the element because we would need to rebuild all
+		 * the eq_derives indexes. But set a null to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
-		replace_varno((Node *) rinfo, from, to);
+		replace_varno(root, (Node *) rinfo, from, to);
 
 		/*
 		 * After switching the clause to the remaining relation, check it for
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach(lc1, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
-			RestrictInfo *other = lfirst_node(RestrictInfo, lc1);
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
 
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
@@ -1695,13 +1743,46 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Remove all references between this RestrictInfo and its relating
+			 * RangeTblEntry.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				RangeTblEntry *rte = root->simple_rte_array[j];
+
+				Assert(bms_is_member(i, rte->eclass_source_indexes));
+				rte->eclass_source_indexes =
+					bms_del_member(rte->eclass_source_indexes, i);
+			}
+
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_sources indexes. But set a null to detect potential problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources, we
+			 * must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = replace_relid(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/* Make sure that we didn't break EquivalenceClass indexes */
+	verify_eclass_indexes(root, ec);
+#endif
 }
 
 /*
@@ -1771,7 +1852,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
 		remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
-		replace_varno((Node *) rinfo, toRemove->relid, toKeep->relid);
+		replace_varno(root, (Node *) rinfo, toRemove->relid, toKeep->relid);
 
 		if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
 			jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1791,7 +1872,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
-		replace_varno((Node *) rinfo, toRemove->relid, toKeep->relid);
+		replace_varno(root, (Node *) rinfo, toRemove->relid, toKeep->relid);
 
 		if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
 			jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1879,7 +1960,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
@@ -1891,7 +1972,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		Node	   *node = lfirst(lc);
 
-		replace_varno(node, toRemove->relid, toKeep->relid);
+		replace_varno(root, node, toRemove->relid, toKeep->relid);
 		if (!list_member(toKeep->reltarget->exprs, node))
 			toKeep->reltarget->exprs = lappend(toKeep->reltarget->exprs, node);
 	}
@@ -1932,7 +2013,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	}
 
 	/* Replace varno in all the query structures */
-	replace_varno((Node *) root->parse, toRemove->relid, toKeep->relid);
+	replace_varno(root, (Node *) root->parse, toRemove->relid, toKeep->relid);
 
 	/* See remove_self_joins_one_group() */
 	Assert(root->parse->resultRelation != toRemove->relid);
@@ -1942,9 +2023,9 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
 
 	/* At last, replace varno in root targetlist and HAVING clause */
-	replace_varno((Node *) root->processed_tlist,
+	replace_varno(root, (Node *) root->processed_tlist,
 				  toRemove->relid, toKeep->relid);
-	replace_varno((Node *) root->processed_groupClause,
+	replace_varno(root, (Node *) root->processed_groupClause,
 				  toRemove->relid, toKeep->relid);
 	replace_relid(root->all_result_relids, toRemove->relid, toKeep->relid);
 	replace_relid(root->leaf_result_relids, toRemove->relid, toKeep->relid);
@@ -2021,7 +2102,7 @@ split_selfjoin_quals(PlannerInfo *root, List *joinquals, List **selfjoinquals,
 		 * when we have cast of the same var to different (but compatible)
 		 * types.
 		 */
-		replace_varno(rightexpr, bms_singleton_member(rinfo->right_relids),
+		replace_varno(root, rightexpr, bms_singleton_member(rinfo->right_relids),
 					  bms_singleton_member(rinfo->left_relids));
 
 		if (equal(leftexpr, rightexpr))
@@ -2062,7 +2143,7 @@ match_unique_clauses(PlannerInfo *root, RelOptInfo *outer, List *uclauses,
 			   bms_is_empty(rinfo->right_relids));
 
 		clause = (Expr *) copyObject(rinfo->clause);
-		replace_varno((Node *) clause, relid, outer->relid);
+		replace_varno(root, (Node *) clause, relid, outer->relid);
 
 		iclause = bms_is_empty(rinfo->left_relids) ? get_rightop(clause) :
 			get_leftop(clause);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index be4e182869..1436cdd6e3 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -648,6 +648,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aa83dd3636..9fc0b69580 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -993,6 +993,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 51fdeace7d..27e4b093d7 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -491,6 +491,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3501fbbeed..a65a76ace9 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,6 +483,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0b406e9334..59ba503ecb 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -247,6 +247,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -403,6 +406,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index baa6a97c7e..f624f41d99 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,6 +1194,12 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c359505f6a..81253d0351 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -318,6 +318,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1371,6 +1377,24 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1389,8 +1413,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -2620,7 +2646,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2738,6 +2769,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 5f6a08821e..6bb740266b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern bool process_equivalence(PlannerInfo *root,
 extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -164,7 +166,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -199,6 +202,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.42.0.windows.2

v24-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchapplication/octet-stream; name=v24-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchDownload
From 79d246c59b1b6bc077c44805c7e1d7d1c190828a Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 19 Jan 2024 11:43:29 +0900
Subject: [PATCH v24 3/3] Move EquivalenceClass indexes to PlannerInfo

In the previous commit, the indexes, namely ec_[source|derive]_indexes,
were in RestrictInfo. This was a workaround because RelOptInfo was the
best place but some RelOptInfos can be NULL and we cannot store indexes
for them.

This commit introduces a new struct, EquivalenceClassIndexes. This
struct exists in PlannerInfo and holds our indexes. This change prevents
a serialization problem with RestirctInfo in the previous commit.
---
 src/backend/nodes/outfuncs.c              |  2 -
 src/backend/nodes/readfuncs.c             |  2 -
 src/backend/optimizer/path/equivclass.c   | 83 +++++++++++------------
 src/backend/optimizer/plan/analyzejoins.c | 16 ++---
 src/backend/optimizer/util/inherit.c      |  7 --
 src/backend/optimizer/util/relnode.c      |  6 ++
 src/include/nodes/parsenodes.h            |  6 --
 src/include/nodes/pathnodes.h             | 24 +++++++
 src/tools/pgindent/typedefs.list          |  1 +
 9 files changed, 80 insertions(+), 67 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c61110c200..b08a4789c4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -567,8 +567,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(inh);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index e3518c602b..b1e2f2b440 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -431,8 +431,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(inh);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 17fffc4087..4fb1ecff03 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -607,10 +607,10 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
 	}
 }
 
@@ -631,10 +631,10 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
 	}
 }
 
@@ -673,22 +673,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(removing_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_del_member(rte->eclass_source_indexes,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -702,22 +702,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(adding_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_sources_index,
-								  rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_add_member(rte->eclass_source_indexes,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_derives_index,
-								  rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_add_member(rte->eclass_derive_indexes,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -731,17 +731,17 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(common_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
+								 index->source_indexes));
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
+								 index->derive_indexes));
 		}
 	}
 	bms_free(common_relids);
@@ -3777,9 +3777,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3815,7 +3815,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3824,12 +3824,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 index->source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
 		}
 	}
 
@@ -3866,9 +3866,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3904,7 +3904,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3913,12 +3913,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 index->derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
 		}
 	}
 
@@ -3941,9 +3941,8 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 /*
  * verify_eclass_indexes
  *		Verify that there are no missing references between RestrictInfos and
- *		EquivalenceMember's indexes, namely eclass_source_indexes and
- *		eclass_derive_indexes. If you modify these indexes, you should check
- *		them with this function.
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
  */
 void
 verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
@@ -3952,7 +3951,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 
 	/*
 	 * All RestrictInfos in root->eq_sources must have references to
-	 * eclass_source_indexes.
+	 * source_indexes.
 	 */
 	foreach(lc, root->eq_sources)
 	{
@@ -3969,13 +3968,13 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
 		}
 	}
 
 	/*
 	 * All RestrictInfos in root->eq_derives must have references to
-	 * eclass_derive_indexes.
+	 * derive_indexes.
 	 */
 	foreach(lc, root->eq_derives)
 	{
@@ -3992,7 +3991,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
 		}
 	}
 }
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 13e987493b..d2fdc2649a 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1686,11 +1686,11 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 		j = -1;
 		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
 		{
-			RangeTblEntry *rte = root->simple_rte_array[j];
+			EquivalenceClassIndexes *index = &root->eclass_indexes_array[j];
 
-			Assert(bms_is_member(i, rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes, i);
+			Assert(bms_is_member(i, index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes, i);
 		}
 
 		/*
@@ -1752,11 +1752,11 @@ update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 			j = -1;
 			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
 			{
-				RangeTblEntry *rte = root->simple_rte_array[j];
+				EquivalenceClassIndexes *index = &root->eclass_indexes_array[j];
 
-				Assert(bms_is_member(i, rte->eclass_source_indexes));
-				rte->eclass_source_indexes =
-					bms_del_member(rte->eclass_source_indexes, i);
+				Assert(bms_is_member(i, index->source_indexes));
+				index->source_indexes =
+					bms_del_member(index->source_indexes, i);
 			}
 
 			/*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index a65a76ace9..3501fbbeed 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -483,13 +483,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 9973326e90..bc9195c623 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -217,6 +220,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->top_parent_relid_array = NULL;
 	}
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f624f41d99..baa6a97c7e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1194,12 +1194,6 @@ typedef struct RangeTblEntry
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause? */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 81253d0351..49f53fd51e 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -189,6 +189,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -245,6 +247,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * append_rel_array is the same length as simple_rel_array and holds the
 	 * top-level parent indexes of the corresponding rels within
@@ -1509,6 +1518,21 @@ typedef struct
 	List	   *ec_members;	/* parent and child members*/
 } EquivalenceChildMemberIterator;
 
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
+ * struct exists for each relation and holds the corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *source_indexes;	/* Indexes in PlannerInfo's eq_sources list for
+								   RestrictInfos that mention this relation */
+	Bitmapset  *derive_indexes;	/* Indexes in PlannerInfo's eq_derives list for
+								   RestrictInfos that mention this relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8f86dd864e..565dce4040 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -663,6 +663,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.42.0.windows.2

#82Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Yuya Watari (#81)
Re: [PoC] Reducing planning time when tables have many partitions

Hi Yuya

On Wed, Feb 28, 2024 at 4:48 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Hello,

On Tue, Feb 13, 2024 at 6:19 AM Alena Rybakina <lena.ribackina@yandex.ru>
wrote:

Yes, it is working correctly now with the assertion check. I suppose
it's better to add this code with an additional comment and a
recommendation for other developers
to use it for checking in case of manipulations with the list of
equivalences.

Thank you for your reply and advice. I have added this assertion so
that other developers can use it in the future.

I also merged recent changes and attached a new version, v24. Since
this thread is getting long, I will summarize the patches.

I repeated my experiments in [1]/messages/by-id/CAExHW5uVZ3E5RT9cXHaxQ_DEK7tasaMN=D6rPHcao5gcXanY5w@mail.gmail.com. I ran 2, 3, 4, 5-way self-joins on a
partitioned table with 1000 partitions.

Planning time measurement
---------------------------------------
Without patch with an assert enabled build and enable_partitionwise_join =
false, those joins took 435.31 ms, 1629.16 ms, 4701.59 ms and 11976.69 ms
respectively.
Keeping other things the same, with the patch, they took 247.33 ms, 1318.57
ms, 6960.31 ms and 28463.24 ms respectively.
Those with enable_partitionwise_join = true are 488.73 ms, 2102.12 ms,
6906.02 ms and 21300.77 ms respectively without the patch.
And with the patch, 277.22 ms, 1542.48 ms, 7879.35 ms, and 31826.39 ms.

Without patch without assert enabled build and enable_partitionwise_join =
false, the joins take 298.43 ms, 1179.15 ms, 3518.84 ms and 9149.76 ms
respectively.
Keeping other things the same, with the patch, the joins take 65.70 ms,
131.29 ms, 247.67 ms and 477.74 ms respectively.
Those with enable_partitionwise_join = true are 348.48 ms, 1576.11 ms,
5417.98 and 17433.65 ms respectively without the patch.
And with the patch 95.15 ms, 333.99 ms, 1084.06 ms, and 3609.42 ms.

Memory usage measurement
---------------------------------------
Without patch, with an assert enabled build and enable_partitionwise_join =
false, memory used is 19 MB, 45 MB, 83 MB and 149 MB respectively.
Keeping other things the same, with the patch, memory used is 23 MB, 66 MB,
159 MB and 353 MB respectively.
That with enable_partitionwise_join = true is 40 MB, 151 MB, 464 MB and
1663 MB respectively.
And with the patch it is 44 MB, 172 MB, 540 MB and 1868 MB respectively.

Without patch without assert enabled build and enable_partitionwise_join =
false, memory used is 17 MB, 41 MB, 77 MB, and 140 MB resp.
Keeping other things the same with the patch memory used is 21 MB, 62 MB,
152 MB and 341 MB resp.
That with enable_partitionwise_join = true is 37 MB, 138 MB, 428 MB and
1495 MB resp.
And with the patch it is 42 MB, 160 MB, 496 MB and 1705 MB resp.

here's summary of observations
1. The patch improves planning time significantly (3X to 20X) and the
improvement increases with the number of tables being joined.
2. In the assert enabled build the patch slows down (in comparison to HEAD)
planning with higher number of tables in the join. You may want to
investigate this. But this is still better than my earlier measurements.
3. The patch increased memory consumption by planner. But the numbers have
improved since my last measurement. Still it will be good to investigate
what causes this extra memory consumption.
4. Generally with the assert enabled build planner consumes more memory
with or without patch. From my previous experience this might be due to
Bitmapset objects created within Assert() calls.

Does v24-0002 have any relation/overlap with my patches to reduce memory
consumed by RestrictInfos? Those patches have code to avoid creating
duplicate RestrictInfos (including commuted RestrictInfos) from ECs. [2]/messages/by-id/CAExHW5tEvzM=+LpN=yhU+P33D+=7x6fhzwDwNRM971UJunRTkQ@mail.gmail.com

[1]: /messages/by-id/CAExHW5uVZ3E5RT9cXHaxQ_DEK7tasaMN=D6rPHcao5gcXanY5w@mail.gmail.com
/messages/by-id/CAExHW5uVZ3E5RT9cXHaxQ_DEK7tasaMN=D6rPHcao5gcXanY5w@mail.gmail.com
[2]: /messages/by-id/CAExHW5tEvzM=+LpN=yhU+P33D+=7x6fhzwDwNRM971UJunRTkQ@mail.gmail.com
/messages/by-id/CAExHW5tEvzM=+LpN=yhU+P33D+=7x6fhzwDwNRM971UJunRTkQ@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

#83Yuya Watari
watari.yuya@gmail.com
In reply to: Ashutosh Bapat (#82)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Ashutosh,

Thank you for your email and for reviewing the patch. I sincerely
apologize for the delay in responding.

On Wed, Mar 6, 2024 at 11:16 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

here's summary of observations
1. The patch improves planning time significantly (3X to 20X) and the improvement increases with the number of tables being joined.
2. In the assert enabled build the patch slows down (in comparison to HEAD) planning with higher number of tables in the join. You may want to investigate this. But this is still better than my earlier measurements.
3. The patch increased memory consumption by planner. But the numbers have improved since my last measurement. Still it will be good to investigate what causes this extra memory consumption.
4. Generally with the assert enabled build planner consumes more memory with or without patch. From my previous experience this might be due to Bitmapset objects created within Assert() calls.

Thank you for testing the patch and sharing the results. For comment
#1, these results show the effectiveness of the patch.

For comment #2, I agree that we should not slow down assert-enabled
builds. The patch adds a lot of assertions to avoid adding bugs, but
they might be too excessive. I will reconsider these assertions and
remove unnecessary ones.

For comments #3 and #4, while the patch improves time complexity, it
has some negative impacts on space complexity. The patch uses a
Bitmapset-based index to speed up searching for EquivalenceMembers and
RestrictInfos. Reducing this memory consumption is a little hard, but
this is a very important problem in committing this patch, so I will
investigate this further.

Does v24-0002 have any relation/overlap with my patches to reduce memory consumed by RestrictInfos? Those patches have code to avoid creating duplicate RestrictInfos (including commuted RestrictInfos) from ECs. [2]

Thank you for sharing these patches. My patch may be related to your
patches. My patch speeds up slow linear searches over
EquivalenceMembers and RestrictInfos. It uses several approaches, one
of which is the Bitmapset-based index. Bitmapsets are space
inefficient, so if there are many EquivalenceMembers and
RestrictInfos, this index becomes large. This is true for highly
partitioned cases, where there are a lot of similar (or duplicate)
elements. Eliminating such duplicate elements may help my patch reduce
memory consumption. I will investigate this further.

Unfortunately, I've been busy due to work, so I may not be able to
respond soon. I really apologize for this. However, I will look into
the patches, including yours, and share further information if found.

Again, I apologize for my late response and appreciate your kind review.

--
Best regards,
Yuya Watari

#84jian he
jian.universality@gmail.com
In reply to: Yuya Watari (#83)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, May 2, 2024 at 3:57 PM Yuya Watari <watari.yuya@gmail.com> wrote:

hi. sorry to bother you, maybe a dumb question.

trying to understand something under the hood.
currently I only applied
v24-0001-Speed-up-searches-for-child-EquivalenceMembers.patch.

on v24-0001:
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+  JoinDomain *jdomain, Oid datatype)
+{
+ EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+   NULL, datatype);
+
+ ec->ec_members = lappend(ec->ec_members, em);
+ return em;
+}
+
this part seems so weird to me.
add_eq_member function was added very very long ago,
why do we create a function with the same function name?

also I didn't see deletion of original add_eq_member function
(https://git.postgresql.org/cgit/postgresql.git/tree/src/backend/optimizer/path/equivclass.c#n516)
in v24-0001.

Obviously, now I cannot compile it correctly.
What am I missing?

#85Yuya Watari
watari.yuya@gmail.com
In reply to: jian he (#84)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

Thank you for reviewing these patches.

On Thu, May 2, 2024 at 11:35 PM jian he <jian.universality@gmail.com> wrote:

on v24-0001:
+/*
+ * add_eq_member - build a new EquivalenceMember and add it to an EC
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+  JoinDomain *jdomain, Oid datatype)
+{
+ EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+   NULL, datatype);
+
+ ec->ec_members = lappend(ec->ec_members, em);
+ return em;
+}
+
this part seems so weird to me.
add_eq_member function was added very very long ago,
why do we create a function with the same function name?

also I didn't see deletion of original add_eq_member function
(https://git.postgresql.org/cgit/postgresql.git/tree/src/backend/optimizer/path/equivclass.c#n516)
in v24-0001.

Actually, this patch does not recreate the add_eq_member() function
but splits it into two functions: add_eq_member() and
make_eq_member().

The reason why planning takes so long time in the current
implementation is that EquivalenceClasses have a large number of child
EquivalenceMembers, and the linear search for them is time-consuming.
To solve this problem, the patch makes EquivalenceClasses have only
parent members. There are few parent members, so we can speed up the
search. In the patch, the child members are introduced when needed.

The add_eq_member() function originally created EquivalenceMembers and
added them to ec_members. In the patch, this function is split into
the following two functions.

1. make_eq_member
Creates a new (parent or child) EquivalenceMember and returns it
without adding it to ec_members.
2. add_eq_member
Creates a new parent (not child) EquivalenceMember and adds it to
ec_members. Internally calls make_eq_member.

When we create parent members, we simply call add_eq_member(). This is
the same as the current implementation. When we create child members,
we have to do something different. Look at the code below. The
add_child_rel_equivalences() function creates child members. The patch
creates child EquivalenceMembers by the make_eq_member() function and
stores them in RelOptInfo (child_rel->eclass_child_members) instead of
their parent EquivalenceClass->ec_members. When we need child
EquivalenceMembers, we get them via RelOptInfos.

=====
void
add_child_rel_equivalences(PlannerInfo *root,
AppendRelInfo *appinfo,
RelOptInfo *parent_rel,
RelOptInfo *child_rel)
{
...
i = -1;
while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0)
{
...
foreach(lc, cur_ec->ec_members)
{
...
if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
!bms_is_empty(cur_em->em_relids))
{
/* OK, generate transformed child version */
...
child_em = make_eq_member(cur_ec, child_expr, new_relids,
cur_em->em_jdomain,
cur_em, cur_em->em_datatype);
child_rel->eclass_child_members =
lappend(child_rel->eclass_child_members,
child_em);
...
}
}
}
}
=====

I didn't change the name of add_eq_member, but it might be better to
change it to something like add_parent_eq_member(). Alternatively,
creating a new function named add_child_eq_member() that adds child
members to RelOptInfo can be a solution. I will consider these changes
in the next version.

Obviously, now I cannot compile it correctly.
What am I missing?

Thank you for pointing this out. This is due to a conflict with a
recent commit [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=66c0185a3d14bbbf51d0fc9d267093ffec735231. This commit introduces a new function named
add_setop_child_rel_equivalences(), which is quoted below. This
function creates a new child EquivalenceMember by calling
add_eq_member(). We have to adjust this function to make my patch
work, but it is not so easy. I'm sorry it will take some time to solve
this conflict, but I will post a new version when it is fixed.

=====
/*
* add_setop_child_rel_equivalences
* Add equivalence members for each non-resjunk target in 'child_tlist'
* to the EquivalenceClass in the corresponding setop_pathkey's pk_eclass.
*
* 'root' is the PlannerInfo belonging to the top-level set operation.
* 'child_rel' is the RelOptInfo of the child relation we're adding
* EquivalenceMembers for.
* 'child_tlist' is the target list for the setop child relation. The target
* list expressions are what we add as EquivalenceMembers.
* 'setop_pathkeys' is a list of PathKeys which must contain an entry for each
* non-resjunk target in 'child_tlist'.
*/
void
add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
List *child_tlist, List *setop_pathkeys)
{
ListCell *lc;
ListCell *lc2 = list_head(setop_pathkeys);

foreach(lc, child_tlist)
{
TargetEntry *tle = lfirst_node(TargetEntry, lc);
EquivalenceMember *parent_em;
PathKey *pk;

if (tle->resjunk)
continue;

if (lc2 == NULL)
elog(ERROR, "too few pathkeys for set operation");

pk = lfirst_node(PathKey, lc2);
parent_em = linitial(pk->pk_eclass->ec_members);

/*
* We can safely pass the parent member as the first member in the
* ec_members list as this is added first in generate_union_paths,
* likewise, the JoinDomain can be that of the initial member of the
* Pathkey's EquivalenceClass.
*/
add_eq_member(pk->pk_eclass,
tle->expr,
child_rel->relids,
parent_em->em_jdomain,
parent_em,
exprType((Node *) tle->expr));

lc2 = lnext(setop_pathkeys, lc2);
}

/*
* transformSetOperationStmt() ensures that the targetlist never contains
* any resjunk columns, so all eclasses that exist in 'root' must have
* received a new member in the loop above. Add them to the child_rel's
* eclass_indexes.
*/
child_rel->eclass_indexes = bms_add_range(child_rel->eclass_indexes, 0,

list_length(root->eq_classes) - 1);
}
=====

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=66c0185a3d14bbbf51d0fc9d267093ffec735231

--
Best regards,
Yuya Watari

#86Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#85)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Thu, May 16, 2024 at 11:44 AM Yuya Watari <watari.yuya@gmail.com> wrote:

I'm sorry it will take some time to solve
this conflict, but I will post a new version when it is fixed.

The previous patches no longer apply to the master, so I rebased them.
I'm sorry for the delay.

I will summarize the patches again.

1. v25-0001
This patch is one of the main parts of my optimization. Traditionally,
EquivalenceClass has both parent and child members. However, this
leads to high iteration costs when there are many child partitions. In
v25-0001, EquivalenceClasses no longer have child members. If we need
to iterate over child EquivalenceMembers, we use the
EquivalenceChildMemberIterator and access the children through the
iterator. For more details, see [1]/messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com (note that there are some design
changes from [1]/messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com).

2. v25-0002
This patch was made in the previous work with David. Like
EquivalenceClass, there are many RestrictInfos in highly partitioned
cases. This patch introduces an indexing mechanism to speed up
searches for RestrictInfos.

3. v25-0003
v25-0002 adds its indexes to RangeTblEntry, but this is not a good
idea. RelOptInfo is the best place. This problem is a workaround
because some RelOptInfos can be NULL, so we cannot store indexes to
such RelOptInfos. v25-0003 moves the indexes from RangeTblEntry to
PlannerInfo. This is still a workaround, and I think it should be
reconsidered.

4. v25-0004
After our changes, add_eq_member() no longer creates and adds child
EquivalenceMembers. This commit renames it to add_parent_eq_member()
to clarify that it only creates parent members, and that we need to
use make_eq_member() to handle child EquivalenceMembers.

5. v25-0005
This commit resolves a conflict with commit 66c0185 [2]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=66c0185a3, which adds
add_setop_child_rel_equivalences(). As I mentioned in the previous
email [3]/messages/by-id/CAJ2pMkZt6r94NUYm-F77FYahjgnMrY4CLHGAD7HxYZxGVwCaow@mail.gmail.com, this function creates child EquivalenceMembers by calling
add_eq_member(). This commit adjusts our optimization so that it can
handle such child members.

[1]: /messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com
[2]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=66c0185a3
[3]: /messages/by-id/CAJ2pMkZt6r94NUYm-F77FYahjgnMrY4CLHGAD7HxYZxGVwCaow@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

v25-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchapplication/octet-stream; name=v25-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchDownload
From 69362f8f5369fd9768dfb9028472963bdfca8994 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 19 Jan 2024 11:43:29 +0900
Subject: [PATCH v25 3/5] Move EquivalenceClass indexes to PlannerInfo

In the previous commit, the indexes, namely ec_[source|derive]_indexes,
were in RestrictInfo. This was a workaround because RelOptInfo was the
best place but some RelOptInfos can be NULL and we cannot store indexes
for them.

This commit introduces a new struct, EquivalenceClassIndexes. This
struct exists in PlannerInfo and holds our indexes. This change prevents
a serialization problem with RestirctInfo in the previous commit.
---
 src/backend/nodes/outfuncs.c            |  2 -
 src/backend/nodes/readfuncs.c           |  2 -
 src/backend/optimizer/path/equivclass.c | 83 ++++++++++++-------------
 src/backend/optimizer/util/inherit.c    |  7 ---
 src/backend/optimizer/util/relnode.c    |  6 ++
 src/include/nodes/parsenodes.h          |  6 --
 src/include/nodes/pathnodes.h           | 26 ++++++++
 src/tools/pgindent/typedefs.list        |  1 +
 8 files changed, 74 insertions(+), 59 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1b3cae4fca..f20fb765d2 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -570,8 +570,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ccf06860fe..b47950764a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -431,8 +431,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index f7d04c8f46..c52f241a27 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -614,10 +614,10 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
 	}
 }
 
@@ -638,10 +638,10 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
 	}
 }
 
@@ -680,22 +680,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(removing_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_del_member(rte->eclass_source_indexes,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -709,22 +709,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(adding_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_sources_index,
-								  rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_add_member(rte->eclass_source_indexes,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_derives_index,
-								  rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_add_member(rte->eclass_derive_indexes,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -739,14 +739,14 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(common_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
+								 index->source_indexes));
 		if (rinfo->eq_derives_index != -1)
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
+								 index->derive_indexes));
 	}
 	bms_free(common_relids);
 #endif
@@ -3874,9 +3874,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3912,7 +3912,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3921,12 +3921,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 index->source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
 		}
 	}
 
@@ -3963,9 +3963,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4001,7 +4001,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4010,12 +4010,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 index->derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
 		}
 	}
 
@@ -4038,9 +4038,8 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 /*
  * verify_eclass_indexes
  *		Verify that there are no missing references between RestrictInfos and
- *		EquivalenceMember's indexes, namely eclass_source_indexes and
- *		eclass_derive_indexes. If you modify these indexes, you should check
- *		them with this function.
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
  */
 void
 verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
@@ -4049,7 +4048,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 
 	/*
 	 * All RestrictInfos in root->eq_sources must have references to
-	 * eclass_source_indexes.
+	 * source_indexes.
 	 */
 	foreach(lc, root->eq_sources)
 	{
@@ -4066,13 +4065,13 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
 		}
 	}
 
 	/*
 	 * All RestrictInfos in root->eq_derives must have references to
-	 * eclass_derive_indexes.
+	 * derive_indexes.
 	 */
 	foreach(lc, root->eq_derives)
 	{
@@ -4089,7 +4088,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
 		}
 	}
 }
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index b5d3984219..3c2198ea5b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,13 +492,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f32575b949..c219de44d7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -218,6 +221,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->top_parent_relid_array = NULL;
 	}
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index c6e8740c18..124d853e49 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1238,12 +1238,6 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6643c862f4..ed075b9184 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -192,6 +192,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -248,6 +250,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
@@ -1528,6 +1537,23 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
+ * struct exists for each relation and holds the corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index bdd6b7fdd9..eae14707d3 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -684,6 +684,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v25-0004-Rename-add_eq_member-to-add_parent_eq_member.patchapplication/octet-stream; name=v25-0004-Rename-add_eq_member-to-add_parent_eq_member.patchDownload
From 5d8cfe71ef20302255f6dce14f9fc558567334c1 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 21 Aug 2024 13:54:59 +0900
Subject: [PATCH v25 4/5] Rename add_eq_member() to add_parent_eq_member()

After our changes, add_eq_member() no longer creates and adds child
EquivalenceMembers. This commit renames it to add_parent_eq_member() to
clarify that it only creates parent members, and that we need to use
make_eq_member() to handle child EquivalenceMembers.
---
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index c52f241a27..39b18e7859 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,10 +38,10 @@ static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
 										 JoinDomain *jdomain,
 										 EquivalenceMember *parent,
 										 Oid datatype);
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
@@ -390,8 +390,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -408,8 +408,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -441,10 +441,10 @@ 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,
-							jdomain, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -579,7 +579,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 }
 
 /*
- * add_eq_member - build a new parent EquivalenceMember and add it to an EC
+ * 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
@@ -587,8 +587,8 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * add_child_join_rel_equivalences() to see how to add child members.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, Oid datatype)
+add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+					 JoinDomain *jdomain, Oid datatype)
 {
 	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
 										   NULL, datatype);
@@ -922,14 +922,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -3225,12 +3225,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

v25-0005-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v25-0005-Resolve-conflict-with-commit-66c0185.patchDownload
From 99d2bc0c39892db2c51ecc98f2d9caac449f072c Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v25 5/5] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 51 ++++++++++++++++++++++---
 src/backend/optimizer/util/inherit.c    |  8 +++-
 src/backend/optimizer/util/relnode.c    | 10 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 39b18e7859..bd920fefe8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3208,7 +3208,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3225,12 +3227,49 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		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
+		 * add_transformed_child_version() 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
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5b..7b2390687e 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index c219de44d7..5ea9a41008 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
@@ -1608,7 +1612,7 @@ find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
 	{
 		int			top_parent_relid = (int) top_parent_relid_array[i];
 
-		if (top_parent_relid == 0)
+		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;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index ed075b9184..bbeef6a65a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -260,7 +260,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v25-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v25-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From ec975ba236ca94a2b78b92ddd576c9c030203a49 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v25 1/5] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  22 +-
 src/backend/optimizer/path/equivclass.c | 414 ++++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c   |  35 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  57 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  89 +++++
 src/include/nodes/pathnodes.h           |  61 ++++
 src/include/optimizer/pathnode.h        |  18 ++
 src/include/optimizer/paths.h           |  12 +-
 src/tools/pgindent/typedefs.list        |   1 +
 11 files changed, 619 insertions(+), 113 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index adc62576d1..93cbc53f03 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7831,13 +7831,21 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	Relids		top_parent_rel_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -7849,6 +7857,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7902,9 +7911,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index d871396e20..5f96435891 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -374,7 +383,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +400,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -423,9 +432,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +520,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +540,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,11 +562,29 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -599,6 +633,17 @@ 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.
@@ -617,7 +662,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +678,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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.
+			 * 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().
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +712,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -691,7 +750,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -757,19 +816,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -779,6 +844,11 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -796,6 +866,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -828,11 +899,17 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -843,6 +920,11 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -876,6 +958,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -939,7 +1022,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1559,7 +1642,12 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1570,9 +1658,14 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -1589,6 +1682,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1606,6 +1700,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1680,6 +1775,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1687,7 +1783,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2400,6 +2496,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2483,8 +2580,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2555,8 +2652,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2653,6 +2750,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2665,7 +2763,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2678,15 +2775,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2699,8 +2790,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2716,6 +2807,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2745,9 +2837,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
@@ -2776,6 +2879,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2797,7 +2901,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2810,15 +2913,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2827,8 +2924,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2843,6 +2940,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2873,9 +2971,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2944,6 +3054,161 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * iterate_child_rel_equivalences
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the iterator.
+ *
+ * The transformation is done in add_transformed_child_version().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	int			i;
+	Relids		matching_relids;
+
+	/* 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. This information is stored in indexes named em_child_relids
+	 * and em_child_joinrel_relids.
+	 *
+	 * This function iterates over the child EquivalenceMembers that reference
+	 * the given 'child_relids'. To do this, we first intersect 'child_relids'
+	 * with these indexes. The result contains Relids of RelOptInfos that have
+	 * child EquivalenceMembers we want to retrieve. Then we get the child
+	 * members from the RelOptInfos using add_transformed_child_version().
+	 *
+	 * We need to do these steps for each of the two indexes.
+	 */
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		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);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -2977,7 +3242,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids,
+				top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -2986,6 +3252,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -2998,6 +3267,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3019,15 +3289,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3042,8 +3316,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3261,8 +3535,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c0fcc7d78d..ce14ca324f 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -183,7 +183,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -927,7 +927,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3017,12 +3017,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3034,7 +3038,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3054,15 +3059,30 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (!member->em_is_child &&
+				!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
@@ -3091,6 +3111,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index e25798972f..c89e415c34 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1149,8 +1149,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1693,8 +1693,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 8e0e5977a9..74ee5cb845 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -262,7 +262,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -271,9 +273,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -295,7 +299,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1283,7 +1287,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1327,7 +1331,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1468,7 +1472,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1499,7 +1503,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1978,7 +1982,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2192,7 +2196,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2216,7 +2220,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2285,7 +2289,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4523,7 +4527,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4537,7 +4541,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6161,7 +6165,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6256,7 +6260,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6272,7 +6276,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6343,7 +6347,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6352,7 +6357,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6378,8 +6383,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6388,7 +6394,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6747,7 +6753,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6810,7 +6817,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index c5b906a9d4..3c2198ea5b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index d7266e4cdb..f32575b949 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +199,25 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +271,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +667,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +785,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +973,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1536,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1530,6 +1577,48 @@ 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 == 0)
+			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 540d021592..b625694e81 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -248,6 +248,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -951,6 +959,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1365,6 +1384,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1437,10 +1462,46 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1035e6560c..5e79cf1f63 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -332,6 +332,24 @@ 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) \
+	(likely((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 970499c469..3894ef0644 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -136,7 +136,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -178,6 +179,15 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9e951a9e6f..bdd6b7fdd9 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -682,6 +682,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
 EquivalenceMember
 ErrorContextCallback
-- 
2.45.2.windows.1

v25-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v25-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From ac7396be79d595eff0d6f7a46051b3adca4b9b6b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v25 2/5] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 516 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/optimizer/paths.h             |  21 +-
 13 files changed, 593 insertions(+), 69 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3337b77ae6..1b3cae4fca 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -570,6 +570,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index b47950764a..ccf06860fe 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -431,6 +431,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index e1523d15df..f8c04ea6b0 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5792,7 +5792,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 5f96435891..f7d04c8f46 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -320,7 +324,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -331,6 +334,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -351,8 +356,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -364,10 +371,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -378,13 +384,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -395,13 +402,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -412,6 +420,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -421,8 +431,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -444,6 +454,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -585,6 +597,167 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -730,8 +903,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1109,7 +1282,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1198,6 +1371,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1207,9 +1381,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1267,9 +1441,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1279,7 +1453,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1345,7 +1520,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1401,11 +1576,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1446,11 +1622,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1828,12 +2004,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1845,12 +2025,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1912,10 +2092,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1926,9 +2107,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1939,9 +2123,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2014,7 +2202,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2702,16 +2890,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3425,7 +3616,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3513,7 +3704,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3666,3 +3857,240 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely eclass_source_indexes and
+ *		eclass_derive_indexes. If you modify these indexes, you should check
+ *		them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * eclass_source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * eclass_derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index c3fd4a81f8..527a6986b7 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -35,9 +35,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -502,7 +503,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -531,7 +532,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -559,19 +560,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -596,14 +606,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -619,9 +629,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -648,11 +660,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -660,7 +673,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b5827d3980..dfaee24430 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -651,6 +651,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 969e257f70..4ede159395 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1042,6 +1042,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 4989722637..4489c2e1bf 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -495,6 +495,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5b..b5d3984219 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,6 +492,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0b406e9334..59ba503ecb 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -247,6 +247,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -403,6 +406,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 124d853e49..c6e8740c18 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1238,6 +1238,12 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b625694e81..6643c862f4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -321,6 +321,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1390,6 +1396,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1408,8 +1432,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -2650,7 +2676,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2768,6 +2799,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 3894ef0644..34a0b0d98a 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -128,6 +128,8 @@ extern bool process_equivalence(PlannerInfo *root,
 extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -164,7 +166,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -203,6 +206,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

#87Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#86)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Thu, Aug 29, 2024 at 2:34 PM Yuya Watari <watari.yuya@gmail.com> wrote:

The previous patches no longer apply to the master, so I rebased them.
I'm sorry for the delay.

I noticed the patches do not apply to the current master. I have
attached the rebased version. There are no changes besides the rebase.

--
Best regards,
Yuya Watari

Attachments:

v26-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v26-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From a927dd85a6253b51cbf228dcdc29078c715a6918 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v26 1/5] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  22 +-
 src/backend/optimizer/path/equivclass.c | 414 ++++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c   |  35 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  57 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  89 +++++
 src/include/nodes/pathnodes.h           |  61 ++++
 src/include/optimizer/pathnode.h        |  18 ++
 src/include/optimizer/paths.h           |  12 +-
 src/tools/pgindent/typedefs.list        |   1 +
 11 files changed, 619 insertions(+), 113 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index adc62576d1..93cbc53f03 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7831,13 +7831,21 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	Relids		top_parent_rel_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -7849,6 +7857,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7902,9 +7911,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8f6f005ecb..b2ae2c1f7a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
@@ -69,6 +73,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -374,7 +383,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -391,7 +400,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -423,9 +432,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -511,11 +520,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -526,6 +540,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -546,11 +562,29 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -599,6 +633,17 @@ 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.
@@ -617,7 +662,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -632,16 +678,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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.
+			 * 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().
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -654,6 +712,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -691,7 +750,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -761,19 +820,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -783,6 +848,11 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -800,6 +870,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -832,11 +903,17 @@ find_computable_ec_member(PlannerInfo *root,
 						  Relids relids,
 						  bool require_parallel_safe)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *exprvars;
 		ListCell   *lc2;
 
@@ -847,6 +924,11 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -880,6 +962,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -943,7 +1026,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1568,7 +1651,12 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1579,9 +1667,14 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -1598,6 +1691,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1615,6 +1709,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1689,6 +1784,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1696,7 +1792,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2409,6 +2505,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2530,8 +2627,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2602,8 +2699,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2700,6 +2797,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2712,7 +2810,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2725,15 +2822,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2746,8 +2837,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2763,6 +2854,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2792,9 +2884,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
@@ -2823,6 +2926,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2844,7 +2948,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2857,15 +2960,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2874,8 +2971,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2890,6 +2987,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2920,9 +3018,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2991,6 +3101,161 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * iterate_child_rel_equivalences
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the iterator.
+ *
+ * The transformation is done in add_transformed_child_version().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	int			i;
+	Relids		matching_relids;
+
+	/* 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. This information is stored in indexes named em_child_relids
+	 * and em_child_joinrel_relids.
+	 *
+	 * This function iterates over the child EquivalenceMembers that reference
+	 * the given 'child_relids'. To do this, we first intersect 'child_relids'
+	 * with these indexes. The result contains Relids of RelOptInfos that have
+	 * child EquivalenceMembers we want to retrieve. Then we get the child
+	 * members from the RelOptInfos using add_transformed_child_version().
+	 *
+	 * We need to do these steps for each of the two indexes.
+	 */
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		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);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3024,7 +3289,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids,
+				top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -3033,6 +3299,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -3045,6 +3314,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3066,15 +3336,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3089,8 +3363,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3308,8 +3582,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c0fcc7d78d..ce14ca324f 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -183,7 +183,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -927,7 +927,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3017,12 +3017,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3034,7 +3038,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3054,15 +3059,30 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (!member->em_is_child &&
+				!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
@@ -3091,6 +3111,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 035bbaa385..d8a1f904d3 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1150,8 +1150,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1707,8 +1707,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index bb45ef318f..11d3fe9ddd 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -262,7 +262,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -271,9 +273,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -295,7 +299,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1283,7 +1287,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1327,7 +1331,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1468,7 +1472,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1499,7 +1503,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1978,7 +1982,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2192,7 +2196,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2216,7 +2220,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2285,7 +2289,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4523,7 +4527,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->outersortkeys)
 	{
 		Relids		outer_relids = outer_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids);
 
@@ -4537,7 +4541,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	if (best_path->innersortkeys)
 	{
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6161,7 +6165,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6228,7 +6232,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6256,7 +6260,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6272,7 +6276,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6343,7 +6347,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6352,7 +6357,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6378,8 +6383,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6388,7 +6394,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6747,7 +6753,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6810,7 +6817,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index c5b906a9d4..3c2198ea5b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index d7266e4cdb..f32575b949 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +199,25 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +271,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +667,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +785,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +973,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1536,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1530,6 +1577,48 @@ 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 == 0)
+			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 07e2415398..a1f044d8a6 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -248,6 +248,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -957,6 +965,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1371,6 +1390,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1443,10 +1468,46 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1035e6560c..5e79cf1f63 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -332,6 +332,24 @@ 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) \
+	(likely((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 54869d4401..50812e3a5d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -179,6 +180,15 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 5fabb127d7..623a770b0e 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -683,6 +683,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
 EquivalenceMember
 ErrorContextCallback
-- 
2.45.2.windows.1

v26-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v26-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From 745b304ecfb4cffe1db1ecc7b64f6069b91f8adc Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v26 2/5] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 516 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/optimizer/paths.h             |  21 +-
 13 files changed, 593 insertions(+), 69 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9827cf16be..bd6a79b57a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -573,6 +573,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index be5f19dd7f..70864fc1b2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,6 +434,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index e1523d15df..f8c04ea6b0 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5792,7 +5792,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index b2ae2c1f7a..7544ea93f2 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static bool is_exprlist_member(Expr *node, List *exprs);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -320,7 +324,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -331,6 +334,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -351,8 +356,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -364,10 +371,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -378,13 +384,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -395,13 +402,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -412,6 +420,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -421,8 +431,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -444,6 +454,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -585,6 +597,167 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -730,8 +903,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1113,7 +1286,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1206,6 +1379,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1215,9 +1389,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1275,9 +1449,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1287,7 +1461,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1353,7 +1528,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1410,11 +1585,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1455,11 +1631,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1837,12 +2013,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1854,12 +2034,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1921,10 +2101,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1935,9 +2116,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1948,9 +2132,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2023,7 +2211,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2749,16 +2937,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3472,7 +3663,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3560,7 +3751,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3717,3 +3908,240 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely eclass_source_indexes and
+ *		eclass_derive_indexes. If you modify these indexes, you should check
+ *		them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * eclass_source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * eclass_derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 928d926645..c8f7435909 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -36,9 +36,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -476,7 +477,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -505,7 +506,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -578,19 +579,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -615,14 +625,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -638,9 +648,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -667,11 +679,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -679,7 +692,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index d92d43a17e..28b3a5cabb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -659,6 +659,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index a70404558f..99e1f1ce0d 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1042,6 +1042,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 4989722637..4489c2e1bf 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -495,6 +495,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5b..b5d3984219 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,6 +492,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0b406e9334..59ba503ecb 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -247,6 +247,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -403,6 +406,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1c314cd907..fefe37b589 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,6 +1247,12 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a1f044d8a6..0d06bb150b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -321,6 +321,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1396,6 +1402,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1414,8 +1438,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -2656,7 +2682,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2774,6 +2805,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 50812e3a5d..a24a5801c5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -165,7 +167,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +207,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v26-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchapplication/octet-stream; name=v26-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchDownload
From 6f6058be5bbb03b06c7500a2ced3a17ea0712d18 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 19 Jan 2024 11:43:29 +0900
Subject: [PATCH v26 3/5] Move EquivalenceClass indexes to PlannerInfo

In the previous commit, the indexes, namely ec_[source|derive]_indexes,
were in RestrictInfo. This was a workaround because RelOptInfo was the
best place but some RelOptInfos can be NULL and we cannot store indexes
for them.

This commit introduces a new struct, EquivalenceClassIndexes. This
struct exists in PlannerInfo and holds our indexes. This change prevents
a serialization problem with RestirctInfo in the previous commit.
---
 src/backend/nodes/outfuncs.c            |  2 -
 src/backend/nodes/readfuncs.c           |  2 -
 src/backend/optimizer/path/equivclass.c | 83 ++++++++++++-------------
 src/backend/optimizer/util/inherit.c    |  7 ---
 src/backend/optimizer/util/relnode.c    |  6 ++
 src/include/nodes/parsenodes.h          |  6 --
 src/include/nodes/pathnodes.h           | 26 ++++++++
 src/tools/pgindent/typedefs.list        |  1 +
 8 files changed, 74 insertions(+), 59 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bd6a79b57a..8a635a2a12 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -573,8 +573,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 70864fc1b2..be5f19dd7f 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,8 +434,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7544ea93f2..ea98e88bf4 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -614,10 +614,10 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
 	}
 }
 
@@ -638,10 +638,10 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
 	}
 }
 
@@ -680,22 +680,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(removing_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_del_member(rte->eclass_source_indexes,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -709,22 +709,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(adding_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_sources_index,
-								  rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_add_member(rte->eclass_source_indexes,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_derives_index,
-								  rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_add_member(rte->eclass_derive_indexes,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -739,14 +739,14 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(common_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
+								 index->source_indexes));
 		if (rinfo->eq_derives_index != -1)
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
+								 index->derive_indexes));
 	}
 	bms_free(common_relids);
 #endif
@@ -3925,9 +3925,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3963,7 +3963,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3972,12 +3972,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 index->source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
 		}
 	}
 
@@ -4014,9 +4014,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4052,7 +4052,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4061,12 +4061,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 index->derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
 		}
 	}
 
@@ -4089,9 +4089,8 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 /*
  * verify_eclass_indexes
  *		Verify that there are no missing references between RestrictInfos and
- *		EquivalenceMember's indexes, namely eclass_source_indexes and
- *		eclass_derive_indexes. If you modify these indexes, you should check
- *		them with this function.
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
  */
 void
 verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
@@ -4100,7 +4099,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 
 	/*
 	 * All RestrictInfos in root->eq_sources must have references to
-	 * eclass_source_indexes.
+	 * source_indexes.
 	 */
 	foreach(lc, root->eq_sources)
 	{
@@ -4117,13 +4116,13 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
 		}
 	}
 
 	/*
 	 * All RestrictInfos in root->eq_derives must have references to
-	 * eclass_derive_indexes.
+	 * derive_indexes.
 	 */
 	foreach(lc, root->eq_derives)
 	{
@@ -4140,7 +4139,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
 		}
 	}
 }
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index b5d3984219..3c2198ea5b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,13 +492,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f32575b949..c219de44d7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -218,6 +221,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->top_parent_relid_array = NULL;
 	}
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fefe37b589..1c314cd907 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,12 +1247,6 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0d06bb150b..3eeaa7068b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -192,6 +192,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -248,6 +250,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
@@ -1534,6 +1543,23 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
+ * struct exists for each relation and holds the corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 623a770b0e..1170c0a891 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -685,6 +685,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v26-0004-Rename-add_eq_member-to-add_parent_eq_member.patchapplication/octet-stream; name=v26-0004-Rename-add_eq_member-to-add_parent_eq_member.patchDownload
From 97c87e1dcf3977c312e7edd7c2c8b915a02aed96 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 21 Aug 2024 13:54:59 +0900
Subject: [PATCH v26 4/5] Rename add_eq_member() to add_parent_eq_member()

After our changes, add_eq_member() no longer creates and adds child
EquivalenceMembers. This commit renames it to add_parent_eq_member() to
clarify that it only creates parent members, and that we need to use
make_eq_member() to handle child EquivalenceMembers.
---
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index ea98e88bf4..1cb1bfa075 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,10 +38,10 @@ static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
 										 JoinDomain *jdomain,
 										 EquivalenceMember *parent,
 										 Oid datatype);
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
@@ -390,8 +390,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -408,8 +408,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -441,10 +441,10 @@ 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,
-							jdomain, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -579,7 +579,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 }
 
 /*
- * add_eq_member - build a new parent EquivalenceMember and add it to an EC
+ * 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
@@ -587,8 +587,8 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * add_child_join_rel_equivalences() to see how to add child members.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, Oid datatype)
+add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+					 JoinDomain *jdomain, Oid datatype)
 {
 	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
 										   NULL, datatype);
@@ -922,14 +922,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -3272,12 +3272,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

v26-0005-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v26-0005-Resolve-conflict-with-commit-66c0185.patchDownload
From 6837c1e592e0870043b64e2e9046f587073dd2f5 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v26 5/5] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 51 ++++++++++++++++++++++---
 src/backend/optimizer/util/inherit.c    |  8 +++-
 src/backend/optimizer/util/relnode.c    | 10 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 1cb1bfa075..2ff9c606d5 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3255,7 +3255,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3272,12 +3274,49 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		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
+		 * add_transformed_child_version() 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
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5b..7b2390687e 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index c219de44d7..5ea9a41008 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
@@ -1608,7 +1612,7 @@ find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
 	{
 		int			top_parent_relid = (int) top_parent_relid_array[i];
 
-		if (top_parent_relid == 0)
+		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;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 3eeaa7068b..8dc32eb606 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -260,7 +260,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

#88Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#87)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Tue, Oct 1, 2024 at 11:35 AM Yuya Watari <watari.yuya@gmail.com> wrote:

I noticed the patches do not apply to the current master. I have
attached the rebased version. There are no changes besides the rebase.

The previous patches do not apply to the current master, so I have
attached the rebased version.

--
Best regards,
Yuya Watari

Attachments:

v27-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v27-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From caac27cf35d50107e3248bdceffcb6702d7e917d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v27 1/5] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  22 +-
 src/backend/optimizer/path/equivclass.c | 414 ++++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c   |  35 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  89 +++++
 src/include/nodes/pathnodes.h           |  61 ++++
 src/include/optimizer/pathnode.h        |  18 ++
 src/include/optimizer/paths.h           |  12 +-
 src/tools/pgindent/typedefs.list        |   1 +
 11 files changed, 622 insertions(+), 114 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index adc62576d1..93cbc53f03 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7831,13 +7831,21 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	Relids		top_parent_rel_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_rel_relids = find_relids_top_parents(root, rel->relids);
+
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -7849,6 +7857,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7902,9 +7911,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index fae137dd82..bcd56da3d2 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -68,6 +72,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -373,7 +382,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -390,7 +399,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -422,9 +431,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +519,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +539,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +561,29 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -598,6 +632,17 @@ 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.
@@ -616,7 +661,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,16 +677,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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.
+			 * 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().
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -653,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -690,7 +749,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -760,19 +819,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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;
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -782,6 +847,11 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -799,6 +869,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +912,9 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -854,9 +927,13 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_WINDOWFUNCS |
 							   PVC_INCLUDE_PLACEHOLDERS);
 
-	foreach(lc, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_relids = find_relids_top_parents(root, relids);
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -867,6 +944,11 @@ find_computable_ec_member(PlannerInfo *root,
 		if (em->em_is_const)
 			continue;
 
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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.
 		 */
@@ -900,6 +982,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -938,7 +1021,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1563,7 +1646,12 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	Relids		top_parent_join_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
+
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_join_relids = find_relids_top_parents(root, join_relids);
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1574,9 +1662,14 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		if (unlikely(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
@@ -1593,6 +1686,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1610,6 +1704,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1684,6 +1779,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1691,7 +1787,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2404,6 +2500,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2525,8 +2622,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2597,8 +2694,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2695,6 +2792,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2707,7 +2805,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,15 +2817,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2741,8 +2832,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2758,6 +2849,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2787,9 +2879,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
@@ -2818,6 +2921,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2839,7 +2943,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2852,15 +2955,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2869,8 +2966,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2885,6 +2982,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2915,9 +3013,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2986,6 +3096,161 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * iterate_child_rel_equivalences
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the iterator.
+ *
+ * The transformation is done in add_transformed_child_version().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	int			i;
+	Relids		matching_relids;
+
+	/* 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. This information is stored in indexes named em_child_relids
+	 * and em_child_joinrel_relids.
+	 *
+	 * This function iterates over the child EquivalenceMembers that reference
+	 * the given 'child_relids'. To do this, we first intersect 'child_relids'
+	 * with these indexes. The result contains Relids of RelOptInfos that have
+	 * child EquivalenceMembers we want to retrieve. Then we get the child
+	 * members from the RelOptInfos using add_transformed_child_version().
+	 *
+	 * We need to do these steps for each of the two indexes.
+	 */
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		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);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3019,7 +3284,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids,
+				top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -3028,6 +3294,9 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	/* Indexes are available only on base or "other" member relations. */
 	Assert(IS_SIMPLE_REL(rel));
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	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);
@@ -3040,6 +3309,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3061,15 +3331,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3084,8 +3358,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3303,8 +3577,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c0fcc7d78d..ce14ca324f 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -183,7 +183,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -927,7 +927,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3017,12 +3017,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
+	/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+	top_parent_index_relids = find_relids_top_parents(root, index->rel->relids);
+
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
 
@@ -3034,7 +3038,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3054,15 +3059,30 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
+			/* See the comments in get_eclass_for_sort_expr() to see how this works. */
+			if (unlikely(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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (!member->em_is_child &&
+				!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
@@ -3091,6 +3111,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 7cf15e89b6..b6410f31da 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index f2ed0d81f6..cfb9dd75fe 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1285,7 +1289,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1329,7 +1333,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1470,7 +1474,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1501,7 +1505,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1981,7 +1985,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2195,7 +2199,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2219,7 +2223,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2288,7 +2292,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4550,7 +4554,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4559,7 +4564,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4584,7 +4590,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6236,7 +6242,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6303,7 +6309,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6331,7 +6337,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6347,7 +6353,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6418,7 +6424,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6427,7 +6434,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6453,8 +6460,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6463,7 +6471,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6822,7 +6830,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6885,7 +6894,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index c5b906a9d4..3c2198ea5b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index d7266e4cdb..f32575b949 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +199,25 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +271,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +667,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +785,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +973,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1536,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1530,6 +1577,48 @@ 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 == 0)
+			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 07e2415398..a1f044d8a6 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -248,6 +248,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -957,6 +965,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1371,6 +1390,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1443,10 +1468,46 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1035e6560c..5e79cf1f63 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -332,6 +332,24 @@ 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) \
+	(likely((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 54869d4401..50812e3a5d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -179,6 +180,15 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 57de1acff3..9a0bcecb9a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -683,6 +683,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
 EquivalenceMember
 ErrorContextCallback
-- 
2.45.2.windows.1

v27-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v27-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From dbf44c033769fdfc3ebcfbc4930af8b1c98b9540 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v27 2/5] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 516 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/optimizer/paths.h             |  21 +-
 13 files changed, 593 insertions(+), 69 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9827cf16be..bd6a79b57a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -573,6 +573,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index be5f19dd7f..70864fc1b2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,6 +434,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 2bb6db1df7..d18f3979b0 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5840,7 +5840,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index bcd56da3d2..2e84663606 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -319,7 +323,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -330,6 +333,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -350,8 +355,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -363,10 +370,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -377,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -394,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -411,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -420,8 +430,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -443,6 +453,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -584,6 +596,167 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -729,8 +902,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1108,7 +1281,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1201,6 +1374,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1210,9 +1384,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1270,9 +1444,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1282,7 +1456,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1348,7 +1523,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1405,11 +1580,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1450,11 +1626,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1832,12 +2008,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1849,12 +2029,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1916,10 +2096,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1930,9 +2111,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1943,9 +2127,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2018,7 +2206,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2744,16 +2932,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3467,7 +3658,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3555,7 +3746,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3712,3 +3903,240 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely eclass_source_indexes and
+ *		eclass_derive_indexes. If you modify these indexes, you should check
+ *		them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * eclass_source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * eclass_derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 928d926645..c8f7435909 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -36,9 +36,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -476,7 +477,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -505,7 +506,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -578,19 +579,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -615,14 +625,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -638,9 +648,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -667,11 +679,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -679,7 +692,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0f423e9684..51c4a4145d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -659,6 +659,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 4d7f972caf..f17e9e2ab7 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1137,6 +1137,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 4989722637..4489c2e1bf 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -495,6 +495,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5b..b5d3984219 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,6 +492,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0b406e9334..59ba503ecb 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -247,6 +247,9 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -403,6 +406,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index c92cef3d16..504308a84b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,6 +1247,12 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a1f044d8a6..0d06bb150b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -321,6 +321,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1396,6 +1402,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1414,8 +1438,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -2656,7 +2682,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2774,6 +2805,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 50812e3a5d..a24a5801c5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -165,7 +167,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +207,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v27-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchapplication/octet-stream; name=v27-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchDownload
From 66f4d2f89119184ee1968a67c1fe4464ba8c498f Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 19 Jan 2024 11:43:29 +0900
Subject: [PATCH v27 3/5] Move EquivalenceClass indexes to PlannerInfo

In the previous commit, the indexes, namely ec_[source|derive]_indexes,
were in RestrictInfo. This was a workaround because RelOptInfo was the
best place but some RelOptInfos can be NULL and we cannot store indexes
for them.

This commit introduces a new struct, EquivalenceClassIndexes. This
struct exists in PlannerInfo and holds our indexes. This change prevents
a serialization problem with RestirctInfo in the previous commit.
---
 src/backend/nodes/outfuncs.c            |  2 -
 src/backend/nodes/readfuncs.c           |  2 -
 src/backend/optimizer/path/equivclass.c | 83 ++++++++++++-------------
 src/backend/optimizer/util/inherit.c    |  7 ---
 src/backend/optimizer/util/relnode.c    |  6 ++
 src/include/nodes/parsenodes.h          |  6 --
 src/include/nodes/pathnodes.h           | 26 ++++++++
 src/tools/pgindent/typedefs.list        |  1 +
 8 files changed, 74 insertions(+), 59 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bd6a79b57a..8a635a2a12 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -573,8 +573,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 70864fc1b2..be5f19dd7f 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,8 +434,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 2e84663606..679cf34174 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -613,10 +613,10 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
 	}
 }
 
@@ -637,10 +637,10 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
 	}
 }
 
@@ -679,22 +679,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(removing_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_del_member(rte->eclass_source_indexes,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -708,22 +708,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(adding_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_sources_index,
-								  rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_add_member(rte->eclass_source_indexes,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_derives_index,
-								  rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_add_member(rte->eclass_derive_indexes,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -738,14 +738,14 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(common_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
+								 index->source_indexes));
 		if (rinfo->eq_derives_index != -1)
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
+								 index->derive_indexes));
 	}
 	bms_free(common_relids);
 #endif
@@ -3920,9 +3920,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -3958,7 +3958,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -3967,12 +3967,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 index->source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
 		}
 	}
 
@@ -4009,9 +4009,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4047,7 +4047,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4056,12 +4056,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 index->derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
 		}
 	}
 
@@ -4084,9 +4084,8 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 /*
  * verify_eclass_indexes
  *		Verify that there are no missing references between RestrictInfos and
- *		EquivalenceMember's indexes, namely eclass_source_indexes and
- *		eclass_derive_indexes. If you modify these indexes, you should check
- *		them with this function.
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
  */
 void
 verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
@@ -4095,7 +4094,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 
 	/*
 	 * All RestrictInfos in root->eq_sources must have references to
-	 * eclass_source_indexes.
+	 * source_indexes.
 	 */
 	foreach(lc, root->eq_sources)
 	{
@@ -4112,13 +4111,13 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
 		}
 	}
 
 	/*
 	 * All RestrictInfos in root->eq_derives must have references to
-	 * eclass_derive_indexes.
+	 * derive_indexes.
 	 */
 	foreach(lc, root->eq_derives)
 	{
@@ -4135,7 +4134,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
 		}
 	}
 }
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index b5d3984219..3c2198ea5b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,13 +492,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f32575b949..c219de44d7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -218,6 +221,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->top_parent_relid_array = NULL;
 	}
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 504308a84b..c92cef3d16 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,12 +1247,6 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0d06bb150b..3eeaa7068b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -192,6 +192,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -248,6 +250,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
@@ -1534,6 +1543,23 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
+ * struct exists for each relation and holds the corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9a0bcecb9a..3e92b5a27a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -685,6 +685,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v27-0004-Rename-add_eq_member-to-add_parent_eq_member.patchapplication/octet-stream; name=v27-0004-Rename-add_eq_member-to-add_parent_eq_member.patchDownload
From 56bafc290624114ae5fa86782b36b7b3a515ef4d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 21 Aug 2024 13:54:59 +0900
Subject: [PATCH v27 4/5] Rename add_eq_member() to add_parent_eq_member()

After our changes, add_eq_member() no longer creates and adds child
EquivalenceMembers. This commit renames it to add_parent_eq_member() to
clarify that it only creates parent members, and that we need to use
make_eq_member() to handle child EquivalenceMembers.
---
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 679cf34174..c54ebd7432 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,10 +38,10 @@ static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
 										 JoinDomain *jdomain,
 										 EquivalenceMember *parent,
 										 Oid datatype);
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
@@ -389,8 +389,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -407,8 +407,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -440,10 +440,10 @@ 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,
-							jdomain, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -578,7 +578,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 }
 
 /*
- * add_eq_member - build a new parent EquivalenceMember and add it to an EC
+ * 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
@@ -586,8 +586,8 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * add_child_join_rel_equivalences() to see how to add child members.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, Oid datatype)
+add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+					 JoinDomain *jdomain, Oid datatype)
 {
 	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
 										   NULL, datatype);
@@ -921,14 +921,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -3267,12 +3267,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

v27-0005-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v27-0005-Resolve-conflict-with-commit-66c0185.patchDownload
From 44f04135490cd42ba79e3537183bca4f8e0c44ea Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v27 5/5] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 51 ++++++++++++++++++++++---
 src/backend/optimizer/util/inherit.c    |  8 +++-
 src/backend/optimizer/util/relnode.c    | 10 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index c54ebd7432..5ff55d71ee 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3250,7 +3250,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3267,12 +3269,49 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		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
+		 * add_transformed_child_version() 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
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5b..7b2390687e 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index c219de44d7..5ea9a41008 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
@@ -1608,7 +1612,7 @@ find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
 	{
 		int			top_parent_relid = (int) top_parent_relid_array[i];
 
-		if (top_parent_relid == 0)
+		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;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 3eeaa7068b..8dc32eb606 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -260,7 +260,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

#89Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Yuya Watari (#88)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, Oct 15, 2024 at 12:20:04PM GMT, Yuya Watari wrote:

The previous patches do not apply to the current master, so I have
attached the rebased version.

Thanks for keeping it up to date.

v25-0001
This patch is one of the main parts of my optimization. Traditionally,
EquivalenceClass has both parent and child members. However, this
leads to high iteration costs when there are many child partitions. In
v25-0001, EquivalenceClasses no longer have child members. If we need
to iterate over child EquivalenceMembers, we use the
EquivalenceChildMemberIterator and access the children through the
iterator. For more details, see [1] (note that there are some design
changes from [1]).

The referenced email containst some benchmark results. But shouldn't the
benchmark be repeated after those design changes you're talking about?

Few random notes after quickly looking through the first patch:

* There are patterns like this scattered around, it looks somewhat confusing:

	+   /* See the comments in get_eclass_for_sort_expr() to see how this works. */
	+   top_parent_rel_relids = find_relids_top_parents(root, rel->relids);

It's not immediately clear which part of get_eclass_for_sort_expr is
relevant, or one have to read the whole function first. Probably better to
omit the superficial commentary on the call site, and instead expand the
commentary for the find_relids_top_parents itself?

* The patch series features likely/unlikely since v20, but don't see any
discussion about that. Did you notice any visible boost from that? I wonder
how necessary that is.

#90Yuya Watari
watari.yuya@gmail.com
In reply to: Dmitry Dolgov (#89)
8 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Dmitry,

I really appreciate you reviewing these patches.

On Thu, Nov 28, 2024 at 4:51 AM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Few random notes after quickly looking through the first patch:

* There are patterns like this scattered around, it looks somewhat confusing:

+   /* See the comments in get_eclass_for_sort_expr() to see how this works. */
+   top_parent_rel_relids = find_relids_top_parents(root, rel->relids);

It's not immediately clear which part of get_eclass_for_sort_expr is
relevant, or one have to read the whole function first. Probably better to
omit the superficial commentary on the call site, and instead expand the
commentary for the find_relids_top_parents itself?

Thank you for pointing this out. As you said, those comments are not
helpful for understanding, and I also agree that expanding the actual
comments is better. I fixed this in v28.

* The patch series features likely/unlikely since v20, but don't see any
discussion about that. Did you notice any visible boost from that? I wonder
how necessary that is.

I'm sorry I haven't tested the effects of these likely/unlikely. To
discuss the need for them, I have added the removals of these
likely/unlikely as v28-0006 and v28-0007, which are attached to this
email. I would like to decide whether to adopt these removals after
discussion here.

There are two uses of likely and unlikely in the original patches:

1. likely in the find_relids_top_parents() macro (quoted below)

The find_relids_top_parents() macro has likely. This macro replaces
the given Relids as their parents. If they are all already parents, it
simply returns NULL. This is to avoid slowing down for non-partitioned
cases. For such cases, we don't need to do anything to introduce child
EquivalenceMembers. The patches effectively skip this by checking
whether the returned Relids is NULL.

If there are no partitioned tables in the given query,
root->top_parent_relid_array is set to NULL and the macro can return
NULL immediately. I assumed that non-partitioned use cases are common,
I used likely here. In the attached patches, I removed this in
v28-0006 to confirm its effects.

=====
#define find_relids_top_parents(root, relids) \
(likely((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);
=====

2. unlikely, which checks if top_parent_relids is null (quoted below)

The caller of the find_relids_top_parents() macro has unlikely for the
same reason. I removed this in v28-0007.

=====
top_parent_rel = find_relids_top_parents(root, rel);
...
/*
* If child EquivalenceMembers may match the request, we add and
* iterate over them by calling iterate_child_rel_equivalences().
*/
if (unlikely(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);
=====

The referenced email containst some benchmark results. But shouldn't the
benchmark be repeated after those design changes you're talking about?

Thank you for pointing this out. I ran the same benchmarks as in [1]/messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com.

1. Versions used in the benchmarks

I used the following five versions in the benchmarks:

* Master,
* v23: The version just before the design change (introduction of an
iterator over child EquivalenceMembers). I rebased this old version
for these benchmarks,
* ~v28-0005: Same as v27,
* ~v28-0006: ~v28-0005 with "likely" removed,
* ~v28-0007: ~v28-0006 with "unlikely" removed.

2. Small size cases (make installcheck)

This benchmark is to confirm regressions with the patches. I ran 'make
installcheck' and measured the planning times of all queries executed
during the tests.

Table 1: Total planning time for installcheck (seconds)
--------------------------------------------
Program | Mean | Median | Stddev
--------------------------------------------
Master | 0.920580 | 0.920022 | 0.007805
v23 | 0.922827 | 0.922795 | 0.006311
~v28-0005 | 0.927891 | 0.927387 | 0.007835
~v28-0006 | 0.926924 | 0.926663 | 0.007905
~v28-0007 | 0.928182 | 0.926434 | 0.010324
--------------------------------------------

Table 2: Speedup for installcheck (higher is better)
----------------------------
Program | Mean | Median
----------------------------
v23 | 99.8% | 99.7%
~v28-0005 | 99.2% | 99.2%
~v28-0006 | 99.3% | 99.3%
~v28-0007 | 99.2% | 99.3%
----------------------------

3. Large size cases (queries A and B)

I also evaluated the patches using queries A and B, which can be found
at [2]/messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com. Both queries join partitioned tables.

Table 3: Planning time of query A
(n: the number of partitions of each table)
(lower is better)
-------------------------------------------------------------
n | Master | v23 | ~v28-0005 | ~v28-0006 | ~v28-0007
-------------------------------------------------------------
1 | 0.245 | 0.250 | 0.253 | 0.252 | 0.250
2 | 0.266 | 0.273 | 0.274 | 0.274 | 0.275
4 | 0.339 | 0.351 | 0.352 | 0.350 | 0.350
8 | 0.435 | 0.443 | 0.446 | 0.442 | 0.443
16 | 0.619 | 0.623 | 0.625 | 0.626 | 0.625
32 | 1.065 | 1.009 | 1.018 | 1.022 | 1.025
64 | 2.520 | 2.197 | 2.196 | 2.202 | 2.204
128 | 6.076 | 4.634 | 4.610 | 4.588 | 4.563
256 | 17.447 | 10.416 | 10.576 | 10.463 | 10.545
384 | 33.068 | 16.023 | 16.019 | 16.015 | 16.092
512 | 57.213 | 21.793 | 21.930 | 21.882 | 21.972
640 | 96.241 | 28.208 | 28.441 | 28.205 | 28.308
768 | 154.046 | 34.724 | 35.059 | 35.012 | 35.001
896 | 241.509 | 49.596 | 49.902 | 49.631 | 49.829
1024 | 319.269 | 48.297 | 48.892 | 48.417 | 48.384
-------------------------------------------------------------

Table 4: Speedup of query A (higher is better)
---------------------------------------------------
n | v23 | ~v28-0005 | ~v28-0006 | ~v28-0007
---------------------------------------------------
1 | 98.1% | 96.6% | 97.2% | 97.8%
2 | 97.3% | 96.9% | 97.0% | 96.8%
4 | 96.8% | 96.3% | 97.0% | 96.9%
8 | 98.0% | 97.4% | 98.4% | 98.2%
16 | 99.3% | 99.0% | 98.8% | 99.0%
32 | 105.6% | 104.7% | 104.2% | 103.9%
64 | 114.7% | 114.8% | 114.4% | 114.4%
128 | 131.1% | 131.8% | 132.4% | 133.2%
256 | 167.5% | 165.0% | 166.8% | 165.5%
384 | 206.4% | 206.4% | 206.5% | 205.5%
512 | 262.5% | 260.9% | 261.5% | 260.4%
640 | 341.2% | 338.4% | 341.2% | 340.0%
768 | 443.6% | 439.4% | 440.0% | 440.1%
896 | 487.0% | 484.0% | 486.6% | 484.7%
1024 | 661.0% | 653.0% | 659.4% | 659.9%
---------------------------------------------------

Table 5: Planning time of query B (lower is better)
-------------------------------------------------------------
n | Master | v23 | ~v28-0005 | ~v28-0006 | ~v28-0007
-------------------------------------------------------------
1 | 11.759 | 11.952 | 12.017 | 11.999 | 11.975
2 | 11.334 | 11.474 | 11.538 | 11.525 | 11.522
4 | 11.790 | 11.866 | 11.894 | 11.849 | 11.930
8 | 13.029 | 12.799 | 12.818 | 12.798 | 12.806
16 | 15.649 | 14.373 | 14.353 | 14.404 | 14.384
32 | 22.462 | 17.733 | 17.861 | 17.908 | 17.830
64 | 44.593 | 26.238 | 26.461 | 26.512 | 26.578
128 | 135.504 | 47.256 | 47.394 | 47.372 | 47.537
256 | 818.483 | 105.361 | 105.749 | 105.619 | 105.772
-------------------------------------------------------------

Table 6: Speedup of query B (higher is better)
--------------------------------------------------
n | v23 | ~v28-0005 | ~v28-0006 | ~v28-0007
--------------------------------------------------
1 | 98.4% | 97.8% | 98.0% | 98.2%
2 | 98.8% | 98.2% | 98.3% | 98.4%
4 | 99.4% | 99.1% | 99.5% | 98.8%
8 | 101.8% | 101.6% | 101.8% | 101.7%
16 | 108.9% | 109.0% | 108.6% | 108.8%
32 | 126.7% | 125.8% | 125.4% | 126.0%
64 | 170.0% | 168.5% | 168.2% | 167.8%
128 | 286.7% | 285.9% | 286.0% | 285.0%
256 | 776.8% | 774.0% | 774.9% | 773.8%
--------------------------------------------------

4. Discussion

First of all, tables 1, 2 and the figure attached to this email show
that likely and unlikely do not have the effect I expected. Rather,
tables 3, 4, 5 and 6 imply that they can have a negative effect on
queries A and B. So it is better to remove these likely and unlikely.

For the design change, the benchmark results show that it may cause
some regression, especially for smaller sizes. However, Figure 1 also
shows that the regression is much smaller than its variance. This
design change is intended to improve code maintainability. The
regression is small enough that I think these results are acceptable.
What do you think about this?

[1]: /messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com
[2]: /messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

figure.pngimage/png; name=figure.pngDownload
�PNG


IHDR
��z3�sRGB���gAMA���a	pHYs��]����IDATx^�����}x>Z��2e��@����b������7�����<H�I��S�jS��%��\�3�KU0wY�m�j.u�T������$��n�^J�5_���7�grYo�5L��lNHI�H���)Ss��<M�4��~������T��������������gj�=	��������V�@��������\��#��Pz"*��'���~:}���5��_~9���+��'"`<��"r����0���[`B	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&��LMMUj�u�V^3F����~<����ayy�6��c�W���WO��q�C�s�yC/Z�3����T�96����(����|������Z0�����h��1R5]�~�6�����h8V@���
�G��J�@���bZ]]�c��z�(���->�F�=>��|
 D@����GC��0���ELCMB|��)�	P��/�%�� H�������O	$D0���=EZM[vXXX�K�=kkk-���C������[$��j��.;i5m��{���{r��������V��4;;�677s
`�LMM��~���u�D����R�v�Z����/��Yi���h"t���\�O�c��9�U�W@y�����ku~���	�;������{T��k��zX��@����{�~�_�HOD@_"����D0LoDp�������N�����p����� ����sm���88BD��,--�P%BD���?>�t���\FM���"�������r����7����^5/�������b\�t1�����i�M���]1����F.�N�s�>����1�G����W�u��O?�_�������s=�Z�:mc�������Z���h{������?)b���oZ�Wt���M����3}����_�q���[��3�mi~��c������Il_�<1�Q��7����Zfe�c��������y���
�<���v��J����B���aR�M�cR���w�y�-..�{�������^t��������N����[�#�__��������j�*���X��ukF���]��������/�!����5�qX__�SW<���Z�uh7�>}�6_7Z-����c���~��Sf�y{��E�qT�2�C���E���cr_�z7���V�j���8���y�b���tz�7E����Um;Z�����6ow�i�����0�u�yS���!�q;�,���u�x�n����IUt�=���j�N����y�mK'����m-��b��s��2c(s>���Y�uj���_E�W4��7j5�4�2����cR��c�2z}��PV��~��65k��2�f���ut{>=F�X^�i�
���"����V��n�z��>6�,����fY��+`�����?�?t����1�E�A1_,�*�\�w�X�V����~�m���}=??�K���9�(��?`���n����7����nU�y~��Q�n�k��2zq�c�w�:���1�?��Y^�y��9��-���!�������=U��Q��Tf�4�6<��A��aL��~�������0��O��sP���cR����^�����������8z���)�� ��G�uN����X^���0���J������\z����H��3!"j��zi\�J��A7���aY;�m�l\Ey�<�
�\�e�{>F��A��<�g_������c��5e����^�[�1������y]o0��9X�9�s��cb�A�fW]7�=kkk�4<�7����s���� �.���<>��U��9
�����K@�Y��~�o�y
l�~N��J��U;6e�;~D�!Dp�E�����N�>���I��q���qh%�y���j
��4�j��2�b��6r=��a6��.{��.�H��X7�
;=�:������EC�"��?�Y�X�A���f����@��v'�4��s��|e�)���i_����~_��������=����{<���A�;�'�A�n�I�uh��8��'-�q����2����Pv��~u���n?�7�>�]���:T���j���v�O�`�v*nvvv7�����L�����a}}=O18��V��8���>v�w�������n����b\7:mS�s;1L�j������t�O�U���C����v��W������2��;��e�W�y[
����.��hw,�N����){>�z��������o<F�����u�V����� ��v�(:��v�M�|�t��v����4���a��M���~mb�2���H�:v��X���:f1��~+Zf���j�VC�����i5O}�f_�YV�v�:�����_������!�k�=��K�u(��2��Q�u���N�C�c����e�G����Y�������y[��j�tN���>�����e�:v���g�x�<6�����u��U��0�����s8l_��a4����P�X�V��e�Z�n�#mS��	E��������� �C��v�A���u_�{�n�s�e�=Z��8ts��k���A��^�q��c��h�n�Ah��1z���){������(����u�a����v����2�����g�"E��I�������s*���q�f?�;�e�k����lW?&������}���/#����e��P���YNh������y{�/�;�����2b(���/����A��A,����({nU���[�Al3@U�~��#�}���u�V�~�z�������]��k�-,,��)����K�W�M�lO]�s���\�occ#�q>-..������si������Z���F������F�K��������Zv�t�O[������cuu5������z�����0|e�����q���M7���.�/������m�ct���\k/�S�����cW��n��v��y���9�������sg��&�K+.\��r��_��t���\�q��9,+++��_/�T����{���#�������Y,g����!U��FD�4��B���F�E��2�=��y3�� ���eYE
��6*l�Y����alY�60E��~��^�I�����^�z;��V�g��^�o����n�����\j���X��4��/5������};����vL_�o'Q�^?F�W�����1}���$��)
�t{LB�z��3�8}��v����SE���=����<����[�c�L������v
�zm�5�EoDE�
{m�4lzP�?1D���������o��<��y��@/z]�a���a��~�z;�����8��)�P��7����!s�N=|���I��/�L����{�,2���*����Q����Q��^�����*����n��W�mlK����g�a)
Q�����'��"���������[�c�H��������I�6���'�N��T��ac�C��"������5*�'�A��g���~����k�U2����<��<o6��A����V�=��~��a
=T���zy���5�����n��e^��U�M��kC�TmG�W�Z��g�I<w����� B|���������0���8}��v����_������n�$�<O��_����=��o��[�c�H��������I�jWoh7j�������a�j{��(�5��1��1��@�J���4��e;�m�W�~���^�W��pT�`�y���s����eDov���M��:uX��W��U����<�}�������|U���E�����"�������� �>!"�7�=E����� ��@�����s�E�6�B�3��z��_8��R����8�=U:��=��W�A��{v?b[�=�O"\��Pdx���3�u�����/�!"�C�����0�����5��Aq
���G-�w�����C�a�|��e���/^��}��auu5/q����U7IA�+�;�^A�_��_|��0Q�vMMM�^&�O��3��<�U�����&�I���E�
��	0pE��\��� ���C1D��h����������P�=H[cC��1���1�QD��>5�a�Z=���?�(��1<�H��v�Z��r��g��,6���I��y�	eU���������n���X�x�p����*6n�7�k
��E��z`h�fu���u��P�P4�CU;��
9��w�1��5H����U�����I9�G���_��*�qE��� ���zz���e��,>�U5PT�����3�hu��;N�FoD� D���~��}�F}�)(�I4��u��h������f�G$������%�,>+v�#Q+���(:h>cN��`�������"`"E��h��o�/��="0
;�Ag�s��E�(z��0�
�����7j>cB��������x.p�w����
�z
E��h�C4z��_��z`�W��
���_�p!���R�~�k�0�x�q���j;��@E�yo��3|��H�{�0�q~�����{m�s}uu5�8BD����R.
O��~���90To�
c��.^�X:��x���9c��h�Ezm��pX�?�<:����%�Us�(�n^��	�t�g�����8��V���DuEa�X�x�p0����Fl7o�����^������/4	���'����\{PsC���-0D�d�m��q>wK0��2	���I�sYc���k�����3����������9@T��V�9�30��"`�666ri�a�{|4@*��hlUo�������U����K�Fl�6�,:9��zRD�G����|��9%2������`5���9�3����>����������"�:6�|)
?�{�0<BD�P�F<�l4�������R=@�di+�l�GQr�H�d�!���s�jA�����X=bTK�3]+�|N��Y�D�$�m}���1T�sP�����\�p!�������!"�Ch����5�<�FwE
����i�D��~����Q}E�� ��h@	���o��x����z���a�*�f�����}{������X���'�����z�+����V���p�>cF���~%E��E��*;���q=6�������'Dp5>D����kkk�4E
|�m�W� K���Ut�����]6��)z���c4�N���
������mQ������n��<����EQO��nX���9�0�������_?�����}�g�=�T����ro{U|��c���W���!U��'��W�8���Y�
��m�OC�����g��m@�����s�T-�@u�����~����{]��~���A*��^�S�x�.

���}���F���~�C�V�k���~B��3f�ka���9V�=q���A����8��^��y.X0:BD�TQ��^�D�����V�!���5����`�����9�ih
��P���cE
�{i������G������1O�9�o�emS/��a�o���^t�YQC�^�>�
N�{d����c9�N}��S
��x����[� T��w��M�g�Pd`����h|��O7
_;5�,j�����T��YE������Jc�jk���������������9��s���7��mC�v�u�������mS7��N�C�����n��s|��5D���0�s�����
7�;6�z8-��X�7!�1�+
�t�]��>�{M����;�����k���n���	b�����k�Wo���e4�j���X~�G<^���]c��
��1b{���W�x�~�v�D���6�d������f�GSSSm_7����9G/�'B����_���2�ue����n��<�:}�V��i�����ow��]�����1~��������9t:6��^�[�����8��Y������Xf<F�k��iw<�e���>���TM�������,�n�!"�C�]�h�����Q4D�Q��S��n���z�������|��2
���
���6�c����FN��1|���ql<o��y��m��/��V�R���ph�p3�_7�����G'����W�+:��w"���N���9U����^uj��������2�u�t�[t�b�0�:>�?��I���96��c��^S���S���y_������x,����Q����j���|l:.!"�C.����h�T��e�m�W�Mc�V��:5�
�U�C�m��hn���9w��l�1/�O��-�S���P?�^gbEAE(R�����F�6`�n�SE���g�������������;.��9FKKK��Y�����z���e^��9��!�qT���q>6��zx`x���5X���-� ��XF���B���E���i��N,#�IlG��4��������Ai;��z��V
O�4��p���&���s��8w����{���6��s��������`�����~�#�r������k`�V��q^��r��M��3��3��������;��&���=?�z�!"�Wo�k#��7��O#�A4��u��rl{4R�et������9��y[�a���s	��_7�Qo0Z�u����x���=/����{��s/�������K��7��J��0�?z9o�
�PN��x�>�����|��_�|�>v������w�>c���uL�s-��J���zl���s	�A"`�h<������QOQ���?�n����e��H�'�I�N��������������a��m��p��y3���8�:������P
F������yX�+z�;H��������amG�K,��.14k�_9e>��U�*���&�q�����~|b�~�/c�������Q��o\������s�����Q���\������G���~�ib�����'�zl.\��KZ^^�%ij7~���������k)�������\����������555�K�Ec�I
@����q�'"�pBDp�n���K��>}:��[�n��~�����O�JX^^�
/^�
�����y7�_��Kt���^������K0��������������������M4~oeii)�h�������b9�,,,��?!"(�U���^�n���K��?>�hT���'�����Z.�d"�>�&�I��*�t���\���������Z����%�BDPBQc�h�^��E�������z<����e��v#^����r�AE���"��1���Z���XD��Na��}(�k�Q�������5Z�v�Z.���
��C\�V�4E=����\��1���\���������k)�������\�������7j�)�(4�(�/j�~����}- ���~�#=@I���l��X�>t"@��x�l���80����� Q���e�&^;���r�DL:!"�R42���e{%j%�C1�@���?�vww�
vF)�!@�����_�*lnn.mmm�ZJ���iss3�����u+mll��7o�L��_����/--��0x/^���kq(z=�����y�!z��0�����s�*q�
GG�-0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	�\�x�6!"������[�n���#DC��$!"�-..���"�!Y]]�%��%D=MMM�����1K�(D����P
BD0Z^^~ @t���\88BD��z�C��_�c��C������y��"�>mll������t��������,!"�E�C����c�A��4??�~xH�C@M��'�*inn.mmm�ZJ���iss3���n���s-��FU���������\
���q$DT��0��q}��H���z���y�����O>�k���0���[����{�����&D����N���|�;ijjJ�&���B@��������\Kivv6mnn�T��[����b������r�`E�h{{;�R:v�X:z�h���������p�
GBD@���8������/��8r�
GG�-0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DC��������������"T0jBD0���`�	��"�	'DL���)C���&DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"�pBD0���`�	��"�	'DN�&�L8!"F���[����ijjj����\?J�x�����c=�1�e�J�������bZYY�c��~�zm|=h3L��x�x�x�f1>�3B@�~��l�~	0T��M1]L?��G��"�S6�3�e�CA��^y�����������Z�gOL?��by��q����c��#��y]��}��l�A����L677����r-��������kT���T.��0���B��a��4���V��@����sm���#t���\�o����������k)�������\��� ��DP��W�8�C���PQ�(������z#Z]]��=�)
���u�^�"���0�
0HBD���J.�)
�����k��W�iT����u��������l�A"`��{�Y[[������riO�����q�V��a.`������u���s�������`���O�:7����l�A"`�r�;7o����(fj�Shh�����O��������0p��_���BA�������+�0�������
W�\I����c����X������9���a.��s���\J���3���KET��n�h���������k)�������\�����ri�'�k���Zg����z ��k��[����b�u�.���<�0�=*;;;i{{;�R:v�X:z�h�M�Gy$����y�������/�Z���w/�����t.���W�8"*�E���O�����iee%����u	���iaa!�Zk^��j;���Qh��n��q�������DOC��P'W�^Mg�����y��s	`�<�����'����s�
GG�-L��4�����"q_s���0���8u�T.�{�n�DL>!"&Z�=�4��O����qq_Y�\6� M��'�*inn.mmm�ZJ���iss3������\J����k�r�����t���\KiP_[���I�2�����<��c����������k);v,=z4�&�#�<�K�s���t���\k����������{�0~���si4\���Py.���*��B�����k���^�p���P�v�������������ai�����"��5��a9w�\�t�R��6!"?���0���[��TQu���4�u��x��O���l�����������Z���`�����^��h�~a��D�'z]i"�����[�ri���|.����^x!��E�EpP������\�Ns�������F.�dB�\6#z#z���s��K�.�+W�������N�x�b.u� {��y�f.
�G�a.��S&Ht���Z�T&�3�$D��5�f�Y]]��=���gyy�6LMM�3E�H��_������l�S��|�z�j�Z���g���;w��=&Pdj7Z�T���\������fgg���f�QU�YYY���������"\����k)���=��Q/"���)�5��<�\�(�������\Kiff&MOO��d�@�(D�B:h^:{�l�u�OD��~�#=0��EP���Q������\�p!��D�����O���s��$D��4�d"(=�Ex(�����]�E!z�i�4/'B<�i4�z4�|N�>��7�a.`��v����477����r-��������kT]sx���\�v-��5��}�m��Q'e�c����������k)�������\��f�t���t���\;8W�^Mg��������<���q�'"�*�2�=���������k��2�Y�a.`P�������z9�M��M�� NL7,���N<N<^�X����zs��0�-�*lnn.mmm�ZJ���iss3��^�������\Kiff&MOO��d�������r�J:s�L���W���g��Zw�DP��W�8�L8=����`8�D48������u���\�����s�
GBD@���!DD#?���0���[`B	�!���v��m�V�!"�P_��������������?�|���r�v�������&�L���z1���0����W�����<vO�c�s�����&�L����������k��t1=0���F���s���+����;iww��!�k�@Y�����������?�X���O������7���H�M�`����]�t)�9s�6?~<O���S�j�F�H��v���7��@t�����{&}���HG�H�����n���|��y������?�5`�I~"�S���������b����������'>��~4�����<��\�s����%`�A�<����
u#�=���fw_�f.����+���1]����F.�F�`����~zjVD��� u�~o7}�k�C@8�H.��8������r��#D0@ F�CE�������������R{��5/�BD2�Q��� ��'��=���v.��<]�r�� D0�eD��{��\��z��T���?�k)}��{��^�t1,�<BD����������S�r��0���~��\J��o����y+�Z��c������"D�����=����ajj�6��};����7:���F��k/��^�Za�(��{���������\&�@�����'N���/���b��/G��D���'����WN��\K�{������K�[�x9���7��}�v���?�n�2������D�J��Og�����"@�Mp�H����J�����g�����rm�;�~#}��W��_�J�6��b���\BD}8w�\.�6�Q]���r�J.p�������z$j'�����&D0D�����2�g�~���?�o~2}���c�D=���sNDpH����S����W���`��};���O}����w�������I��?�\��~~�v����~ O	L:!"8:2�~d���-p����'s	���9s�L.=�����	��]h�������+�6����%(O���/_������Ab9�.]���:���@�����K�E������:���@�n���K��G����t���Z��L�D1M1O�Qx��r	�����+677����r-��������k����[������m�y�f.����T����O�2���N������fff���t�M����\��?~<�F?��'"��&��p��7�a"8�����<����O�,:�|�I�h����2�
���W���gs�{~"(O�GG�-���/�����=�����k�����\[&P]�/_Nw��������'@���Py��On��:�������k�6~��P���iaa!�F�~�
���V�>��lGOD�q���t���\����b������0�������0�iZ[[��F���U��1|r���ZoDE�-L�)����G�
����X�~�p�BO��N����2���g�A�A�����"��PyU������\�/�"��}�,*
���W���F��Q+���n����V��T������k�A��w��'N�Z��DP�0�������E�V��A��i��U���9������k�e�=�u
{�V!�����\��g��$D4|���K�=����D��}�����"��PyU��L������sm4.^��/�S� Qs��h�N��V!���R����m��p���R�>�c&D4:�E�C/�����Cu~"(O�GBD@�U9Dt��f�!�*}�k���S`�9`3����C>��~j^�v�7�T��a":(:u�T:y�d�E`(��};��qch��FUz��:!"`��t!1r9�Q�u����NU����K{:���|���j�q��y]"��J�E������\�S�l&W���=�N�8��a���>>�E���'D������A�����v��C81sH�[���z�=n��|���\�occ#������R�2�����Ns�;E�6�!�~�]��8mc 
`	0t��������S/=�5O��
�=��h2�;w.]�r���
Z,���;�����!�@+BD���7s�?�Z��4��VVVr����b.1IN�:���9�r��-�{�������T��[����rm��������f�>}:��i������t��M�����'�]������������ws	�"��E�(z���L=4���M���qS��O����\�����0P]l{c�>����a�����t�������s����7���\���������k)�������\�����#�v�Z��(�%�Do7��cM�6mw��}���Zh�E��lp'�?�����X����TU9�;;;i{{;�R:v�X:z�h�M�Gy$�����+���3�����W���gsm0�=���?�._��k���w/�����t.��8_�/!"����"Ls&4k5M�UZ��&�s��	5�w�����*����Q�����NU�gs�(�M�P�8y��gsi�F"�s�N:~�x����������0~�����O���	�H���q���h�N�4�=�4Y��7�*����Ze��(��j�V��������,/�1"j���s���S�����'C=w��M�o�����{�:!"�0"�L���q���ii��U��1������R�(4c��S�Qh5O�Q�������	u6�?���@gBD@���E�2����J��g�!�q�
�O���~�VQoA�nKKK���|��P_�V����e�^���������c����Gsm2<��#��������K�6z
��w/�����t.��0�������0�!���]e�2e�U5U�� Q+�un����P?a�amc��CD333#��:l���Ww��I������u�����
���qt$�0b�!��0\��D�}���^*����K{.\��K�5�~.^��KT�/��K�w���\����@sh�9T2��:�a�n
#\�E�B�C��b|��B�4�h~~>�����Q�y�f.QE�/_�p����s	�"8����P���'�O�����zx�W��O��CoDt:{�l��~BDC���Ns�6�1(��������+��4���j��<�A������K{:���n#�)z#�z�j�UO��!���"������j.�i�vA����\:�@M��7���"��
���������O��_[[��be�1T),EyU��'�Cn:q�D-�E�v����477����r-��������k�!����\��E�����Ic8%��Z�G�����q��������OMM���>�v�Z���q=�.3G��i}}�eOD��o�����N������fff���t�M��c4���K�N����N�<��?�k�E��������7n��}�����q�~ DT��^�i���*x�*�0�@�(�
R��������'���v
�t����.s��������\�r%�9s&��������Q3?�'D��#���0I�J�N��)L���4nK�m"t�J�0N�Q���8i�w�� O+�>��T��e����j�j6=�7�������\��.�8����Z�fB�2n���/<T�������L����!��vii)���(�Y��m���IOD��'"�����Mw_~=�����#����JOD�8"*��]��P���F��x�EQ��H�`M7!��*H�N7�n�1t�(
��s���S�rm�7n���/�Zu��`2}���������_y#}����cS���?�~�����?���S{,��"��Py.�L�V=25����nCD!�>���m�D�KR��2A�X���������D0y>���w���\+�WN�����gs
(��+`	��"�d�������N��u�����i��U	5"�Q�Dt���t���\K�������W���g������d����[�w���s����O����2��N\���Py.��p
N�z��9�k���)����^��OD��UD��������>��}�������w��F�w���<���qt$���h���TE����r��������}�ko�y������I���'��G?����#�����#��p�2������(��I#D�����Zp���D��.��_�c�Iw���\z��s�r	����?��\���KO?�k���G�X����O�qL*!"��>}�v���\�xq�a�E�H��P}�8�����\J��=V ���c�����\&�@�]�����r-�������b������'O����:u*�8l���n������>p��\j�q�/�����gj�=�PIsssikk+�R���M����v�"0����/�S�����������&�C7o�l������~��������k)�������\�����s�Jr"�s���\������������v�Sw���t���\+�OD����_K�Wn�ZJ��'���������_�J�����R���'r
h������7a"L=�S��(�EE666j�
��Cu������tO���q��D��������������:}�#�V��������+���?�?�:��wj�tBD�8:�o�C�
��A�VVVVjC�����4*
�c�c	��=�������O}�~h��o��������`2	P{"�������������\�|9��{7��C/�09~���r)�w��Fzg��\k-�����&���{&�0Q�"������k���������T}�x`���[�si����V$���^�j��9��8��I3���*lnn.mmm�ZJ���iss3����[�j���f��������N������fff���t�M����\���;w����aU���W{���OD����^L�����=��������>��}���������(���*}n��\����W��!DT��00BD��;w.]�t)��)z!:q�D�u�OD�����o����_���~�O>���_��\:q�
GG�-._�\����"@��/��Z (z*#� ��'DP���gs�:"<����(�N��s��~���?�o~2}���c�D=���s�60��v����477����r-��������k@�vvv���v��433����sm2LMM��`�;w.�:u*��;y�d:~�x��A���o�Z�n���K�
#0�'"��x��7j��=�X��J���n������'O�wf8l\���Py.��p
��+W��3grm��)���5�@����/�_|1���+������{��G��O?��}����O�6��_��H��A�������#DT�(���/���t��w�M��������?�c��41m�pP���t����?���>&�?���8}��_��=�����c�jC���1�A��}O.T���\������fgg���f�����I��������L������055�Kt�'"��^z���k��k�����G��O>�~��<f�;������o���~;�I�g~�g�3�<�k��r�
Gz"�.����ri���?�@�(��'�x"��4�0*BD��W_}5�Rz��G�?��\{P�R��5�0JBD#t���t����p���������5�y��w�y'���k����P'������Q"�zp(B�.]Jg���
���S�w�����:(8Xo��F.����:�����=���7r	`t���$?���zu����m��bY�L�G?��\����~7��E ���O>�K�#D0��P~��6��Xf4D�+��E�BO<�D�����|'�����;��j�>����0:BD#v���t���}C��o�SP?=5+
"EHI�`��;�K)���[������E�(��k�`���F���^\\L+++��F���������`�D�h�$����\���k���m�.��������y^�Q"���C��~�z-L�g"��		����L��g>�k����/�\}����
Q�q�������8BDC�E��j�������({ j��s����S?�S������=o��fz��WkC���KKK�0zBDC=5�>t������������������s	���
�w�����N�:�K�E�IoD���C�����K?�?���ib����IsB�v�Z:�|ZXX�c�����Gu���R6������6LMM����o�{[�q�F��Q���F+>���?����3g���>�}��|O��c\����I�`��F jj%G�A��/�P5ez����'����kCY1m��N;���7"F����t���>W���/�bm�r����@�`666r)�����DuKKK�����7s	���w�v"@�Mp�H����J��x���j@�L��'�*inn.mmm�ZJ���iss3��)z�+���q�v_��N7.�{T����9d���-�1���?�K��w�n{vvv���v��433����sm24>�%�a���K���N�+W�<B�z�j:{�l�=��kA�y�����q�~ DT������v����r�~�z�=(zlE�&�?+++�����O�.�
6
2�r�V�h0:��:�C/!�;w�������~BD�'D��#��"z��E�Q�`OL�C}=��B����b-�e�:u*���a�}�v.@9BD6�@�(E �Q�����f1���;���u����ja�Q��`�����1r���\����F.�g��9��p���t���������muu5�������k�]_�X��u� Q���w;��6��&���'s	��,--�RJ+++����=�q��e��X�����������z#�P�z��,,,��~�����+�h�p�B.Ayg������q#��>!"�!��m5���Ihsm���|.����L��v��ToD�!����Zi^�~z�j�h�u)
31>��v�?��\��k�q���\�����dmm-��z�����F��PL� �e�[����2��Y��5��Qv?6����7s�w�.���n��j�|�r.��
*���t�R���i}8����$z#j�D�$�D14���b\���{��e4�lTu�a����\j�������\����mD��9v���\b��{7�Z��O�=�	uZ/!"�!�v��A�V�B*1o,c��
A
�g��RsH���i;�"�N��������};��E�D������;�@Q���b�b�N���/��'D0d��H�b�q
��y3��3���[�n=�����,Egg��-�����k��E�(������&������/��'D0�@@��w������������4)�S����zh:���{C���d:�^��^��K� !"��`P�.C����aa!O=���i��i��i����h�/�E/@e{#�x��
	�L�F+5�
���K[[[�����l����5�hjj*��z��TY����BH�|m����b����T����/�u��y�Q�BD;;;i{{;�R:v�X:z�h�M�Gy$�F���;�����6�?�|-���{�����������p�
GBD@��3~�	5o�����.�����G��Q/�=J�!�X��uw�>�l.��(�D��_|1�����?��|��\>���qt$��D��O��������\�]��E/H'N�Hw�������G�k"&���E�6(����t��������
�}1M�{S
����%�E/A��rc�P���{r��������V��4;;�677s��"<=�"�R7._�O�Ps����� �9����b�������k��?������v4?^�BT����N�������;��=�k���G���u�����s������1��0�/�0�������K�gzz:�Fc\�_��Py�zf=	��(H�J}���� P�<�HZ__O�V�!�����_d�*����S�N��'O�1��pQ�����o�7n-8��OD�	�H���q��"�� J?���M+U�5/�Yc���Qsp��_���h�'"�����q$DT�8^�i�T�'�a�'�8o��nD�������c��sQ�c�<_7��Q"�����"���|��QXZZ�����%h�"��8�mll���'4!"�!;l�����y�h���0R���@e����7si�a<�@�L��'�*inn.mmm�ZJ���iss3��ijj*�R:�_�����Z���N.^��VVVrm0��L�PP����zzY��8������k�r�zvvv���v��433����sm24��8w�\����S��v��=�K��'"������Py�xfyy9]�~�V>�_��A(���i\���l3����
E`iqq1��!����P����9��t��������C
��`0�x����c�=V�&�0���[hii)��)���riO�y�i��qJ���=��D<*�{Q�v�(z>�p��P�s.]��s�(?~�6,���;y,U��/~1}���O�/_N��K�T���>�*"��}&�'�1�r�D���Bx��D���|�z��pV�P�����)�(H���PhCA��O��-E&�r�J�A��e������^��^|����[o�{R�����F���A��u����������k)�������\���@J�b"�R�G�I�*����j�7o���[__������riO����!������Va�2����q���{gg'moo�ZJ333izz:�&C�y2
 Fx���w��'N�Z�D��w�}7����������<����z*����lz����`����+�p"*o�/�4�h��+���;O���
�t"
��D�t 
BD�3��(Du�
���;��������������p��?X+�;�Io��f�\���|&��?�gr
gBD�8"*o�BD���p/^l��O]��]��k�u"
q<VWW������^�Z�����!��;��� �D~"(���^J��k��k)=z4=�������c����;���Fz������~�g~&=��3��+!"`	�7�a 
��.����F���%���F�.�y�����{j���A,o������;�����6zW�^Mg���������/|���������?���{ j�W^y%�R:y�d��g?�k��"��Py�xf��+�$D48���K�.]������z}(�����k��V+?����^������z��Z��'�H���P+�K�GBD@���E���fee%������z��G�{�a�
��+W��3gr���9(��q�v����S�����nz7�{�n:q�D�u�OD����;�o����k{��������o��~�(�������?�k�8"��Py�x�1�p���t���\��"��?�Dp(BC�/_�c��&���6����W_}5�����s-���~:=z4�Z�^��7���������/!"`����r	�D�����b�C���?���=�u�(�|��tRf}��G?��\����~7��5��|��\�b��������V�A�`�r	�D�N���b'N��)<�,��)HTf}�]���x��\K�;��N.{��wr)��}���s
�{�����|��K������?�����~}�v������)�{BDC=
�/��K�c���RJo��Vz���s�A2�i����������?����o����_���;�b�gO�c����'��?�����[BDCp���\J���[�F����Q,�SoD���l.�y�����6T�^��\��</��~/���&}���q���_��k��|�
!"�!�p�B.�����K�as���\���o�aff&}�3���T�����_������o��(���^����j�B����/�{��N��y�Rz�XJ?���m���1tC�`�������z#�	v���\�������������SO���7�|3�����!��b����\�=����������������#������Z},�'~`�6�����_���P���\�v�� ���bZ^^N/^����q���\������z��������������c��41m�����_��=�<���>�+M�z��F��@;S���e�J���K[[[�����l�����jj�����~��Q7"���������s-����4==�k�ajj*�����;�����v���w��'rmp�~��z�j:{�l�u�OD�����^|����+����z�6��GMO?�tz��g��?���8h����O�w��\+{���e�j���_M��7��?��������*��8^�"*o/�+��+�$D48W�\Ig���������K�.]�������{"��0o����x���j�P�������_*�R���S������7�J��_�����_�r:��'s
�Q"���|@�����u����h��D�Q�"'�CD��}G?�K{z(`H�DT�8�����r.
��k�r	��'��+��%F'N����DD�=�\:~�xs� ��'"�/��"�s��Z�c����'k���_M�������|"}�/���
#�'"`	��"���E���������=��i������`��������7����gRz��������t��\y�c?��J��~!�%���q$DT��00BD�"���S�N�3g����"�s���t����@Q���,k��'"����������k)}��{���z��E����Ii������G������Yz���&�%���q$DT��00BD��\��6�SW�^��=��I����K����_���\�������������{�}T��vJ���|g������Y�����0���[�L����/3��_J�<����'C/���|}��9@��|�
!"�25u$�������O����b��>��nL��'�*inn.mmm�ZJ���iss3���[�ri���B.�i�P���������s-����4==�k�ajj*��s���Z�>�����������?��{��������R�y�L������>����'��t��\���-���*^��D���*^�i&4��5����p�Q1!"��}���J;o�N���LG>��y,U!D�#}�TL�>��Y"F�&���{r��������V��4;;�677s�`��u+��,,,�������q�;;;i{{;�R���I����6���r�;W�\Ig�����q���t���\��������+�N����s�C��X��N�<�k���^H�/_������<���q$DT��00BD4�@y�_��H���n��7@�	�`qq��acc#��j"���/})���P�����@y���Aq^��@���������X�������?��y�/��8����BD@��$"��"B��A !"``�����%HD/��K����u�V�x�b����7,//���R<^<n����X�~-� ����
"��"`�"8����VVV����_�^_�S,?'/�Y�����O/a������ua��$�"����}O.T���\������fgg���f�����>}:---��p�?>��GrZj����v�Z�
N��������B�����������������\Kiff&MOO���|�K_J?��?����0��=D�����+�N�����z�h���+\��=�4j�D�@�;O�A�kZ=F=�U�X�����+$j��������eT!� HD3"`�	�H��<!����+\�~*
��
�j[�|5O_�g���zY���2D�� ��0���[����Q�`M���5������\���v�5�����k��"�������v��/�����'IF"8/"���'"����QB���rm���_���w�2_?#����A|e�e=B�|�z.*;]�zoD�0�����e�=����� &����q$DT^�CDe�#�I=$SWvE�C+++�����u=B�����\f�*;�Q$:|��I"D��#�fcc#������R{�c5�G7��9I�n���K{=RQ^I"P�&�0���������n�L���[XX���4�t�l���V������RJKKK�DY�D��0H������Oo�����������������F=��}��<%����~�?���������r���z�q�z4:��&�0H������/����t�������ky�������_���t<H���k����PP�rF�����`Ts���Q=8������������4�D�	M&"`�����k�?���r���.��n�~���G_�k�0��v����477����7���M����v0"$R������?�k���!�k��7���Mc��������N]7�*z
��O]�v4/{}}=���v|�yrm�vvv���v��t���t���\�����M���g�K/���0��y����/|!����h�����������\���������N}�hz��o����Nzg��|��_�����p�d���v^������W�[/�n����ylJ�|������h��O����'�X�nzz:�F����:"*O�h�T5D���4�G���!�F1������"��U�� �D�!������ ��{7����|z��W����O?�~�W~%?~<���K_�v���_�C��t���>z,}����c�������~�������cR����i��#�����������V����_N��}6����<=����6|BD�8:�o�"|3(����D���u��P�z��9X����F,���D%�(�"`�����PiQ�(��������o��Kp�7����T�(�t1=L!"&Z���(z&j$���z��LOG���
6&�D�I������\J�������������\�=����;���#��?�H?��k�Qo��|0��v����477����r-��������kT���T.��4�^�C<�������@�Bkkki~~~_oE����y;ZM�l����
E����|��������s-�c����G������������~6���KyU��3��/|��G�G���������_��Z�B�Tz���r��;��H�y������_�����6�������/���\K�����O|�����
�7�~7���;��?��Y���?��4���,@UMOO��h�~�#!"��\�?����?����*H�N�`�����<eBD��k?�hP�CD333#����������?����������o��o����c�K��j��������&=����S�~w'}������7~:���5����.����|�����m �� �W^;�R���������\��s�
GG�-L��D�?�D�'D�CQc�hii)��D/F�b�^un:l"���P=D�0���Gsi�����h�y���px��_���>2�P�Q����j������S.�x"`��	�����k�Va�X�a�
[U�Q	U�0l=4�N�������x'��k�.�������M��W��k)}��r�G������r`\	0T����8�0�6�0�����!�C1����[�����!FK��Z��Q��O����f�����k���1]]��n���~�}/Du��5/����	|\iy����T��Y�dy��n�jCC�4H��f@,V���
C�����f�O��x2�@$�0�r	kH�t�l��R�t���mw[�Y�dm%��[�9O����J������S��y�{��J���U�zr������p�
!"��kii�����;�=�j���������_/5,�EW��$��\Oo��l�&G�R��'Gd"����^�,��U�<����Tf����� ��tU��k��������������Vk�?}q�V�w������G����hind������������A����Q!#Htc p����^���&�Dff�d|�W&�/Ht|Hf�"�V����2�����}�U(t��b�l�e��Xt�F�y���zr�����?�����?9#�/��Kk����������7����C�l��w�Y���V�23i��I��r�w/J�=nd��\D��� @�Fy���#�wm������%�r��j����8����Dg����O��F��u]��xr����|����JO��z��`Mx��h g�.=������J��{��g����Y)�r��g���=R��$����>�'��:���t=����7���;M$�:1#�W�V�����Fd?�,��S��r+j�b�V�����j����H�7BD�5�����7e�F�u�����r<x�F.�J���_J���_H��D����a�BC��� @ [hg�/~�u����!w�Tk�.�u��?�t BJ���[_��U"��99seR�M�����M�:[�������������V��N_�����8(������ABU��$T�l�������t;��t�*�&0cc�J������m�HSS�tuuY�l��������f�#
�tvv�]�J ��+��[��}��9�"o'!���D�����V��)�
������D"����J���Q���U���'�����O�y���j!@ ������3�e���R\<�w=���y���jq�w��{��Y��~��G��?9c�8A�`�������;k��+��C��u��
���Wr!"Y�ar[��M:�k�"J��h1K
����\K�"R�V"@>;{�3����*5�@�}��� 7����#g.9�`E��qh1���s��e�z������x�
@.*�-kB�2��'�n-�5�
����Np'��K��� �7t���_�r��N�AM�5����R���f]Z�|��� ����]��P�����;}a�9��C'"Y�Or���������c��ov����}2��ND^�����S8`3.
��������mfy�����x�����K��k)�:���h�
����?��T�|���m��s�g@����D�7����D���2|��U"����XkH����"BD�/�k#CD� �� ����~���J�b�V	U��*���A�r�*����Kq��p����\D�@���a���ZYi� ._CD� �� ���������0���5RY����������3�e�z�������E��d�\|&X�Ojmkk#P����"H�9D�?��k���r�*����WY�P42"�}g�y��v����V�T�EE���Hkk�;v�fxi F�1�Aj� ���E���5~��J&:>���z�o��|C����}��]��t]*G���{�Z�� Qz� ����N����V��NG�NC���292(����V���=��8=���5V�7��`
>|���������Genn�����%>�k�4`����~�H;H� Qr� �~��r����rE��d��y�x��j����8������v�SP�����/{�����t���A!=G<$���~
y�D��`!�D� ����^<�#Q:�N��o��`
;vL8`� Z,<���d�,$�ih	@j�\��phg����+����!�lYo�.�u���z%��A��@gg��D���� ��c�����ZZZl$���a#�z���[6�������?|���_�^>�{-�Vk���i������v!��{���n���6J��?Y�"j����( ;k�-��C���r��VO��������p�
!"P�
%HD�
��KW�������o�+����{>��l�����l%�|G����}�l$���n��;v����[l?���=HD�
�����?�����?9#�/��Kk���������������:t�F"mmm6J�\��|
 �����~ �=�s���u�@~#Dk����F"G����V�2����q�w��Q���{m4�kX�| �����}��U�`E�Tl�*��w8[��t=���F�������u�����m&�c��-�={��(��+Y�@��%HD�
���C�:��~�T�5H�j�������:_T��nG�3��Z �"�5r��A�4t��	�E����>�.�=z�F�������7d`yr=HD��������v��UV����6[���O�@�!DkDC=���W<P����Cqzo@����F	������ "�:�;h#�`EM�Q���uq���@�!DkH�?sssN���jkks��w�w JT�|�$"@P3�sr�b"T*�Qz�u�/:���u���4���t�"��k4��~��/�_/z^����� "@���+6rC6J�����\G����k��k����	k/��D�^�7l��k&:i��������
D�~�$"@�+.
�-[�[%259n�����x=��C�`�$"@H���56��
I42bUr�_��y��_d [�D����E���5>p1e�H���\�����f#�&0cc�J������m�HSS�tuuY��;�l;;;��r����F��E"����J���Q���UH��'�����O�y���~2���'��G~n�+XQ#��r)�d&:)S���:���{��~��V�t���+������/�hx���Cr���Y��a5"Z�$"@X��=�y��KV-��[7�x�U`1����"�V������j"�C�<��`��@��T��B��u���G�������$"@X����S>�G��W>o���e�����������:�/0cc�J������m�HSS�tuuY���]�����d���V-��={l�\$����D%[�L=��r�}��3�<c3��`5������+r{�).
�,X�\|�
�z��"L{{�8p�*��G�B�!D�z�"HD��!"�����UD�(,����V"�6BD���a��D������`�"�5�o�>(4�
 �Z"Dk����F����c�}�]Z[[��}�_O������^����+������ "�5BD�<h#���.
����W8`3	G�q��G�F��_G��~]?��������!�L�D�p="�5�g���o�3��J!�L4��,<�����ka)�C�g&ZnW" n� "���M�X���-�!0cc�J������m�HSS�tuuY��4��x��������S
�h�����N�*NV�pO[[���O����������s�:��M&��9S�w�^��k%�HOO�U"������jz��'�����g�y�fX(r�����_�x�Od|�Q�)��G���'�����n������_(\��d�\|��y(U��x���8|���rC ��+U 'Y�h���&;w����~����!�\�>"���A"D�.>�a��Z�5�5�����
7
!"������/���3�%������,����Y�nD����$���.�]w��{��(�%BD�_<HD����}��~���U7�\n�w���@�@.*�-����)]G�~ow�T��� R�t	�_���N-��#���w�XH�C�x]z��D�U"��Evnw�Z{�z�\�R��@���Or��7k!�L6XnW!���v�YIw#�uI��9�u��NDpc�<&��z�U"���s�z��J��9wYdr�&b����Ix�s���D'"��ND�4���\����iw����I�Ah1����8���a#��I	��\��Gm�J R:���
���_���"��={��hi����i�)���Hf��#6��J �[������b#G���V���n>G���HKK��moo���V	�.:�������NK��cV�T�m�����Genn�*�#DXu�`�JBA��\o��C��4,t�����]������@a�Ot!R�e6X������"����w�����v���P&4`�a"�DP��7>�F��	,���R	����Rss�tww[%���$]]]V��I�����6r������V��'N;-�z�}����������N�R���z$;VCD����kii������{=4��,dt#��G"����J���^B��U��v��/����;��*���8���� 20����?Wny�������a]���,�����/�xC2��������S���������f�������g���K���L�F^&:t���c������'�H���z��x�Oe���V���U����$����:oEL���K���
��m��A����Z{����"���44����4����K�1(��:�!�IXJ�Kz��~�t������b#���"C�V����>+Li��m���D �e�'�i�dj��R:��H���H�;)��{��y]��u �[��H��unT7":��7y��=�a�\�U"a��2��	�������4�Jp�;���`q��d=^��=+	�?���5Y�(�`t���k��f�H;iG���<�R�CD���
��\/g�w��^��jq�[Zd����*�p8l������"BD�/���l�t�����<x��P����-����F<=����������>,������.��~�*�(�~ ��U�A�l�! �0�� ?��:��'
p=m��A�u��Rw������ui��������`My�
e��~-�H�0�v��^4<��4t��1�v��m�����+��U^{����O���&������>�l�������J��`�����hi�����g5tvv����;�^g��`9F.�@N�
�����G�n�<����l�����Gm%K���[�,���,���V�455IWW�U�!2�S���)'�����et��~��w�U"G�M�Yk�����"i� �T����.�ji��l���j�D"���c�Hcc���a����lTz��D.=�?l&�����l{�_J�(h3����+������/�h(���C��(+�KO��!�d!�d����W�����
3%;.�5����\BDp������z��<j3�+��Gv��Ob���l�!"��U�5��V�(�h�&N��?������{�J�������������]�����F��������GDu�D�������j����8���o��B�����*d����k���6Z�����L���KP�0�_�����N���������?a�HEHd�v��6�l��]�n���������d*0cc�J������m�HSS�tuuY����E4p��`���M�koo_��G��x�H������kR	6r�{z�_��4���?��vtt,�t��a�����T�{�=�u��-E"����J���Q���U����w������*�]�E���Hb|B��Y+bjn�
��_�
�S.�~��d�\|��9z�h��VK��L:�k�"J�ZL��C�}���%�Fu�D�������mMM���y���It����2���0��>�>�a&��y���s\W����"�V�
�hg�L��������
�db)�C�����������������?,��-������uN��L��� R�~��w��� ��`�r�(n���N� 
�$���kt�Z��}��$����y����zL���n���<&�����[��o|����O�����g�s�O������TZo#Wq�
X#�9^����������*���&����*;�o*�Q$����D%�����733#�������K6���M���o}�������Z7��D��aZ=}"}C�8X� ���sn��*_�:�H���?����/UWWK}}�s�����c���f�\�"2>aE
�? R��X!"Xc���6���g����c�DB��444Hmm�TTT8�����c�X ������u���X� ���~/���C�����m$���a#@>x��Gl���a��A�tN�D^�cQ�jo�l��V��M��8+��%��C"W���:����8=�L"�5�g���o�3>r��� �����H���J����ZH;��8���j�����{�r�_9{�<}��j����8������_8p@Z[[	@��F�200`��mh1�5z����d��~,����m&=]���8�"0cc�J������m�HSS�tuuY���a!
%-�����D����*���F	��VHF;	}�S��Jd���N��tFFF����*������q�_�����?��ej����N��HQQHJ+���{�����V������_!"Y/_�	6Z]<��j"D,�>/��?h��������������u/z��3�`��r��Ko�~�R���.��~�*�(���"z�r����<q������Q[[k������R�F�6�XD�;����(@�����z��ND�^.~�[kk��V����m�����T��|��/�����3��w�T+;w��o��Yr��
6k��_��?~�*��[�J(�j>
]�p�*�]�v����o r��;� ��*������2��	�������4
�����D7���"BD�/�k� �|����G����0�w��I����XkC�����?o�8������Q�LNN���[���oo@�<&��z�U"���s�z��J��9wYdr�&b����Ix�s�����WrQ�m�J�����������4����*qBB���200 ccc�E�:�
��	���/>j#W������V��'��F,� ki��'�[�
����j�Tl��l����z�������i�&�\�������\t��k[ZZ�\c���HmU�Q���~]7���8BD��t���y���K���A���H��F�K��Vk�/�����N�Z����X��������k3��]��qs�����U"a,��n|�Q����
����W��K�����$��E���b���|�6����~���5�y��y��RU�h�c��}�F�^���.D�������t3 ��\�� +577Kww�����������p��1g����l�k���6V.�HOO�U"���g���\oz���nD*�v:-fb��D#�����j�x�3��ehh�����8[ � ��O'��m������r�!v����{�1%�@�U�^���+������/�hx���Cr���Y��a5"d���Y���Y�]�6I�|�wVG��db��U"��V).�� ;������G�qm�����e��"#�����u����!"�����U���{��Z���?�o#Wqi�F���?s�F@j3��2�����������

�Z�����x�Hy�`1��`�h���k�F�f�&m����<@���������������W~�����j��WO>b+`�����l�:w9u�H���Ya6>��6`q��@Vjnn���n�D����������B����&�w��jy���c#`�"�����X%���(�p�*n�7����W�8�`�Z���8�t&�/H42�����V����:c nnfJ����.=�y�fR��������JJmV���>,�]��r�V�T�������O��E�w R���e��Z��-_�BD�^.����.�J������u��>}L��o��J���AJB�V-4=9*�C�V���%w�������077+����)�=�mfq�������%(�X}'��2��]�W��r��U��
7!"��W�`
 X�7�{��\�#}NP(���������W>�F��W���j�K�a]P�U���^�^����� �,����:D�� Dk������^���&�Dff��NC�$:>$3Sg����y������n���*@���&�����J$\Z,�����u�RW^"�%�V�[kC��8=N������e���J��o���{l����?�7���u,!"@�z���#�wm������%�r��j����8�����g#W��R�&��2X����k���n�����]��L�}���|����:��\[	��"�5�o�>`�>�'��:���t=�7r��6��(�pi��Ht�����C P"�/t��BD�����m����B_����5/�C����f]Z����7�@��&�����%�D*J6J��N���������H����FX���o�?}���^+��V����s�Z��m��m%0_Iy��\�E����|B����={d��}����#t#Xe�����zg,FCD��MV��N��(���9�s|Y�6���C��������8 �������j�sm$�76%���A"��[�=�E��@Vjnn���n�D����������
i�(�x�h)4���H$"===V�466J8�
 �\�����g��*�pi�4T�JEp����Eg�wX�F36#��m)�_�:��._�BD�^.�l��x
��D��'��~9����U���R)+
H ���\�����9Ot R[���;��~� D 7-�x=�<t����T6��������Q97u�������\G'"Y/?�����F�����6V�ND���L�S�����;�Lj��x@n���"��R��E'"������0�� D
�������� ��N���36+R�y�Tn�)�Z~G���|�`>^����z��_���m���{����������c���i�k���6Z}����_+S��\3��2v�)��r���m��x�
@."D ��"L~hmm�#G�X�P[[�u	�h����V-�o�>9x������_��������D���ff������k�F).�,�������W���
R\��P��_�E��d=^��m��g���V-����k��h��c5�>��!"��T��|��/�����36+r�M��sG���+�%�o�`�Pg.]������:?$�/���-[��m[k�5/�Mn�\c� S�~ "��x�
�tttdE�d����j�����$iW���i$����S�j�}�}mBD,����|�K��	Sy���M�~�U���cO�g�����������=wZ2��Wr!"Y/�_����?��T�����u�mN��t�r���R��k5d��~����R��X�w��a�z��U�k�k�|�OZ�(L�{���S��Z�=�n�>�b��b�EE��
�h�
��C,�N;�d�y������AC\��^�X�:��K�NI���?X�@���RV�I*6lw�Z{�z=�Pi"�(XQ��i�To��l����z��E��H��7+�
���C����X��~����i�������*Y�
,���W��_J|�oQq���4HY�	��Hqi��j�����qz���A�
���C��#?�J��
Je�v��k�P�z)	�;[�u��$h+�9����V�7��`��D"�_�R�����9�s�d��h�����+�F�?^�G�D~�����a������F���B_��/m�*����P�U��|(����G~a#���������v��UV����6[���O�@�!Dk���F�1�_���^����NV`��������F�����F���N��L����]jG)�B�������~�\�S��t���;qf�F��do�c��&e�(N����S��l �"�5�
�h�d���V��PF'���jt�����?�����[H)�Z0*�^yCG��K733+'�^�J��$l����~��@�<sV�[f�Fe�r��253;'g.&B@��r��]w���s���o�$Y�o@%U�!o�d���6�
��=)��0i������R`y��It!R��!��_w��e��F.�@N�
������=T%'���l�����<�Dz'{!lU��o(�:�y�BD���u��v�������l�TrQ��R�����7�[&��C������Y	�B�k�F�f�&m����<d�������{�W��d��Wd��������?��/����Y$s{��f��
�[�?��@����cQgg���_6���r.��noo�w���������j�����(=�:=^�@6����_{�\z��Lz}O�g����-�����Qz�uz��@�	����Rss�tww[%���$]]]Ve�@ �F�TO�Z[[��N�=��kM&��6���y����/j%�Y�7����*��S��P�h(���V���������]K�O�J$����D���%
Y�����w�7�>e�HyM���*�ZhzrT��z�y��[���4[@v����$�'?m��n]��^����8fcO��'E���N���w����U@������{���J��~��UV-���h_b����.���Z@���6�>r��+ D ��z�(U@�R!D�����6{����|�P�Jw;�_#UP)BDz���/n���qy�'~j�v'*�PU}� ����L�\���)�����;����*����Gm���+?��3�J�"$��)�-�	�����>��I��	��GR���V-���!�����~�*�����o��4H���K2;����C�_�7a�6l� uuuV�=BDrQ�m�H�$K���i#�6
�xi�g1���������i�#����r��/��*���)���������T��j=>t~^�H�#@��&������
_�"�=E�HU���^z|������C��ZS&�~���	i����^����qg�����y"=����KH��{��d^�/�����w��n@��D��A���E���X-���M����*W42,#�d��Yg�������vO��������8������^/{v���e~����P����2|�����K��q�W`.�����������*���&����*;i�d���V�j4`�g��Y�������+o�E���d�@ `��_o�g5�����8�n---V�s�>�����������H$"===V����K(�
���?��O<��U��������o�U\_�����lY??�������V,�����sA������/t�*�7������V��M��������_!"Y/W_��.B�:y���3mmmN����N���X�o���Ve�l�TA�d���{;���RCA�"jll��/��'�^��|��r����������Z���V����r[�z�����������_��+��}�Hu�i�CD���%X����*�_h#����?:%���;�6����r��y��n����,�!"������/��1���R�-���-CDq���y;
�
�bz���Yl���"����99~�����Q�����%���6���:��D��aZ=}"}C�8X� ���snd`fvNN�^��6HqC����"BD�^���X�T�����\�� �b��j���K;@y�c���=��{�ky;�!D7�S��_���'�Dvm)/�"��	��g�����7��W|�*\O���"BD�^>������Ng���L��D� R���R�z����j"DXk'?/O���Z%R�=7��&	�M��\�m'm"�����������N��(��l�����\D�@�+�a�a#�,p��p�����v���tA(����aUf������<xC�b����;{�����?a��n�HyH�( 2{::>)��v����@�����B>�b�/���K���o��Y���g[@� D "��x&7y;
eZI�T4��4��{����:�������^��K^����������<x�fW^{�����������j����������!D(T�~ ����5����
��]�=~%:�t7�s�8f���kA�@;_�c����l3��:]O������@�����'��&��������x;��������=z�h�nDK���� ���}X�u�_4����]6���U('zl�|�\�*���b�������M��Y�|;km@a��+������0���I��t���[P��Q 0�-X��D��Z,t�)BD�TN��+d���V-����r���c�
"���V�|��Z��~ "����E
���<Y�d��]�TGG���P\&�0(��[HIi�����*��(a�D� �K�����J�"����Ld|Bd,"20b;MCs�l�#�����\D�@���a���%���
��ipg)!"�,H��j�!" {����(;544��f|�19��{�	�������T��������"�S6���|L��k�!"V!"����G�(!������'����`�RCDJM�J&�.I������@��N?����f��/~���B>y����'?c���[�������:oEL��o��^�i������E��d=BD	��N�<���V����P��R��?~�����y������ke�@�7�/���\y��V���������w\^{����gn,����\D�@��E`m"�������~��*���"k�H�����>+b�}��%V�"`u���\D�@��E`m"\���6Z���/�7��M�D^��W����Z���!����HN|�EV���.RQfEc"'�Z���?���Z�F�����WrQ�m���!��^��!���[���|��m����"���� BDp�%R^{�U"c,��N����	BD������]�;�\����g#����Q+R��������BD�
Z[[%���]����\t?a"�U�������A"�?�g����w���"��`����9b3��~
iw"��\ihN�[����S�E�� ryHdl��j���i[�-v\y��V���tU��k�����n�qi���/�d
!"X&
i0h)�;A"�������V�F��C'��[��t���q�b>������-���9�?���������q����`�:d��}��I[[�����4��n�wd]���JO��z`1�{���#?�*=]���7BD�:r��U.
>|X���?�r��Q'\����i#P�N��+�j�7e��tt�����B�>u�*W��F*6l���;���^���D@~#D��i�HC����G<h�K�\z��2��]gp�_��J��^����Vk/]1v����C�:��~�T�5H�j�������:_T��nG�3��Z �"�e�����8]�R��4H�A#/�f
���cr�+�o
��[����E��F����j���R[�;.r%����S6r��n�`��D3:_�a�U�������BDp�����F"��Q���PM�>:���
���_�H8�;h#�`EM�Q���uq���@�!D�p�������(=�F����i#@.{����u�U_��W���v���]~�F"�U�Dq�b�u]�x�Ol�ff����D�4Tn����N_t� �"�annN���o�7��
9{���������'�t��]x��NK��cV�T�m�����Gc��i��Bt�F��`�F����� ?"�hffF>����O�S�IM��Z=��Ot!R�e6X�����p{��f��6J������D�������KV�����������K��1@\�����5>a�E������V\�[���Jdjr�F�y���z���g������V��B!ihh���Z���p.:�9������
J����D�"6X�w����n�Zc#����D##V%��u]��x����<���\
�V%�������V^�<����Z����~]�=�{��n��k����A�������5/��F�
!"`	���l$RUU5������5q�c������\�.���Y��g���m# a��u��}��Jdv:*�}ge��W&Gezr��j=z��������ND@�"Dd(�����Un���x���z@�7<W�����y����D.��M�[�u>:mc���+���*`����S��u�U�����]9/��8[��t� "2444�M��������m�l��A�nx�U��������Vk/]�)v��x���D��:] �"2�q�F����l������\uuu6\���;Ng�Lh�"],fzfVG&���Sh~�Qkg>6}%�N��o��`�8�|�t&���n,}nV[[k������t�����96Z$hg�]�?*uw�]���n�.�u�Y�����X�����_����S�����W���F������b���o;���/BD�����HdddD&''�ZHCF�&�{,�W^{����O��/�|�iLl����7<�&�����&g.Y�
U��=��HEm�������A����U�Q`.�����������*���&�������Q]�x
���D����*���F	��Vd����Q�Yw��6B>���?���[%
����nA�!�@���?/d�������Hg��Gr��/�Jd�k(�/�
H�{�>#���vVK��-RZ��zr\��\��h�fD��o6�����*�J6�~�!D �e��0���6��>l#`��\v�����cVe���]��5��
���������?��U]]-eee�X;
;�8}~��W��* 5BDX�C_���_�Z����'H����/��Jd����w[R!D "��xX��@.#D�mffF>�����K�l&�M�6�[��V)..� 5BDX�7�_d`��,�������q:c�erx��V�����@j�~ ����?i�O^C�4����]���^�IM��ZD�����k"U����w�����D ��In����uWO?f���=)���A�D�x�!�l����[w��6B!x�����G��g���Hyy��7��w�)�y�sl����\cS�|�kV�T��$��J�R��]���sV�|�=����+�����\D'"��4����?0�u�uK�p#T��Jmu�B�&2�(4Mt/��	��(xs��bnnN���o�7���y]�����<����>]�k�%	��@�����F"��2�	%��'���<��B���sO���;(�?{������Z�G{N������|�3������6�����z�N������C����$�����:?>��M��hW��\�dz2y�H�u���x��((g�|F����r��������������:g�����}�t��U���j���w.:���z����>,������������6��Z����[���{n�W5�j��ih��i8/�Wdrt��j���NE�j��9@~"D�.�;&����]Z[[���I��~]�u�y��+�����=����Sg�|]����g����c�DB��444Hmm�TTT8�����c�X�����Bz��[����:]�����;����*�����uhl���j�u�������������h���+���#G�8����Z�������_�O��zjh���xph-�
������wZ�Z.�m�J���2g�����#�y��40�J�9���?��B�����U[%��^d�vw������E@*E�����2���w�Lz���~��9@�"DXS��M&t��_K���O�a=w��P2K=7V�h��y�����>$�kBR[^*�E�V�B,I��Z�;��U�H__��D����u��}�&�{,0>���Y(.X"r�V����l��(s�Z�|���h�����[,TR\$�������/��w5J}M��qi��~�K�u�@~�_kF�2�p���Genn��������t�jw$������}����z���%��O�����s����sP��8�����s_����:(�e���U����^=����P����X�vZ�w�����_|�F��z��J+|t�q�������ZE8(�P���K<"�[�u^/
C`N��Y���Y����ijj���.�������+
������
��C5��t5��5��������5�s��a��[�Z����C$����D%[����W*����Ve�w�����{�B���������x}���8���C�2�q�����������i'�O}�SV�l��5m'"522"���V����ou�~���H��c�����8���/������{d��?s ���=)�=�x����{��~��V�L���\D'"���wJ R:���V���C�l���O�����h�'U7"��<h�����,��� ����k"U���������y���������Qj�I����
���������3����=����{�C?�(@�t������? �*@��5��Zo���SP���vvv�h>����/v�v�JP���������O��UVZl������Aa������V�LLL�(�h4j#q�
�V����?b#Wy�
�_7~y�y��}��U�`E�Tl�*��w8[��t� ��}Y���Y����ijj���.���4�w�^���L��?q�}����T�{=���${������{�uhZk�HDzzz�ill�p8���`��8�g��;}nP>���m�������-��[�|w�Vo#�� �o��l�D��+���R�R��sW]f^����@Qf$���~��r��q�D�n�*�P���4dt���Dv��%���U(d�A���N�,��5�q~�#��C"g=�N�}��%V���.��>��D���R^�Y��*�I�FFd��E��N�?��W��M������\D'"���w���{���[I`(�T�2����:������{�oT��E�:+��CZ'[����*wY%2��Qz�uz<"�����u���+W�X���������{������`�uz<"�}�G�l�J R:_�a�U�������BD�5���LGG��VG�a�dVH��v!��o�����nJt"������U��~]�=�.�/x��������^'L466�\t�s�/N?�\�����g#����Q+R�������q'{m$��I ����.���!�7��`Z[[o�%W�$����������FXk�/{��\����A��3�{u~W�m-o��z�K_*�6m��5<<,}}}�E�^�������?��u�r� ������l|��m�ff����D�4Tn����N_t� ��blYI?�����*���&������6��r�)����P�����Z�m�9b��ns{{���?G���+�������c��9]���R�=�j�D"���c��i��p�*���������D>��������R;{�3������>\,�")+-�������@�n}���}���fgg�S�������Lruuu��w�S���|?,t��Ko�~�\�U"��g�e"�"c������v�t��V�P���r���P�>�?�3 ��d��]�7����D���2|��U"����X����D���!y��[�����+X!"Y�Q!������{�ikk�����1+�����=F�r=��?DT__/�P�*��/O��~�;V���{_!����* ���?���?�jq��|������* affF���A._�l3�m��Q~��[���mH8���d�B�]}+�����%�Hm`dB�#���e_+�u��$��.���U��Y�C��
���[?�"�J�j�U�M�����V����=W�"��S���<-��F���o���%�l�X�lk��R[U����zH!"����G�(����CDK�.����c�_�K�s����;"�%�H����`)N����l�������m�������M&O?bUj�[�/u��gV!���q��J5<<�l���.}3bii�����;'/^��U]]}m������d����m�6�\�N�C=6�o������wC)d�����3���%�������*,���c�W�O���B^=������$~+���V�He]�3Ng��W�cCV��Or�%���+~���6�yc�3�]6lp��^/���"BD�/��������V����9z����uP�"���0!"@�"D��������3�Z���U���n�G�~��9sXh``@::2�������t�M����Y1��+�J�LNN��B��;_���Hmm�UHef��L�~Af�>"���mV�(|��{���E�*��Yd"oCD)o�\��y{*��K0�v�I&����V������tO�o
!"���`q��d=^��=�"J�I$JZj��{~�;;;�T&���B���a�F��*���!�\���R,��@��fe,:+������HU��V�+�CD��=��f��u�$JFD����*!���}�|�I�hv����.�x���-T�=���k	�`��KQY���@U��l��R�d*CD*��
����E%A)��9i�(:>,���dv:j3*����b�"��@�G�@��E���}#�����V9r��U���6��{��O�DqK�����j�w9�!�����o| ���t�����c�������[�������{%?o{�U"���4T����f�'g�w8*�����w��W$��};.\� _����*LUUU��QQ����XU����7��-[�J.��%g��* ���&��f����Ay���o�D�5�RT��g�������A����

�JQiH�#�6�
V�Hi�\��!��N����D��l��$\)�S��WV�^yt������P�U"��/w4��
�������^����z��{V"��lV�ik� Q:�!���C��Y���R;#�%��������
�r<q�O����J�3��O����* �wP.{�*��CIDq$:se�*�-{^/;��U������JtwZ�~�����gff����2;;k3"�`P��D���S]��������)���T��<o�/*�u����}788h��< 


V%7��#9��Y�=�>%�o����JE��Z���y�'��7���P�U�avfJF�zd:���`X��������������T�7JQq�������2|��U"������[~�~ ���4|����vZ�����o�����F+���_���N`�?���D�����Tu��Y7��/l�t4���y��RSS�t�)))�[(�D*�n��ZP���[��~�d�!W/|@�*��<"�DHF>���O��4T�.�n���PD@!������-7,r��g�+O��]��Q�~/���O��zjPh���63��~���[	7��<�R�����D��������g[%�m]���/�����)9w5�x���BE��)f5�;m��5�PM��������:~7n���J���w"����8�yh���Nw�|399)����jy���4deE����NT��}��z�:*2�,f}��V��!73�!5��<��pz:�����:���#QQI��NGeN���G�b?�tO`N����u��c���DX
t"���z��"�?,�R�"RK�����RH�����w'Z
�s���I�����7c#�# �y��	!�����~�
V��VW&�I���365+��'����_�u��mT�����b4H4;;k3.��SW���G����X�***rD�|�4D�s�HE�y�������2v�n�6?Ht������.�=v/
���g4�ckKm���<�C��n���V�"J/Y���������B�@D���E��BX������z�y-�<���5� ���a)�h�����;�3(��1��O�U����S����Wl�����l���Z�M&�:�yPX4����(
i`&����"=��M��6@��G������}�y�K�__�Qm���z7H�[��t�E��==W<@��k��D����*��RZ^-�:A*�j��q� 
!"X#� jkks:�����������n����{��������k��d��R���?t����K�+�����w��O������x�f]Z�����5g�(*���]V��E�w�I��N�����i�G�?~�.]�h4jU���>?
#���Q�,J;*��V�h���"��"��m2&X"r�V����l�q����:��:zc�E4�6;��D�H����.NX�����^�3inzJJ�+�NL���O���u\�?�H��FC@��7������?�����w��%�@N:���k5n��[P�a&����A*����l�8��q#�R��w��a�����JO��z����g�Hd02#�����~]�=�-/���Hoo�����s��������� *<N8�a~hxL��^+Lc�HM�>:���
�7$r���NF�5�k��Da��6U�a(���5��h_���|�> �ak� �"�5��4377�b�/]���������P�jv4��4���d�*/p�{�J���i��f�wVR���Z�6*����X�s�v�z��U�`�Z��6I����Vk/]OG"�5���6r�GS��N�H�������F�Hyy����[�0<<,}}}�E�~ �cQ�4�s��{<A���
bj�R�������������1+b���5�a%��k�����WKI0l�$��tl�H��~]���xO[+y��oX%Y���&Y�
�x�D�I'���7�J��yXj���<x�F���K��i���=R�n�Jv;��$�<{e^���R)�i���-,�������Z��c����S=�V"���[_��U���99seR�M�����M�:[�����L�{�W���*�UQQ�4H���%@��`�g�V��b���H�<�����4��s���k��yC@��J��o�$������H�u���K���BD����a�u��� �v�Q�PK.�p4@���n����������]R��G�K
��>z=�"���}������m��_�zn����}��������^JB�[4�|(����G~a#��}��e����Ff���)9�?�l����z��g����Z���c����-���J���P:�e6�(��K���Fa��LtR������"���] P���~]��?��@���b!�t!o�"o�&Wh���.M����j�]��b����w��\��7���� :|��U���d��{~������Bv�t��D�����8����N���p�{>;�#Q:�N��J4�jq�V�TEY�gLCl���i���KV��{��(��K�	���w<��N�����\����AY�H���_�y�� ?"��<I������Qn����#Q*�n-�5�� Q&��Z��X�mTku;�'33�r���D�J�6J���W���C�,O�^������\�z�*�P($����S:����=PU��L(x~U��V=}V$��F"V�@T���I��`��������G����#���~]�=@~!DY���c6J-]��l�a���9'<�,����Y�D^$���~�d��^:��s9�C�����^��(4��$����������;~�������M�z��V%��fZ�:]��i��������;
iX(�%;bt�.�c��3�.K��T�����/��7$r.��/��}^����s��x[�9����$X.U�o�m?�t����6L ����K���������*���&����*;���^�h�D,~���r��g���T��@ �n"��a5�{zz�illL�����Ah��~�*���M,��*����L�\�J����*���������,�KE�H�J�ebjF���2����[_��l��v�����+=��U����l��B�BF������������������]�"���I�_>c���
"[��qo���D��kv�$R�Y�6g����:q�������T���������|2���z�)	�w�jfjR��?��H�C���RT\*�3S�E^sR��������ozr\�/��J�#��";k� 3������@KK��D:;;m�ZGG��,^�t(�G�����9g�3{��w�O�q�='��������!�^���R�(-r�Z���`I������{�*`>����F��_o���DH-i�����+"O_9��0@��1�-v\���<��")*	Z�()���Q'@�[������b��BD����P�0����m$���e@�W;w$BD���LO�Z����uq���s_����:(�e��$]*v�{�|�6\���q��n�db������qb����Ya����BdS�8�/�ahvz�f��dj|X���w:��Vk������c��s16�����,���V�455IWW�U����u^8h��}N��������7�����[�v!��w�U"mmm��VJ?
����*���F	��Vp�����y�?Y���J%TU/%�J�I������������Nn��;����������x}���8���C�2�q��������G�z{{����J���N����/����v�����['6���300 ���pc��-[����I4���~�Dx�ihh�*��������*���E*��(���\�
���Wk{��+�m�;.c"'�Z���?���Z��=��O&:@Wo��["Ej+��D��D����R����F�~��9�����`X*��(��W>�nL��������F�������6BD�^����y�����;���4L��3���Ge��=V+G��������_J�-���j)*	KqiHf�&ev:2��z���M�~�U(ts�3�o��l�D��+�����=0>%��&�P�������w/*T����[%Z<���&&&�������6����\�"W�^���y�����{rrR��?o��BD7m	�w&���>7,s-+�	
]���pUc�;�w���3��!D�\�Q<4=������L�����u�cS�H��^JC����c��`5"���z��"�?H��6����Iv�R�����yX��H�}1�wm���I�U������O?��Dn�+���"�R���S�V����/��[��
��BD �x����P�nw���v!�nD�|_�F��`$�d�Z��O�y��*����*6l��`~�������P���������&�dr������ �e)�ST\b3�a&:!cW?�	���\D�@���a4Ht��!'(���&����=.��da�"��d�������)���b4@4;;k3	z����$��6'�J�s�_QQ���(��/BD����J�D�>����Q�LV~Hu��b�,�6�gJ}�|����`9�E��`E�������3v��m�	��0����� ������Bl���h���q�U"c�����]�� *\�t����70��
T
����s���yD������K���F������~�����l�`�@���O��F*6l���;���^�z�=�/�<�\B'"Y�Or������yX��Ht�p�����
��wm���I�U�������Jd���T�R���'g���D�c������=d�
�Q<@4==m3"�q�F��N�! 
��s��e'dWRR"�6mZ��(��F'��6����nY����}�hx6��
"
u��|���+���,v�����Y$���3���Q\�[�p��8(���%����hdD��\��i��2����������ND����+������0�� D�_�����/%�T�p�����4$3S�2;�hd������&y���c�&/���%g<�{����h�6?���*�8*	HCu0i�������Jt&��M����6l�*�U6��l��o������|MMM������o����K����X�\UU���Y$FCI���V�A"=��SQ�[����"eV�!}����#8I&c�z�m�0�.�;�Vx�{G>E:�9q�������*b?����������x�w[��]������\���H��Re����8
��%�������:�����P���V\y��BI(���`&:!cW.XE��<�~ "��r�E���v�����F����~���#G��pXM������+���}�*��R)����P��$LO���H���$�9���~Cnk\o���~L~��7XU�����"X$e��215#c�Y�����v�{�,�n�����9D��!
�x;
�Dj9!"��$��<N;iG"
�BD�������kb���������B��m����V;DT�yG�o�r�r��P�L\�,%��T�7:+���rB2*XQ#�u���S���s\�����r�3���m5��#������(�5)�9fzr\�/���`y�EE���\����c����^
�W��K�R����b���|�6R�����Sr��� B�������2��O���y��b"�kt���!�\z�8��C�����/@�1M�H��Jo�*vl�e��D
D=U�7��x�H�f�����&Pz^=�~�|	�� +?�o#�`�:e�(N����glKW^^.�����4��]���/�
.�TWW;� ���K��1qz.=g<H�_K�&
�v�9�xD�R��d4t�����:����4\9/@�v����u�����G��_@��y?��Pss�tww[%���$]]]Ve�@ �.��<�jmm�����pXM�HDzzz�ill�p8l7����������.D�$X^cUj��!��d�H���U��=���������z�3��07W�������9��q�S��[\iqQ���Pq��'�����H�Z����~Y��r�U�iw��J���u�V	�2{�z������`�gI���LLL����6���9��48���/���6#�e��y�fff�u���������[%��HCC�U����HN|�EV���.R����'�c�����9���u�HM����Q�S��u���C��m"������:q�������T���������|��*���;�$��<���3�n�b?���@d:�#�2v%���p��m��4$5|��U"����Xk������ND�L���P��W���^��}��� �?��B��K3�}���g.���_�U�"��e}�6����^)������ux�����bS��p�7@����m����] R:�������	�����5(Y�H������G���Ya�=Y=��[�
[q0�3e���(�:���!"X�����hii�@���c��\3S�.��������iy�B��OKd��B�h������u@2�9(���*m�%��k�����LN����=b��u"
uV��
=}A�����G�Z�|��ci[�8�<����s�����p-��cC��X����uq���BD�L{�����6���v!���U���8 w����1;�Qz�uz���gxbF�f����{j`R.g��u��O��_�ivvV��	�d���k�X=
�����{���m[E6o��QH
��]�N�u�Z{��M������$r�HV"Y����;��_��2Hv�{�U��@�!D+��
��/^��gr���^�=*�����;o��F�ezr���t������qqdJ�N��+�A"�,4��Y.�m�J���2g�����v$�s���k��B������H��(.�h���P��P��_�,�ukl`
�(���2���t�5�s���=g�~-��(L��!	��d����2�wVF�{erd0�7����z�r��?N�+���s16�����,���V�455IWW�U���1�v![E"����J���Q���U�x�z�w��OViw�R	U�KI��f�'Gdb����$�����Nn��'�'s��c����*q4����c�H�=qe��r����$25+��O��>X����T��
�����Jt:������H;[i�hb*D��>���h,v���G���������J���Wz�!�D��_�����HE��
X%�q�F��\�s�kxxx�1����E�fffF�y����������~$'��"�Dvn���,���C�|��a����~�y~��?���\��e�=2�kD�m�{�	33��yNd4����Z�-y���0�vd����JE��Z���y�';����#�����S����LO�a{��UJ�����o��x�*���^��l$�X�\|�
�z����A��������_J�-���j)*	;��?35)���y�����$oz�s��_����]��!�x�H�]Ot�Y,��A�3W���p�LL�%=�����j��
QUU����o!���/###V&BD��q�pP���&b��
4$3�����-�O
��Gy���A����9�BT�GBD+����%�R�D���a�BD��@�@.��W =z����A����Z������%�r���D��Qa�0��z4���
�EgmFd}�x��A��bg����_�����O�A����G���D�����o�H��-����Q��M�Vk/]������)@���������<����A����={�]�2��V��P&t��G�J$�.B�F��^�u�7]{;� �����D�Q�t���+V
�\�jEL�u�i�����u�V�;��qz��2|��k]���X��e�9w>6��t=������Y���Y����ijj���.�r��c�l�"p��-�HOO�U"����� �|����G���P�����m�y�|>]�2p��c����*������4�>wljv��@����UzNn���^���3�sX�G>���f�T��U"���������Jnxx�Z����/;�Bq��q9}��U���j)++s���H��;v����,�
����mUU�s��3��#9��Y��i*��3o=u^dh���]��\��6���=~����J�[�7��;c��|�s�w���RQ�B�w�g@����b��n�!%��o��� �V�Hi��C2����q��
�^Wx�&	W�Y���c�}���D>�{-����*2���_�o��d�|yFCC��#G��Lrmmm�{�nBEXs�� ��]s.���Ag[(��[��3������E";j����V�[��w��*��b
!D�R��V�H}E��$��������Y���D�	������>'�.��~*�6m�����R\�����
1D��S"Q�U_{^V�������]`�����snu�����MG'd�B��e���R^�Y��*�I�FFd��E��NtS[��6).����C�����\D�@���aZ[[
����O<H�k���'N�����/�Q<8S���j)*	;on�������D#��}�q����'w���;�%D�������,e(�1��1���-����|�S����~�I���N���wJQQ~�w�Z
-D49����0�-[D�/�x,08"r��1w�	��E�4BDK7:p^����ye����8
��%��`�z����W�����\�+�������p�"������9�S�K���A���H��F�K��Vk�/���'�w���T�	�����)�24��s�Ru/ZWV��",�v"��g?�h�H�]��q%��T4��>�&�dr�J^_�"�vkE�5iDJ�����B��b�(Dt"��r���4@���TKK���:p����;z�(����D��ND��JB�V-4=9*�C�V:-ftrFN]������"����m]�
������y~��hi�������c�\���RZ����dxx~���������*�Z'"��S"Q���y�HC�;N��O�o��E�s�;�W����P�����UBU��JmrdP�����p�t",���"BD�^�����:����<�hH;:th����
X�� �$BD���Z	�X��&Gdvv���N���L_�h�}��v.
Ulp��j.v�L���8BD�]��K#S��D����W�H��Hz�N�����I��V��"��{S��2w��Y���`�H(���:	�6��F�N���I�y���,��o�
^�"z���P�i������4�y<���qO���R���V�)BD+S�y�����Jmzr\�/���p"�!"�(�_�$Y(�nB�F��;)=������R���K��k"UT�Y���nvf*v�$����>��-�%R,�p�kfnN�O;������ ��<��#6r���.)��}^�cQ�4�u���IF�u���x�o&�1���:��ND�^.~�[{{�8p����P �xsc[[�����*`��D�����b�v).����3S��h;���]�q��{���H_$�V�pI��g��+�3��H
y����D��'@���sC���Wt"J�����]�����.D�h7���g���?��?p���;)
]�j��['R)��9�Q5>{��l������<��ND����}7��������?����cj�+j�����3��+��!g�q]���Kv:�|w���H1u�����,
���"BD�^.����z��J�?�}h����x%a$ BD�[�=D4����Y`cNd��IwSV�I��5V����D����;%����F���R�G�{V#DT��G���i��_����Qr�hT>��[�������Jnxx�Z�H=���I;�B
������P�4`�����G��JCD������|�'g����.�p�UE##2����_�����{�U BDr��Xe��Z��A�s����~<'��E�>��HqI�������]��	�����������C.
��B!��!�SG\(�Qj�.�J;q���@���6�]W(",�k_t��\�W.:A�d����~����6�7�� �����F�I;M���9f�'���Jo��F�ezr���t�������~=��(\n@( E#����$�q�F����l����������0_&?{|'`������=�*
YG�NC���292��i��j=z�������y���rS`���d���f����J���I�����N�O�^����<��D����*���F	��V���������D*6l��������������o��2:p�fc�������P��$LO����e��I�����f	�<QQ��������]9k��g���u[�U�]=����Cop������}R��--&�g�g(jo���	�J��w���]��m��G�<��"�srnh��m���_�u��m�>��O�������Z4���FF�. ����������HN|�EV��v*M���>�?�Q>��U���P�t#�p�������T���*����~ �>u����s�&��/�
,&_�BD�^����=���B���r��g�o�>9|��3V!"�-�"R��D��N8�+����p���dfjRf�#�:����RRV��"�!"u[]�T�Y��z�Fe`\�\����-������LL�8c�F����P^"���u�jljVN�OXE�(��~��r��q�D�n�*�P�{�����\�p�*�]�v����o��!��:����q�������q+L�:7(T�4��������T��~6U[���c?�{���!D�4��c��d�gP*Mwn��x�X
BDr!"Y/_�Y��Okk�9r��z����c���y�~������"������������k�m\[[����{��/���������&BD�[�9D���&�^������i���l���)BD�;qyB&�5�0@T�������K$*��?w���|F�(s�w��?�y��	i7�`p~�,�:]�&'?�����8{c!��U�b��6%��49�����D�(s�ND��Oc��(����4��XBDrQ~�"7�E�4L����c�A
��*
����wA�H�m�y�������_��~=�G��^O
o-�{�����3~~0�{4��A�8
iHA��
�MO�J�����- R:�9�����Y��(tz�^`�8!���^���1��c����M�����"Uv��x����DJ�+�I�����g���[�~��z=@�"Dk��iF�#K	��C7q���zv�YM���L&t�Zl�r=4�����R�����X(U�h���LM���J�>\V�:]O�q�]������$
�-��
O&:/}�Ke�������a���s.:������XO:��F�|��g�_��y��N_����U�����RY�]*�$T�>v��l�����D5=����V�7��@V�Oj����J���I�����^@�������u*���t}gg�ttt,�ds�����\��>��
L�8z�fh*���`���/�u����b������/��Oun������D"���c��i��p�*@�y�T�����b�H���R\��?�gg�t��G����j)*K�(67[?�h�}��w�����TD����J�3��O��������~L~��7X%r[]�T�����i�����LL%�?��hku��W��Lrz���I���=�L��E�dljVN�OX%r�{�,�n��*$3;;+��������f�����w�����E|�_:��s2�����6�MO�Ko�{��e���*+�9}�����IIY�U�+��.g[Z�M��R���="�����	
��\�����%��x��v����V�Tr��+������/�$�,����D��g�_������w7�F��"��%R��l B��h[�%/'��$C[;��:�K�x��P&��*Y��{"�I�d����D�%��l��eQMJ��7�ht7��U�RVUWuw5��?����y���S�
���=pk��F��za����j�}7)��o����w����L�C���,�Q��N���yN����w�+��93&�]J�.��n"D�������}=�Qc�D���F��
3#^5�������[��W�$"j���T�'����W_}5�i.F"����4883�K�������1W)�������E��C��4���\AJ��?����++���*#�������hu����1��O}W�
4&D�"���"�����I��"@U�,\���Sy��z��'r�*BA�B;�c��O���)�{�Qx���r�j�!3�W�X>\�|�D@hb�B%`Tu�/�b����D4V/@4X	�TcD������e���K�b�8�f���g�;����7�7� Z�n]��yse�vQ��@����4�j&�q�jn�������Q45��T��-$���{�����G���j���*�.1���f!��1��kby<���
�G�	�����z�����Uq�q��=�����F!%X�n^�����A����i����UG,z�2����'+5KK�Q���
C��K�/O4Ex����(X�mn���3?�q����{��{�Jixx8m������Z�(�����&���U[w�VJ��O���``u��33��[������'R����#���z���A�`Dp$A��jS�Oj�X����C5�PL<�V���7���;�\�#�m%�T>v`�����A�(�(TM]���Q�W�u��o,	�Do�4�����
��)M\��^8?�N^O��N�������~a<ML��<�Lo�����}�-m���gs�*CCC3�WM��eE�mY�6��C�Uu���F�b~,/*oo��)���&f�'5S^���?,�Z��6E`��ne$�^P�g�����\<��B�(s9�u���'�*X���4�|�/�Gp������by�W4u},�X
�&n�
�XVOh���i��L(\�J/^�L���V���(���b��r�(������3�����kom�,��:5�ma���R��%W)�N�={2�o���kS:w�zu���5�]lE��yu��\�49~5��+���~��#D0 )N47�p��O?�[��0S=�@R<����G-t
����#�Uuc�z�x�-A���uo���<�+*����n�`�s��J� ����N���X/��)����>YZ&&&��s�rUmh6�ub���l�� ����\U�����3)}���m�E�~l�������f�^LcWrU_,��j���E�`�����i>���������>���_F��5���H�����l���r�]i``&rsz�:���X����?�����vE�o�p�Q��s����[G�'���b��Z�(�#�����������(D5��V8{�lnA������t���z��\���vP�|�m�Uu��+
�DW/W�}����[@��_�Xp��������Yl�p�B��t���{~��-[1�1b�r��v)]���-#��c~,��j������@�DgF���'n
mL��_��m��F"�b���(�m����;��������j,�SE[�l�-�X6����O�{?������i`���b�1�����z�>4r������{O�"�=�^?s2�~�t�r!]�Z�����NU���vo��HD��n���2�v�J'N��UJ;w�L�����Q�+�L�������DH�������y��[B6��g���G+�E���ZE�����j�q4R~|G��m#����S�N�*��[���W��=������~�7r����;����}��15��.����������hG�;G��By��������)����4v��\�����;�2{8���������������2�^��wll�F����r����@��n�n���T:}y"M\�9��~�V��s4:�=Wx���3�,�}��sE��O>���?_i�]�v�PP�<t�Ju�M�6�G}�����7&����7���|2�il��Mw��o	1�����K_|��\��=��������\@�������_�F��zBD��[CD�K�8"D��}�G9D�u����<�����_��t+>���:q�[��e����-i���t���(1�����y�7�=��{�Ug51��W����\-���K����s����MG� ���s���U������a�"���?���y W����Nz��s��=���0�������3��{��7}�����n����`�z�+y����#����L������������?7s
j�O��������+�M�_�Q?���^��*�U����[�W�'<,�M�BT�,�r�(����r0(��]z�r[�T-����:q#�R�82�4@�
V�������mo{[nU�;w.MLL�jF���XTS�&���� ��>�[Sz������b��f��G��M��u�i����}Q��X.@K�}���#�U#5���ja�����^��<�P98$H��x����Hg���z�v}&�z���G��E���~�i����������o��s����x:}�t%L4::Z���bYM��/���&_�t�x�_�j��3���;Rz��)��!�����Q?����&����������Fz��x�>5��VE�cy������r�+���+�8q"W)���3?~<W����L���������P|~������;1
P1���l����m�����VT���vGM�Qy���all,�:u*W)m��5
z��U������~�7r����;����{��yc*�_y-MM���1 �iUU������4���4����3�����4v��\�����;�2{@���������������7�����2�^1{�ht�Fz���\�q?��]?����7�;W�MMM��/�ez�����������������6��5�����?����w�J�;RZ�2u\��$}�d.�����i�w����[}�_�^���]��{��#�������322�[���������z�|BD���B���^������IDAT����<����f.!�n�r�h�����#+�{��3�#�&W)���#
���������������;���U������z��-OSS����K�e������4����z�hjr,����M���?��k������~��s����~E��jE�;wu2�xi2WK���W����7W��^�'|�s��s�{���_��A��J��/���;]io�0����/������\���VmK���������_>��\U
���V�J�C�ijb<M�_M��d�~|�{�=�@��f���^4����>�"�5���>����{s�5� �n�,@�|���z�����w��6��5[*�Q�����<�+v�����b�P3:q#��ku=��}��|O������c��<�j��U���,�u�(���7DaM����z�}�����-�e�Ci��i��mix���|xU�6���l�P^3U�{��j8�?F"�^��D��v&s�+#4�2Q���G��c��������X|���s�#N����������c�=�u"#���0�l#�����l�p�����v���z�]�.���Ds�h��+���W+��O�r��^=����s������4��
���.�O���������t�������������w���wU��������'��UJ�>�h��m[����&.������J�m���nU.��p%��_�������ix�}������?�������"(44�6Wo41v%�~f��������O�[s4b$"�	]��CD��W�	7� P9��Ni!��@��"�(@t��l��zK�������Z�1�\3{8���g����J��}�� �\CDK���_M�?���>���m����.]�J/]�HS3������i��so$D�\|�_����������-�fS���t�b�=�j[z���X- ���o�^��$C�7TF���gOO��n���7�_����������^�,������L�)Z��������-@��^�hp��-���[L���c?5�����?����>���C�UJ�oVF:yq<��:�F'oTn�����[D���	������^9���k�h ��D��=����o9@V�0����z��|����������[U�������;���O?�[�0P3D0w�D�i���_����^�����j����>�6����������&�sg�Un�.��c;�N����V��WSm$������=|��������Ly��~��0psZnt�]�v�'N�*��;w���������jXd�F�)�s�Y�9j%P��#�����*���'k�3�~<��sX5���Dccc���S�Ji���id��QX|�<w&}����*���v�����>1��^x1W����{��e���g�(����}�WoL�k�V��\{�L��Pi/\��lyk������X>�:W�kjr,��?���>���C���<-u_��O�s_��\5��=�����_�4��/})}���O'O��W�ZU�����w���y.��������W�Q�����>���?���i�\��;{)/��x�_I;���
�b�?�7-W����4�vc��ra�;�K�J�����`��@C����l������!�V�)KM�>t���\�t����������	��c.��v�)����!"����!�0>z6��~��Q�x:M��^i�5����F*�q}<�~���Ji���4�G4*���lN���T��:!��}�?�.|�X��oy�-0F"z���re����|�s��s�{���_�wF��n������oIW�}>������K~��������G����?��_����VoHk��Fx���������J�-woL��S�Ui�	��U�#�A�EP��z�����;����������D��D
j����H����� 
�W����k�_IS��ru�������~c�q?� �}'�
��#����+��
����C-F��vP655�>��O� 
�N��@Y�����;������b�X_��F��gCn�J0hb�J�����Q(n��e��)p"(#�Dx(�r�(�k6jQ���]�)�'B�)��8���BA�u���{+�iwj���d���[Da��uihd����ki��7��+���������m�1?���v�}Q�?�����S��?������928���8�vlN�W�H[F+�Q��i8
-��������\A�o��o�W_}5WU���K[�n�L�.�uc�g`�PZ>r�7����^/��F~�o�����_i$��z�������������i�
��v���N�8���v����?����Z����g+sf����z*W���C��y[o����v�>�fn�9466�N�:����o��FFFr@�y��3�#�&W)���#
��������]=w2M]�?Q=��W�U�w���������4z�d�R���L��5W=�OK/��\�J�h����C�\�J/��UJw?�C�����\���<y2����s����p��eK�5�111���=���g�����b��cG���k�������9W�[�������7ro�kG��~��sU5�zCZ1�*

����49~������{O�����L7�~0#�Q�)�H�H��J��]1��#G*��Vt�8��Eh��ih��<��X/�_
"�s����VJG�������z5W������>����V����� 
1/����W���om^����)=��zuQ����v�H����;sU����/����P�-�b}"�oBDt\��#E8�^�'�G�����D�(�Aq?�BMq\1?����X�Q����7������*��C��<R\��S_����3gr+��k�VF"j$��:5�m���/����thyJo�'�7�����C+��Q���y�i���;�H3P�w���������^��7!"M�s"�A���#����>Z�S5�8�vBL���3�s���LZ���O���yNM�4�nq(���c;�)�BV�h>
QMy��~X�&&&��s�rUmh6�ub�����������5�
krQ�������3?�����';�����v��Qy��������^��7!"����3i���\U
�Z�F�m�����F]��vP����V����F*�W�K���s���(D5�v�?{�ln�������Ji��������c���g?��V�v�����j���������M�LOwWn��\�r�:�����%D@W��v9M��t�_�beZ��Mi������
����m��7��,���b{������UJ�7r���z�}������`&''s����h��-��Rv���4v��Ji�Hn�����s��>���
���b���_�UJ�����;��-���������m�1�����b�^��+���At�M����sXH��]������J���P���HeyQy{���ozWn�tal*]o>Q,��j������B�7o�UJ���:ZZ=��*�
�t�g��zvf����G����Z������r�j�����Ha����j�������|-��~#D@W��>�1h���l�p�����^Mq{���1��N_�h$�tm*��4�����)m��5�R�r�J��E�(��)n�����o����-~l��+��v�Bn�w�

D5�<��y���� ��"D@��159=]�UJ����Vs��b�����>���C�UJ�o����������it�F�6��_OS7���������~t�����7���y��s���2�PM�;�|��b�z�l6����U�����F�rc��b���L���|{e&�bxUn5W\���/T������r�+���+�8q"W)���3?~<W�\����S�N�*��������:h���y�L�����UJ�7�H�+���}���t���r���
�����s���kW��K/�*�5[���
��U���K��O�*�O=�����F9i�����t�+�r5�������3��+��o��o�#G��j~��[�V�\Yi�D�/_��k�g����\�n��=�����sE?��o��t����UJo�'�
krQ���c��\L���G�����
Rz����3��t���Kw�7�j� ������+/�*�O��������������D@�X��
���059�[�]�>3<Cl��"�����_L����jn��z��k��+�]����9S���0��m�C-��^|-�����$��<����w}4���m�6�V���xn5W^���?���WG�������;������j��C��>���l�����9�E����z��;���#F�V�lzO���P��?�&S���R���)�v1��k���c������{��[�������e�-wo�UJ��Ws���z�}��?7��t�]�v�'N�*��;w�����
�����t���\��}��422�+��3��Iy�3�Ji��ipE_�'�]Nc�^�UJ�W�L�k��~�oMM^K�W^M��okF���V�\���459�F���UJ�z�����������������?sU�y���r�@��~3
-H�&o�sW'���{����������>}:�������Z�n=>>��Q�:�r��t����?�'�De�\m��-��W_����.�������m�������+�����g���r����;����\�����������[�K?���F�����W@/2])@C#r�*����H����&�.NO�+�Q��b�(����y�w>}K�hd�`���t��i�����u�����y���&����_����~��J'��}�k���k��@����X'����V&�_��o��\5�	��|�m�Uu��+��P=W/W�}����[@�1���%7�#���HD+�lN��������O7�n��e�+���M��o7o\O�^?�+#5����t���t����idE���66y#����`��?����?��\AJ���z��_��Jx�1*�O��O�5k��90�6Q�p;P�������gO�|����/�L����rU5�zCZ1�*

����49~5M�^�K�>���O{��#W�yp�������W@/"��a�3��z�L�����,-��B���c��4~��J{��i���v3�/O�������;�����J������?��?�UJ��-K���K����z||<]�|9��q�R�w������p����/�|z���\Um^�����M�V��PJW�R:WHf��C���~,W��_y���_�����U�~k����?���3�����,~�zQ�?����}����zEk]����������o	�^�:�{��i���i��U�)�1/���6�-�\=��[DC�Sz�=)�����nHi���m�1��<=����/�
���@�\D@o"�E7W�2������V��2�
����~5��#m��9
�93b^,�uj�}�����^��'s�j���6��EI��~G.�3�������[��7
T��X�%c�����J�v�J'N��UJ;w�L���0Wccc���S�Ji���idd$Wt�3�G+Sx���������>��_L��{=��������|�=i`	��}��+�[7��L����}�7G#��zE�{]a8�N_�LgG'+��
w��?�[�6����w���W+�
6TFj��������v�L��?���6����}i��*��k�#���/�t�J��j���;~��U����������q3�z�r�~���l�B�n��[����
�EBD@��#t����?9����u���U���������F��~-M\�TYV����`:�c�s)}�~4��o������eeY13:L������g��*�-������CX�._��>���s��;�L�V��U}8z��j�-|��M���:BK�����>5h��5�;6����.�t�L.���/O�����`���
�E����?}����+��MoJ+���V�l���Un�^�qGeyMl�C��;s�����4:q#W�������=����G�z���U�j�c���zW_�u?��]��>����Z���4X

�T���gi��J�|���U�44��;w-�xi2��z=��^��:�����.��#�^�:W)����Vc�ub�������l���[UWg?k��^y?��]��-�RZ�1-[>���by�WS����Mk��#WU��N���&���'*�Q����u�Lh����ijj&tV�b�����;�X���U�����F�rc��b���B�����kW���Ws����#��\q��>�5����/�:m��G���b�X?���|0�R�q�F:w�\� Q��e�N�<�[���-��[)������z.�4�<��)n���]g����CB,�����?�?�?����~�������K�������+��b}({�������w�*�������/�.��W�V�h��XV���P�����[U/��8H�O��Ev��>�[0;���u��J[7��UJS�3����~},�Re�{�X�+���U�����i`�P�Su�\�&�������;�\�2W��.^��^}�����D�nlE#�����:����'S���R���)�v1��k���c������{��[������	�����;r+�������\��'���)nE'*������Z{��ynU�1������z���5k�O��O�2"Q#�N��@�]��XZ���rUu�Ju��gOVo�.������!D@W��o}SnU]��J�����[��X^T���?����~��s�\��C#
���?�����/���i���yI������V��:D4�������B����b}h���i�
��v���N�8���v����?�+`������S�r��������H��;�'����/���jh��4�|�t+�N��4u�Z��z�������t��v�
�bd�r�h��`Z=�,�\1��MN������T^Z���X���#���.]����u��h��s_H�}����W~;]���<7���������i�C-�lzO�����+������G?������������W���+��Q��A�����%@448���8�vlN�W�H�W,��F}���4�| �Y�h����
�[�~�s�j�{�����7���R�24}u� `>���Z��/�O���w�9��z�~lE/>�+�U�m�PZ�r0W�Z7<XY^t�����X$7K�0OBDt����-V���X/����_&�R�82�0@TA�X���7��[����
@W����T���:���i(�f�u��X/����7������*��C��<R\��S_��z�]�W�����g^�U�����r���z��*�Q����G!
+W4����^y?�K���J_;y����
�H�6lK+����VmH�+F*�Q�����5��s�.���n�����k���(T^���%BDt���� ��b����kru��?<���W9�X��
�5�����F'n�Vs��b���*!"��C�n��=��,�����t�b����<6�RY�0@T�c��g_8�[���7�+�R�06�.�7�(��z5���
����]i��]����Ji���������W<��#�����z��������s"�s���\���}��c�=�~��<g���y�����|����S�N�*��������\��55u#�����U�BtgZ�!W�M\���]y5W)��?�r�R6��W����@�RZ>���J���8���kS���ibj�'���������
���3��[s7v������r������H#������m��|��
�EBD@��#Lo�������u*d��qD����������W�Z��sg�G�L�RZ�iG\1�����X=2W)}�������+����?����~>WUG���ei���tmr*�N��e����X���#�h���<����������������s�|��
�E��-tD9���u�6����YH�D����*�Q�b���h7��x�}w�V���xn5W^����m|��\UE`��K�����*��Q�/@@?"�c�!���>�������<O<�Dn-�z�����k��U>�:t(W�[��K���@z��6�*���r���z�}����3�\Y��^��`�f���b�v�J'N��UJ;w�L�����<
O-@�H�"�S!��~8Ws!���j�
����:7�h��BK�N��UJ��oO###����o����������Vm������]=]�x:W)}��|{�_�rw|��}^?�lz��_I���4������F�|s���oM���/���>���n����V�Y���r�����������G��4� ����H���ZU�gh5�S��������Z�,@���u����~�g=W1:��4�vk� ���+��������<'�_�;Jo��)WP�����>�s?���j||���jZ���V�@#~�z��|� �@P+"�S�G g>���q��)��O?�[��}Ep� j��ot�����?�3W)MMMVF�v��4q�b����F}��K��b;"�qk������C�U�w���jn����Uu����Z�G(�[D�S}���Q�{��<����}�N��'WUc���+����'+�Q���3��������Vs����r�Rj��n9UT��Q����Z��K�'��Gn���X/��VBD,���~:��g���X"<���Z#����P��?������ynU�1�_����@��\z���^?�����������f�����J�v�J'N��UJ;w�L������GI������W��U�m�|���8p W��iut��G��={������uC�}����1t���X:u�T�R��}{�t����+��~�+���^K������]�W����#��?�������K�{���B�����\�]�,�Q������������
�~��+���Wu�c������Y7BA��~4y�F�{����_y�?���{/�W��>=�4�Y�c�O���PY/��V�C �nd$"���Kn�g``������<
�������s�\y�vGM/�D�u��4<<�+�7�����:��;u!�����7�_�����w�����_���Q������W�������#���
�EBD@��#L��O�����?[��Z�_9��B�(����������������Z�>
._9����o�������K�e5���w�G�����_������>��<�[,�M�6�-[�����~�����V��b���<����NG�x����+��MoJ+���V�l���Un�^�qGeyMl���"���t!"�Zy����7
��X��~��s�U�r�]i�*�,/*o��
����]i��]����Ji���������
�Vu$�r���r�g!��z����g��\��c��wo�R:p�@n��`T;�vGM���9�~��\�^ccc���S�Ji���ixx8Wp�����N�]���VoL+�l�������41z���c�H��}������Hn-�_�H��z~��=�"
��D�����8<��������R�h�����#+4��kW��������V��'-_�6W�]�v%]��R�R����g�=w��@;�~��e��Zw"�Ta����e�!`a�]]o��������A���B��-�3�0Q�,T�"<�k��BE�w��-`1�]5��nZ����&Gs�����r+U�7
@�n�������������[�Zx��|�������t��x��������X���Xp�Q{���4R^o>a��:v�XnU	����o}SnU]��J�����[��X^T�`)"`��7�@N#��b����`RL�j9�8�[)<x0�������~�;�UJ�'�����H����&�.NO�+�Q��X^���T	������[9��+n?O<�D��gOe��nE���#+��c�~ ��M�sU5q�R�����r�6��X?�`���c��V�#�<�[������7�����g�(���L������l` ����t���yW��\���v�"�#"xSM(<��D1?���v����U}1ZPqj���������Q�(����r
X|+�/K?��<����7}��/��yM^Ru�����}��b}n5psZnt�]�v�'N�*��;w������nV/�<X�}���o	�9rd��J�4��m9�"�T��8�PM+�1��s��\(ccc���S�Ji���idd$W���y�L����a�uczZ�>�������ZY��~�"�����L�f�"��\D������"����qt*@tR�#T��d�B���� N�T}����z�
�D�(�A�0QYg'��v�=����]���'r����;����sE�9z�h:v�X������[�;t�PnU������w��Xp�x_���v����S�N�*��������\@�z��3�#�&W)}�������+�����������G�!"z�p���
�E��-������	@�"�
�nhF�������o��������tu�:s�6�������<����z����~�g=������W.��UQ����?��o$D@W����T���:���b�X�[	��bd���������ui��;��M;*�Q��F$��]�k'��2���i��mi������
ip�H�6��?8��&�{���\ D@W����r�j���i���\�*�O//���_�-���J_y�ln�44��a��&��z5��p.�"��LM�H_;y>W)-[>�[����o�����\,mBDt���03
Q\1�[���������&D@�y�}w�V���xn5W^����J���388������J�����j��^l�@��.��}3!����������/��z5���:!"���{WnU�_9�0Ht}�J�6������w�BDt���oL����Jijj2]�x:]��r��z1MM�Un��z��tczyMl���r���i�
��v���N�8���v����?�+`������S�r��������H��3�y����Z���g���������;��%�������u��5�����������G�!"���o>�����|���_��������+���~��e�z�@����@y>5BD��`���<�����9-����]���'r����;����s����X:u�T�R��}{�t�3��������[s�9�_�H��z~���"��������[�O	@�"�>'D}N����9!"�sBD������	��:�y��400p���=���������q���|��E������8p >|8������SY��a��q�}�}�����Z�	`�	��"���z���X'��D����&���b"�'=���U�o��t���oNG���+���B����V��v,Q�%G�D�b"��:t���"���SO�������+�b��R�}Exh��������|,q��	�!"z��r�~����)n;*��c�=vKx��|,���:E���RIh�����X�{�b4�c���V�����j��[!t�=�����������Vc�p�B�F4����"�0!"zJq��}�����b���4�Q���]#t�=k�������G�����f*�O	�UBD���v���[K�=���c�U5�`Py_��<��B��P����}�r��� �n3psZnt�]�v�'N�*��;w����������C����J���#����Us1���={r��������s��Gy���P��R>����XHccc���S�Ji���ixx8W�;FFFrkq��
�EBD@��#5�	��������|,12�SO=�����P�-!��O�����M���-[r�y~�z��|�!H��Q�bt�ff[����2���+W� �>'Ds��c��VU-H�=z�2?�w�����V� �>7p3��0@��kW:q�D�R��sg:~�x�XJ"�s���\�t�������������'W)<x�2��|��;��(�G�m�y�266�[�{b�����+�	]��0�tc�(���x��YG��g�q��9��_�hY����{����.dz���*!�i�,�G�i�BK�"�g;v,�fW^���	E�(F*N1�8b��O?�[�n�`�	�3�A�v�;a>������VJ{���-��"��<x0�R:p�@n��v��s��������HeBD��r(��Xg�G��}>��#i``�2���'�����9"�t�S����[U�PN=�u����b0���C��X9�TU	�����9��M�rbD�FbY1��o�����A���4)����'WUf��=�����!�� 
O=�Tn��
|sj6����{s�*BB����b^9@t�����<!"zR9�a����?12P9@����3�G6��-����b^QG�d�X���I��0N9�SO����N�lt���\5���hF���a��D0�^�'B;1?��dp'F$�y�f�����b~c��������	�����+�8q"W)���3?~<W���~�"#@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD��nN�m���k��t���\��s��t���\��������l���2A���J�v��\u���L�����������+��k��M������&F_L��S7Y���44=�����t���\u�-�W�-�Fr,4�_�H��z~�:������<��\u�O=�����[s����_H����Uwx���jZ�����^s������O��;<���i��m�z����M�����;<���Vo��\���S����������������s,4�_�hY��������r�+���+�8q"W)���3?~<Ws��sg�G�L�RZ���40�"W����d�z��\����?��k���]z��s?���J�M�����\-������s����W����7W����O�'�|2W)m��5-_�<W��������3�J��GM��m���F��nz��~ W)�������_��L���������z���
�U��:�~����UJk����
.�w�S������UJ��������s,4�_��HDY�W�,�������@�i�:�����"@�z��E�;����=Z����#�T�X6W��=��.�
/�����^s����b�b��{)��������X;����Z���<uR�V�\�i�CK����[{����1��o�F�4��:���6�Q��B=����|�������uZ��R/j�����s=����mvL1>za����3�#]�_r:�<��M;*���459�F������D�����#G��?���E�����jF������t������<���HD�oYY	�,�������r�#����O�}���{6��g��\5w����;��#�s�=�`�bO/��R��DT���64�l)��o_z���r�Y�Q����j���R��e<��{����S�n������5����Q
�,�����gg���(k�k�:��g�|�'����S���n�,hUy$�uw�W	�,���W��W^�����:y�YJ�aB��{!�?�����
�EF"X�"3�t��e��2:�Eg�����������Q������VE�8�a6�<oC��oC���m�G����y/��\�:�<���7tR;����u���h�b�/:�^���;u]q��������AhU'�1�^;�m;���j����^��}0C�`	j�cN=����]�/�]���E��c������)��v�/���o;�.�s})����N��!:���������p��[9��V�K1:HQ<��~�~�=���������G<�V���w�-���]��)��1����@���l�n��5�x��u1�;�z�/����5���g;o[��|t���k����2��q�����_[���]���'r����;����s0w�<w&}����*���v��#�ZS�ci���\����?��k����_���0?�p�ZW��TM��������z��\Qv��/�����*����L�W,��.����;{-W)��g5��{s�]�������LW����}V�w��x;�]l�O�NO>�d�R���{���p����xz���r����>��m�������my���]>������\-���g��S�����Z�6{��>�N�;���u�f�g~7=�o?����1�pe.���G��3_���?�;i��o���R�=4�w�v�����������N]C;��y]��������������K��W�jq\��.��B�R��O�Mn�����������=o[����N�W{u��(_�:�:���W@/2�����;�[�)v|Z�����:��8W~�F���z�n�92�E���z��N����Y��c��V}��.���P�����q���E��K�t��e����m=��w(�{���-����3��o5��D��w���q
\h�x��u1��v�;����y5���,��0���v��	�H�`	)v�l�Q��b���PS�x4�nG��V:�e��q���H���y��E�:�?{�����1���15�|-wpm%�S~�u����C9H�{�����i'�S�������|n��������N]C�9�-@����V+KS'�1K�;L��}��:k�z������r��z���)v�Y�g��q���vtv�u�[�}CQ'��bg�����X�D�H�3�^G�r����l_~�u����C���k������~���@'���x���;D���7oVn[y���kL����}���G�%������,!�����u�b���t���B��':��2�����4�>cY�}����E����*wr*k�8ZU��v����i.���s]|��}~��}�k�sot0�D'�xo�{���v.:�������6tC�����b���V>��!���N���w(���5cq�1��|}�}���8�:uNuCh�����.����n|���N}���5�S���N�W{u���s�|
]��t!"�%��Q���e��\s�0U� �g���};�����by�[�\XO�l��e1E��V�9�G���,�=�/���S�i)j�/�7R|��������}R���������8���e��x����[~^[U�l>�����N����|^�����A�����������x���j��N'?{�:����F�KK�u_��0�y�YJ�aB����������-��$D���;C��a��)g.�~"`�����1��4�f��\�{q�t���������\;[Q_����z��_����X���t��}��W�P������Is�N����9���:��Z�3[�������A;�������P�b�|���T�.��^)~��2�N+��_�3,����u��e��������N]g�)�'��K\s��Y��n�Z��,��0�W���8�:y��FBDKL�CL�/�7R�X�n���VEG�#G�T�*tq����@�:d�~��b������xL���b����u(�����������Sm��>�x���}�K�������I�\�����{��N�eq���ky�qN��z���kj����z��k�n�;y��?W����k1��q(����n$:��gX�q����^�~������z�}=�s`>�gM����$Eqn�����y���B����@������O�6q����x�}��X^�������V?�:���k�R�zu��(�����H�`	*vv*w����j.{���,Dh�^���w����CP�sY�jb������vE��r��F�/���������q���yd~��i������P#�9����U��S���9���5���1�kS�b~�5o�xm�z����q<�N���|����N���|��r^�x��uk��f����]c���z�gS^�|}he��9�yS��=[�'��u�&��8���S<�}6�>q����F�5����U����w���5�w��V>����L�`	*����:|;R��9+�]�4�J�����;�b��b0���>�sP��NG�F���Y����A�V;@qhq���v�'!:�G�_���]��*���N��+��q���b��b~�c{�smYm?e�z���66���'�S����S�~�>����S{���1EQ���������!��ze��]���P��z�G�����-��V>��a�ff�.��)���.^/�|��J�:S>�b�V�G3��&���;L�*�C�]�R!D��;B�G�)+v�j��S�Wt��u�,�)�W���Ag�����bt��|;�����>k�IM�^#�=^���9XT�p8qn�;��z~����|���,��_;o����5Q�;���y��c*o�Hs�_��n5BY'��������I�s�(���#u�\r�Y���b�x_�xOE�l?�|)_Z��H�k_+��1��t������m������\��q��]�v�)����>�Q�'Z}��w�0�k��0���}�k���@�"X�����_��)v��k���|Sx:�Q��!k1��sl���l�tjZ|q���}�����1^�r��V��
���_>���-�����k�Ct��)�r��^;ok��;q�}�x�����l�N�/�!��>ng�P�`y��8�V�'�G����/��jv����|)*�k>��u�oe����|q=)^_b?��f�m|.��z4;W��s��'��-�I��z�;L��5�w��V�n�2r&@�"X����F���#��s�l�b�Na��j6���Y�A-:��>:�1�<:C;�c��K��~��o.���$8��O��|���i���y��+w���O��3T�k�J'��;���y[�.���������
�'^�x��<m�5
���a��9N<��uboE�ptq?��B5��N����F��g��������(_�b?1/�[l�,$Eg����x����#��~��z�����?Z���!"�%��I�Q��b���t�*��`�)3���U��15���E��5q���|]-�rg���he�i�7��}2���|�s�������;����|=��#���7�itn��y[�.���3����n���(L1_�:P/�N����S�3�=T���K1/����)_��by����E��PM��3�{���v������2Q�v�����z�:����� J�s�����~��R,���n�w�y��9������b'�z�$��kf�p���_-@�S�����u�m&�#�+�;:��dH;�y��r��V�9��������~��z��by�A�8����Z�����^8o����v�c-w �}��2�g�Q>�]_:���P�b������|��kG,ov�*����P|��=���������v�|������%�&�9�+��F�jE��	#�����by���a�B�}��-��}��@�"X������k���yc����^'���4���8�i�Y�,:�E�w��hU;�����z�\,1�\n�3�|���Yqm�� �o�i���5�v��{��2�����|:�������u$���kL�����rg�����s��������k�s=�Cy��
���u�S�����cf���;�B_c�=�����&���)��K��V�$U�$Y�\3���[�h��yL�Y+�b��8��t,vV��u
��V�/��U�O�1�e���Kg��>i7l��/������Q���k�m��P^��m�1����lR�	%����������t�K�=X~����v:��{��g�����m���CR����.BM'��t��������k��
����,q��)�T�\3�N�N:��Zg�����\;l5���`�PQ�|;���n�'zK+���N��Qc.��8��_�U������mQ�:��k�I�������S~n�����k��u��3��|�(^W����uh6�:��W7���z��u1���~*�d�����0�����w�������z�s�!"�%��������f�{B��Wx���Z��8�N��D��� &��\��>)���Y� FGm������������m����<�[���q�Y0��:�7���F�W���?����:�r��f���|�K����L��R>GZ
v�v�
-�z��FS������N^��.~>�f���@wY��0�r����0�����s�!"�%�����!��Q���g>��yt�F��WZU��\�����������F��i>�A���>i���b1Z=����t�������V��l����S�j��'�x"��Z9�V����\^���w�Cn;�����B~��;����T>GZ}/,t��������V�ws������.���`�>�=���|��t��������C��@?"�
���y��Aj�DG�V��z�S��N��h>�����xZ��j�/�_G�������$��A>�)�������y���r���b�0�K/��TjE�:[�[����������O��)?���_=�u�^��w�5n�s������t^�3��6���7���f�sl7��A�z���gh���\t��:��N]�\>��e��k����:q�Yj�aB����|�D�7BD����;O��cM��p��NYt*w��':���J��X^����Ssq~�sR=�Kq��G��Y�c������kT|�4:����_(v����DY>og�����wl�<�?�v����?#��a�U���������;�6{�����tj��~��{N�W�{b9|��{�|����j0`6�k�l�nh�}7�x��u���8�f�^���?�������;L(���(�l����{c.���+�nf���^1p3�\ @��kW:q�D�R��sg:~�x��������<��\��z��4�b$W�cjr,��?���>���C�o���
�V��T���\\���>*w>��nt,w�����B�B�����+*o��;gtV.v:��;v�nG�z�������u0je���1�;<�F�q�5���������>�s?������2�^���ht�Fz���\��������-�����w��F�RQy�8W���w.��PW�������V�b8}�tz��'s��=������s�8����K/����}���m��\��^?oC�������������ku���������+�����}7�������_o�8������F��nz��~ W)=�c�;��\,�����gg����w�������S�;d��{y�8���|.��z��{ot�����@l���b+���90��:�~��f�.���/-^���q}�j����J�?�7=�}s�zO��.���w���y����{�W�]S�Lh��
0�_�H��z~�:E��V�:���M��p�u��Mt�*���\��u�j�Y-4�����(�~�t�ZLK-D����l���m7��ks��Nub�!������v���<�a.������}�~�}i�9YLBD��{���j����i�=i.���s���S�����^,BD��y��y���������n'�����P���2�B������|�.vsj��Y�)��[�F�)��he��':��"�k�SU�;:u�c��G�V������}��br�f%���S��b��z-��d!���y�����p���J�g1���~�o�mX������X��N��<�v����w����m�����v���*�����>�I��I���bh����F���N�WC'���s��u1�s�����]nL]��zs���'�^c�af���j����������z���)�������(�����v��o��������]�.�-:�E�����<"Ptj6O���������et�P;��14z����HD���#��y��b�w.�G������[�G"��>Q���6��:�Pj����#;v��gM�O�s����k�m�9���W5����A��D������s>Z��n=w{�::u]�>��S����HD��}���a��1K�;L��}�����'�P��+�	]��0@�,�t�R�YBD@'	�"DK����^�,�}�HD@�����N9s~�2u���VW&��^I�.�����r��ix�]���\�|9]�r%W�a���i��u�z����irz�&+V����'����<��^�����e���e�H�����+�	]��0t�_�hY����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD��nN�m���k��t���\�t�=��m���
�����K/����v����?�+��$Dt�r������[�O	@��9-����>����K/�
��=���>��O�
�;	@�[�o�>%D}N����9!"�sBD������	@�"�>'D}N����9!"�sBD������	@�"�>'D}N����<:t(
����V�-��~P���MG��k��$D�<a8hN��y�-�!"��=��#�4"D���G�����
hD��I ��gO��f����#@�"z��O�`���/��y�#S3�����Mq�u�W<��#D0BD@����"@t���<h���j�	��	0/1B��C�*A����[���hMm���zk�ML�}����Q����`����X��Hq�k��x�b8P7��bY�O�������X>�z��V�Q6�q�T~,e�=������h;��������61��.�F�"���q�"�m��J9��L���5���"���s�J��jWC���Z�o���"�-0��1�m��T:��Q8x�`n�7���&��W^�#G���{��
hD����xi&�aD���A�<��w���7Z�����D z���s4#D@K:�#�,��zQ��5
�,�kS��A""h��:z����,�v������:�����i��}����7o
@���A1d�#����'�fa�%&��)�E�����{wn���������vGL��8���z��<h�M�H7��������0JL���G,gn�����A�^�V_��9Q����1���M;v,���0�l!�X.HT_m��z#51���f{]Z}M{4�x>�q��a'`~��hj!�*E�^D��G��v"<!���w�93Z!*�����4
*-�hD�|��"��V�*��QMD�1��Q��me���{,�f'��M���Z	��2�M=F#������Q���f{M�~���z���{���j���{ski���7����f{M>�[@�"�!#�t^����G��Vc�
i�r�@�"X1*���7dz����^��\B0�S#������I^�_BDp��u��c���VcHmM{���[7��}�G��������q�����ZW���%s�n �	mB�H`��z�6vH=��?mzq����*��j�i����b���8msiR�BN��� 5���T�P��lI��@��l�A��o5�W�����������3�3�����m�}~�{�s��`�I"�9'���8p +��}��e%�� ���<x0+
f��
j��R���w��'O6>m��={`�H"����[�R�����4�;vd%�y���P�$"J��f��~8����4�� �M���$"J��f��=Y��]�ve%��m���Tl�����������)�kL��X+�$"*�KV	;v���h�&�e�$9�J����[�R�A>w���o�S����l��I0�	�M����s���T-M�%E�J<�Iu������R�I$8
k���Y�� �]��~������Y#��J�7o���"Ahii)�;%�Xb�Ql�$��!�&����:��NrW~���c�M�����hD!v"a�;EK��9[�n�J��	N�	5���4��{�f�j���R�����:�/���;+�H}
2��m��=+��2�P3��q��_��G�T��w?��0�v��$"j�Qo�L$jrd���fi����K����g%`VI"���V")eqq1�+�u���Tm���Yit��fm��&?^���
�m�����Dm$�D}M���+>���~��G$�@�C�D���w���?y�dkI)�&���bQW|��F�
�`�H"`(��o?-��(y%�&����M$$�&�����C��A�A������X8w��8p m��%�+	-�$����'-//�}��eKN���[������O��n���a%mX�$0�H"�IDTZZZ�J���5]1zMf�!g���\��������ID������
:j�$"��9+�F��������@$4C��n�����hE�����F���F4C���eK��gO6��H.�)[XX�$���������o��P(F����$�4�HD��s���4����J4A}m��9m��-�kW�O���D@-{��m=�(����Y���-|v����5+��@���������={��;vds��������y��l	M�D��"�hyy9�Ki��}Y�t��mK[�n��%��$"�sge�sJ�9ID0�$���DsN�9ID0�$���DsN�9ID0�$���DsN�9ID0�$���DsN�9ID0�$���DsN�9ID0�$���DsN�9ID0�$��[8�m���Bg�/�L����0�9ID0�$���Dsn���u
������i���/L��8���`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'�����o�:ze@�8�<������}����l��-+��s���y��lnrz�7��9�zo���S^\\���`vMkG|	�qW�z��8���A{��I;v�����m��6m��������s������}<]��6+�){����+L�����`I�I����kW�u-�{�n�fN�9���G�����&4}r�z��33��
�K[��a�����LN��QM��������`�E�RL� S|�G�X4��,$�3)�����e�����QO���,�:���D`�����-*�JS���W���H�o���P@�$�������Y5L�g�=�5�����^	�L�~�h��_�&d�Q�*q%���q�K&�vI"��m,�H�,����(M�������QB0�nyy9+�����m��Y"�t��+I�O~�@;$���H�,9x�`V:��m����������3u��!�O�{����������x?��{�iU'�i4"�-n�`��+=J\����Vh���o�:ze5E/�E7QFc���b67�n�X���F�h\�7$w�z�y-��>�.�4���_�/�y�����`z��
���E�iql���7��:��X��[�����gK�+;���{L���4���s�����8����{4���������G�m�+Z�I���,���2J\��}{�����@\��s�qk��� �B�}W��C�����%@��r��c�_cQ�F2��0;��WZf]Y�$������N���1���<����<���MkG|	��vY�w�������:mQE����S�uE�3�����a�+��y.�;��7�p������fD���@e"4J�T0)q��$Uzb��az� L���7v��E��H��0�A�A`������S���-
�Y%�t��r�E���,vN��hh���Y#fii)+�)=M����8*nEB�Fp&myy9+=�{�...v��;x�`V�IU"���`p�J��8������0ID0���p�F"���I�����@O��;wf�3��I+:'�n���?~����NUmQ:�����]e��{��9��`�uo��W6�_����U#�y�I)��p���Y)���wg��	R�t*������Seq(h�$"�a�7r����)����G��T�,hT&eyy9+=*�...f��	R��*���-(&����Dsj7pF�R�\����
�x,�x^Y/�u�_��^E��]�Q����T���������N�E�+3��Z���������_���ELm+��s�t�j��At�����]�Y�{.�}�Q�k����D����1��O�����zg�
*�=���Y�g���X��	���<�zE=�����{�>��n���}�wj��,Z�&��#��(����� �8�]���m����u��6��v�a�_��^E��]�Q�@���z�>_�v��c�k�*Z���}�i��t��h�����nL���Y�i��Q�����=V���8��quw��l��Y:�b]����:c*"�t����h��}������F�=��<�z���)�h]���=����x����v��;��b~�{�����X��m��
��e�������k���;
�}���+�>eS<�����������^�7��Zd�c.��W�����e����m�X�:=V��w����5%�{��.�X�:�rR�3
��b��
����tjk;�WLM?uT_u�n��D��1�Q��am�����O<'����e�z�K,���Q�h�����NW��;
�u�`}���������=�6�X��6����mEu�����M��a�.��A�sU���i�mS�O�}�i�E������]�9��h]cjj}�<V��.��]��o�s�L����]L�m2Mb���b��0��*�g���s���b����u���9�O�1?�Q��Q�Q�]Qo����{<4�Mz��K[�~W�{����m����yT��j$"�qe��-..f��D&[�lyX�x}���do@QW�9h�y���LM�R�5m�3�I���z���7}�Q-�u��A��xM�~�^o���kK��s������S��4�>
���\�rmV��o��=+�G���<\��3�y0�1��u<�����s����w�P�.���V���m�J�+;O��@�<��NW�������n�A����NK���?G������x-��9�������E��{>R_�\u��~�A���k�-|T��b���Bl�x]�1m�kP�J�3�����Q� �b<M�n(�]7���Vv
����&�X4�H�'\�h���&/��
�L��k�����o��Z782��~��F��{��:��Y�b��%��i�s�+�M�
��`\7M��j�.��n����
QOS�� ����wg����F=����x,[��D���0?��@\��A����k�j��?G��T[$������*��F=���a��imE����#>W��i#���8>���������nl�,�������W���b6��l�����i �fXUcG��o�m������A^��k�d}��e��i[�aM�s�{��:��4�(G����������
���+��su�"�~�I���j{��3E}�Y�����=7�6�]�=�KW[�s��z�>���}?����G����F
��u�G�����D����k���L�I���i��v����2u�.3��G^�6�A�g�6�QL�s�{�Pg�h��Z�_����=��������4�������5���:�]+�1���I�scj��R���\��wh����,�Q���C��X���T�P5+��=�����L�0���w~���6��_LU�^������m�V�����~u�{W�S��U������X���t�X���^_4�c}F��E�+���]��U��b�o�i�u��}���N�X����?E������~�4���w��u���xl��)�x�j_����~�&�U�:�T��oC�6���5�n��c��~�L���su�S�zb����������FQT_�{��h�GE��iR������i0�����{4���WL�� �^S�����)�x�_�A�[U1�mC)zm�TU_�o�k�S���Q����o��6u�g�����)z]~����*���u?OL���8�kLM��$���:��{���:V�^W��g��+������w���,Z��)��j_5y^��:�=��hb��>
U�N?E�u{�����^�������������w�����b����l?�QWY��P��15!�aQ�1M�<�yP����o���������?���_�z�����?������S���i���xnQ1��lE�����>���P���i����:�� �k���eS�;
�J��S�u���6�qs�j]�}�U�KL�|�������W����]������NE�u{���:�<����:�n�u�;
r�w�r�,RV�8��C��z���}�N�i]/�E��1���������i��j#������ME���i�j��*�����mS�>��a�>G�u�Nu?���kLM����������.;��y�u��KLM�����e���o
����n�QT}�Ie��^3��,;n��:�9���I��W���4�6j��PT_\g����cF����}G����������0c���
m
Q|����t�o�A�6o�������6w�Q�n��mK{������zn���������9�X/;���{��z�o�c�ce���Y�\����kR�qs��u�l��6T
�+��b�������c��k%���Zu�
�=������+����*��5�w���rVzT�gnK��E�o�����m��:��ZZZ*m�l�
���D\��A���ye��Ml�Q��s4�.����n�[��+��~�f��]��qs]����6j[� ����\�����!��h�,���su�
]U�\�����N\�H��]�:�W���O���������)��xR�6�z
�'�fL4T%
x�k��K�T5���x3L#BYcR���������?GY�������x�y���~d�]��}��xM�90����={J��m���[v>5 �����ub�cl�{b��=���Q>��e�j������u������N)�i#����70���0l{M�J�&����m3�q�x��v�a����J���A�]Y;�����V[� ��E��������e����Wj���g�=B�u�,>S��X����+�w��a7�$����������G����.k��{si�6n�v��jT�����?GY�{��6��<j�c��5M��~���cn���-
 �+0R��m>��*���e��e�Q��e�%qm�s���5��_ gP��:1�:����v�m���CW�j������6�Q�s%���mg�4u��s��K[_��6�5��R���gM�����g�-�����Q�u�O��m�F���17+��Q�g��[�$%i����8�{�RI"�1�?�{��d�/c���}���M�6���-��I�\d��D�Qo������?GY������O���.���ue�����P��<L�R�*;�F=���E4��lE�\���{B����F*2��A��n�������7��}6���;hb�tE���S7���
>��o������p��aL��v����k����9�)��C�3�5f��������<k��.KB����������	E��8�Y���N���Q����^$�3@�$�����Q$��F����/��'O�������tC���m}�5��(�ug�cN��}����k�(=:
��c.�w��.5������-�� �h���:������a��);G���U������!��K�65|q������a>G���m$D��a�Q���DL��ym/KTj��2M�L��<�g����d}�:���o"�(��I���Ds`	D�b���!t�����&��~�F��m�<��4m���Xe�=��
��T�,�T���!������I3E���V��w]Ypj��������#���@4+��M����I��h�]r^������;Km�Mh���,>%9`����l��m�"eI�����J��4�	�1�v�������D3,��n���q�����10z���{�bv�+HRv�E�m4F615�mV��PG�1��E�����]��O7��nFM�+����.�}E�0l0��7i�u��{?n����hW�I\�2e���Ce4��kc����p��E[���Y��V%��������X�A��Q+����������`����HA���y�o�
���4�����J��HFb���4��r���,AmZ�U4����91�����iY��9P����@�(��e��
>0^�n��+EL)n���6&q%��<��2^E���L��,6S��5M���+>;5����b@u����A����Y���2�M�ID0���a��t<��0m
>�A�n@'����F�2��2/�*��9�m�~�4/��8�K4�v�ysf�X��I�����Y��U������Y�n���t����1����N�1�n\i��+�����i��nkc8��3�6U��fis�$�]��S��<e�;��O��"~��)���O�I��;�4���|Pg�_�C�C���S��:��A�q8K����;��X�J�	E�j�7�L"��Z��������mq1n�����;V\i8+5fP����m�i��7��le�r���$"``����������T�b�{\�b��"e���@�8{���&�+���L\�A�����g�o����<��sgV�&����R�w�aECFL1����'���@VjoDLN\��Z��4�=D�r�bV��9:M[���&��{�f�4���Y�te�[E���o��TO��	�q%&�H1LB����i�m�����=MW���-�	�}o��7e��yR����`4��������
���������4��*3�����tq�5o��e�}R���}��st�Q���`�x/��e�R��gPo�����!�����vI&�1��YIzWj�J���%E�o�:�[)��\o����Z�w����|P'zA�aZ���f�X)��zOoM��~�6�Y��K��J8��>��^��3��^�o����&��]X�5�_��/��<��� f�x)����>J\i<V�1�R��*��E���w�($����++�)�<�u�zMaek���n���e��c���m4K��F�-[�d��,,,t�E�����Y�}��l}W��}�u$������y��%15�U�����'��8����c�"+�-|e�g�F�Wj�J�����g/;V�(N@3$}E�DYCMz�	�@?E737�`�D���4K�/u�5 7�*KF�%U���^/��%z��Q��OYoqa����
s�]�A���a�����Z6�Q�r�,�Sc��>��%������F�`p���V|��j��Y!�Tn�ce��u�����k��Xl���[��k�(ID���h����B�Qv3so��0�����=�E]1�q���Tv�n��;�d���6*PD�f��O~��}��n�$pR��Q��
t��m�D��
��R�y����,x�]��c`��������bU0��1�<�J����p7/3�Yoo+>��}�m��W*���9���A������{l��MZ�q5���0.�����n��&��?��XJ������&�H��kZ�nct�S��:�Fi���l�L���aT5����++�W�]b�G����/�@e������o���}M|�w����(�i	0��G��}��3����$�8��`jw_�G\�I(k���i����2�m�e��&�s��K��;����4l\i^��u}��g/������8T]OW�v�6H"�6j2��
I�e= 
�%P����H�����v�]Y��8zH*{�X�Q���m�V2B����,H��A�����+�C�'��z
��Y�����0��Y)�\���^���8���~����TL�xf��m*k����e��k[�<���&�t�a�J����,F	PE	4���YV�W�v��H"�*kD��h����e7wJ����	BUoW�6TW(��CR�{{NT]�gu��D�A�l����,�b��m��4�X2�>%(�s���5e����a��n*���
�v�m�6���7��|Wb����-��a�<s�3�m�U��aF�����9[�������������� ���"��(�f���Qu�+e;@�$}U5��h�kt��8��n����_#tE{�17hCu��]U �iU����P�\
U�i��=�j;�gt[��8y�d�1U&E��j,�{��;f�/�5�v��d�5<�m�	0��gT�q0��9��9�� ����ur�� ��c����-�*VI
��K���b���1]��R3�����k�X�,�a����8VV�v��I"j��&k�������R���Se���F��F&��
X��+��zZ�s�w�f�W�������e�R����W}�a��$�S��A��N��.e��y�/b�����������[w��T�O���������5����Xt��3���G"SU0����Y	�'UmU���O�vV��j���_�~�vp������xO�:^u.������/����fY���R3q�y���U�DU��b�� ���S���b?W]��'���D�R��&��
��S<�5E�GcHQcR�]U��q����{��=��	�L�����1Vv�U5n�5o�6U�x�X��s)���`[g��^w��ue���mX�Z�g��E�}P���o�}�/�����+�����i�=��zUm����Xf�LU�����M�JLJ����6I�������v�����)>o�g��5������!�t�(q�:����]���+��$��u�������i�`�)�]������ �ID@-u�b������io�az�9�B�,j���S�=�B���k���~���A?���'���v�����+k���
�����`�0��V6�O�s��zu5�~m��#�8F�*�����iB�����W���n[P�-�;��t�*������c�G~`:�c[x�1�^Q��]��n����W:� ���:u����:���}g]��(ID@m�4���tj�n����������a�A���1u�>�D�q�c���O�gjCw�O"@���mn�^+�xo�Z��1�����_�p����w���M
�j{���++�d���^Q�����e���Y����2�m���M��f�}�m%�4�&��0K����<��jZ���&E0�n#���6��h�����
]��a
�Q��qy������8����L��{.
���"^?M��-�G��e���z���a{��~W�c]�xo�Z��1�mk��P��MOU�~O�:�s[�{������hD0��M`���xm��}q%��I��c5^3+7�3]�Z5�m��si�k�,���B\��s�Y����,J����q�<����r��q�D,�@�?��
��pS�x�w��/��5�,//g%8]��/�u�����@����{��8���a3�-�1�����t?SL���+Iw�w�������
��V���---
���c�v���=b}����A��������h��q�T�{�*��� ����0oi_��c��������+Q$��~m������[�<��7}-�g�m������z������ �\w���\|�Y;�����l��Y����`�,|��d����Y�+{�J�o�h���Fy`�D�%����E�H7����/Q_v��}�D����_�8.���iZ�8�K���6��7/0q%�(jc���4�(�~c�������V4�c1�IP���5��X�l9�����I�L�����%�h�$"V��������09�����u�	1}�z�/`vu�o��q��#U�I��p2R��P0LQ��]$�����S����>����g���c��l�Q��M\#`A��y��R�}A<����q���KKK��{cJa��O��]�|�4W�mE�7�a�P�;CS)9�l���=j�������o_�7-�G"����kZ�8�KL����Q�}�:���@������4�(�sV�?L��D��a�����"��IqsA�����hGw�^Emt���<�h��IE	D�a�$���R��[]E�oaqq1+���n.�^O����Q�Q�v>�T��{�6I"`j�b����R=��V4,gW��G0���*��`���Y	hZ����9]�
�e����S���`��T6
�{�6-���N�����!�����h<��}{6w�ht;x�`��E�����@��/�7&�|�@T�;�$"`T��_`Z��Ae1�h���sgeL(^_����0���x��R�w�{�&�q$0��Ft��������7�Qu3B�+@$@���W�@����N���H`P���Iu�!��a
r3���)����A����@�(�8��
���8�Y��0���,z�i��������������44
7t0.;w��J��D�L�!��'�Q�>��u�����w��`�"ms�L4��G�������w���qY8wQF�`���!�:{����������h��hl��ukZ\\��@��{(��z���;��
��i���/0�8�<�)�������i��i�=�k��N��;(:�s/���8�$"�)'��A�q�����$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"�9'���$"�s��`�I"���={���RZXX8m����5�|]u�x���Xo�E&�1�M�m�����`�,���Naa���W�+LD��������m[��sg��ys��ZYv����
f���i����\����������0i��_"����:1���1�i�&�(�#����M ����Z	9m&Epj��-�\=u�&M�j�Mt��w��l�����i�&M��%�������+��G�e����r����	?�u�qFYo�4IDL��c �K����hnyy�2�S�(A)�I\Q'vuF
j�n���(������v�L�IDM����T�=�z��I"bu��s��������'�������%�;���AQ���V���Wx���t����

2
Q?u�B<������.�kzc?�*�(��4��T��D�WU�Q,��{�_����`�I"�#�K\U��+F����2����h�A�:�I�)���<x0+�q�:���XN>	�W�#�~u��u;�k�n�f�����N7)��Q�����:A�:�����)��o�J���-�0�ab4!���������;�+��q��n�v�����ML'O�����J�
$���6��Ju�
p��Q��������B�s�����T�N����`�I"bh�#�4�4�QFP��:@=����$#UudW�(A)���`�H"b(KKKY��~	<���s�����gaa��)��I�%��n�r��(	:������$"j���H��D�����l��=��'�����z���[�l�$U%
<x0+�2JbP�.�����A�8����Pm�
�N}uG���H���{���w��l�X$���Q&�/����8@�zp$��l�DD���
;�O�6�N�<��i����ey�H����)���`r$1��HAKKK������tJ$��E��o����y����H&���.�G3U����ukV:e��]Y�Z������Im�
� ������M&*K$��s�w���	DU"�'^�k��z���1\o�p/�����{h�������BgA��!(��u�ID�>�C]���;zQ��Hpd���c>_�����������l`��=�����=-�c��5�#���Ep���"^#���]�y1RQo\�W>��{\
�����c�uO�%`��E5�(�c$"��-(?M������NY^^�J���Q�����t�}���}�k�`�D�
0���0����sf�sf��a��%"�W��"�&��)��	D���M���'��Yw��`r�����'L�s&��������s��_�z��$�#���E`(F��u����4���Be����{��'}��_L=�P��5�90{zor����f�sf��a����H�A�D\)F�����1!�#���70���?a2�{09�=���<����9��#�m�e���u�lE?.\��'F�����"��}��lnt�������w����^�&�z��Y)��/�8+�������5k��
�����:t�P:q�D��~��t�Yr�a�8�a�9�a��y��Y)��7f%�	��_���={:���;��8����w�8����+�,--�6rQ$����0��'Ml	&�����������u���U�:e�]G�I������]�V�^�)�r��d8�`�b���G�v�\pAZ�fM��K��yEqwa����_v����n�^t�I9�\�;�����z�I��?���~z�?��`I"�3*O��Sd��|������;�P�|��(uP_o2���[��������E��n�v����`M7`3��>����={:�I[�l�L�$�k{cv����R��cGV����#+Z\\�J����z�cFu��n�v��8� �8�}���� 	J���A�"u���9�z��\�[���:���T��]���)�7o�J��1�2�V7LID9�I��}�u��#Ai�������"���IN�t���:#���?�w��Jg�},^�/�4-u�4�DD������)[�l���c��^;w��J��R��;�=��]�x�7�S��@��H�7���#���OUGo����KEu/..f�3�Y7L3IDt�=!"�k\o�$������B���:u�A��@O7����x�|Q��woV`[�n�J�t�?�"^����|wE�:������x]Ul)�Y7L�����),,t��b)J��'�|�%�D�e��]����|��NMs���[o�J)]|��Y	�w�u�w�G7l�P�=L�C��'Nt����Og�%�f�sf�sf��w���R��qcV�0�q�%��-
�������f�� ��q���t���Ny��ui��U�2��#G��c��u�k��M�W����v9�`2�{0~w�}w:z�h�|��5k�t�@���W�q���G���*��:��
Zw��b��x~o���L{�,$�3L�f����������$"��}��N'�!�����)�������;��=h�O7Q�[^��x�"�fD�'b;e��X��ac4u�4n������h�B�0E�C��z��Y)��/�8+����������

�g��u���t���Ny���������,q�ls�l���;�RJ7n�J@�5�#��Cl	&�����������u���U�:e�]G�I������]�V�^�)�r��d8�`�����t���N��.Hk�����v�56�(��.�s��`�I"���[o��f�?��X��b'��	�ID0�$���DsN�9ID0�$���DsN�9ID0�$���DsN�9ID0�$���DsN�9ID����N���f���v��y�I"`f�q���7�wO���}4���?M?�����������x<���$0~�}�����-����M�?�p��t�<�������c'�}|3}��;��<@�$0�n��k�������dK��������v�=��|�=�������;�4���?��2������x<�
���Z����>���o��x��	HM�������6:M�l:���������x</��4IDL�A�0@�����z�H�[�'b��A;<�����7�&I"`*��M��J9'Of�>r�F��� ��x���'�}0[2�x]�~�$�2���:1r�i��J)r�z^��l��A�0.�0x�#�1"�IDL�;�y���j>t�����a{����n�tV�9Y3���5*�`���*���Y���Y	�&8p ���'-,,�6---u�x|XUu��Q�Y7+S�tZ�f�>t�����E}�#@S�C-'�|��v��V�H���eK��cG��Q����L�x<oP��SUw,���a~������{L�z�g��L�����{]�{Zr��$05�;���l�Q����F�����H��#�7H"Q<�(��H<oZ�`���'�����-w��7IDL��<��������J�"k�	D���O'O����{���m������D�Uw�x~�Q������C-'�|��v����&���1���q%+��|"N$
E������%�l��=�����D�~	9[�l�J�D�OQ���W�����������������;~2��$"��S�yLVj�S�^�������);w��J�"�����rV:S>��(��+���}������m\����S6`�H"`j�{�x�{��>�,?*OY"N��hDe�;���j��6�`eWr������#����f�Yi�yO���������$�Q8p +��{���Tm���Y�������z�c�R��r�������0U^��s�R;��`�h2q������)���Y������R�6��s�Or����?���?r,}��fs�8�����=5+��}���1�O^<������y���4�����T���Xy��>+�;���\;��x�a�z0n;����knK��+��[8��+7;�����U�I/|�9����sgV:e��]Y�X�����FI�����f��y�c�R;^��s���$0q�����[�����/|#[�����f��C��G���0��g�������������={�%��D�<?
Q�(@��VT����6�`e������>������fD����_���08IDL����P���G�%�ra�|��B�S���g%��}�����l�Tr��;����w�-[���t�G{����`�}�S�+�������-���6BGi�/}�����4
`X����v��7��'O�;���S`z�����k>�P+2@�P�"i�X\�@�������Q�7��J���G0m"�S������l)@����������/>�-	���V�f��C��$"`nE����.��7l��)K(�������|�����y([Rf!=��������l��\�Q��y�K:=��������hCu��D��x]�n�����f�LV���"�t�5�H(�:�����\��?��g�%y�����=��.{~V�$"`n]z��Y�t��"���n�;����+;�?Zv��'����/M������@��_��B#�$�"y(��b����;�KL�w��,������J$�AE��HoB�d"���>��������|5[Rf�����V��(F ���p2Z<��p��h�C��z��Y)��/�8+���l�\rI6��H*��*��M�[n�e���Jq�]w}�{4��i>��3:t(�8q�S^�~}:�,��0K��0���0�z;l��qcVz����Hz��J��_@���x�y��{���)������O<�>x�������>j�yOL/����/zjg"���_Z���?��6o������t���x��7���������g���c��l�����{�zcK��~V�mu�<����H����nJ�_~y67}".�dt��
��������wq|��.��{&���W�{�k_?������:�H���K/|B:�WG���p4[R����&����i���-�����WkdpE/K"�TB���O�=.Doq����A�~f��q�D4�:�����:]{������D4�$�ls�2�6�0�6�0���$��}9�����=�?@��������O��?#[R��������Vz�9�I���&�Y���|��8�����O�����]�o�o�����{\
�����tZ�(<�Y������;���4�^��d���x�����D�������O}*+��6�n�O��mS���o�w���:�Dulx�c�k6���}��������������,}�����'ds��H"jFQG4�3D$���qB,�^��y�c� ����Y��o��7����d��
����{�K�&�Hz�S�q(��@0>w�U7'�0��������_��@��}OHo����I 
��O_���#/{b:��VgKO���x�"�
F"�4�`S��Se����F����H��4�=)F"]�kX��o��hv�f�`�9�a�9�a��G"��CG���s(�������������
g��8������������Y�{�zcK�x�+��l[����l�Rg$��n�)]~����t:~�xVb������l�.�`,�-���?��t���N���~Z���L�Y�=����=����=�1��'�M����IG>���|?�?����?~A���O���;z�D���������z�c�����a+Z�/�e$�f�q$���G��{�i=�E���]��x^�c%_>03�uO�$���M+��Q���7M$�.ID0�����9��9��7�����N�}�3[R��|mz��ges@���q��V��}��'��g���F2�w�6J�N�z%	�Y���cl�������*]s�5�\�YH"r�����'�h��u��`L�9������]�fj�L�s�/:��$��}�����y����#?��t��������a�;,dtEm��PtD�M>	'����D@$���*�xU�O����m�]w]V*�����?������k%=�i�Ko����@0�����R3�n�����^E��Y7���k��tV��HE�|��+�������N z��V������������*EG���k���Y�X>I����"��%���'�L�nf[����M���:v<��^~0����T{�����{�K�����-�tF'w�v
������f���:���E��{���K�����>��oeK����������U/{F�`�I"�#?�O���������)������:=�����8m��|�����6l���N�`4������Hv�#��j���_�����_�-`��v��g�#��[��T��������w�Y7�#:�������b�\sM����9`�<�������Jo��t���li��c�O�����3���l	���DDG$u���$�"|���V$x)1����u�"��D�?������p���_�,[R��s�~�g7�������0��������T-�)]��������T��]W<���TVoh�nfW�D��.�,�<y�O�A���~��W>����_��T{�s�I�z�K�k.~v�`v,��V�(,,t���B1�OoN���{lE�t{����������'��[o�J)]|��Yi>�g���K����`��W^�I��*�h��4�[n�e���J�N���\��,0�:�N�8�)�_�>�u��{�%�a�m�a�=����t���|6W������M�Y���G>���q��V�H�M����d�-[�ds�����tT���U��A�
m�=)�[�T�z$	E�� ��)]w�u�d�a��N��������;�u���U�Vu�@��9��;�����k���F��qp��d8��]����N���/���"q(F �Bt
{�5�+jcuC�M���P��r���6�fvE��;�hC�$�x~�������7]���	D��������H���sgV:%�"�(:s�q�X6h2N~��x}o�e����%��Y7�'��zcJ�&��������^������>��p��j'=�)�M��_$��y��J�z�%�����������Y7P���|1�����_���lI��>�����������eK��L�@�"�(:���
�S$��vN����}��3:������x~���6�`�E�P�9>y�C��j�������^�^���fKf�$"j�����uUL<��N%y'_W�u����'����3}����%�^��s��W�4m���gK�7�D�|BN�:	D]{��=c��2��x~]m�
�����o���H������T[�*��~���^�1�����R��&����z\0���K_�V�g���?/6[R�o��w�k�paz�w?![�����nBN$%�c�<t����	D]��]��[^��aF	j�n�������_�H��'���T�t�c�?��s���?#[0$Q)F*��QF�f����@���K���dK��o��NW���l��"m"�(�H�����A������Z�L��c'�;�s8���������V���IW��'�g>eU�`~H"bh�����N���T�l4�62�=P���������'�����lI�g��:�����-/~|�`2>|�W�o�H���U����g=1���/I����-�?�������������wg���M&j#����?r,����T��?�'[Rm��==m�������&[0�����7��>��odK�]��Y���^�^��I����p22C����Y�+{�!ER����������RJ[�n�J)-..���81Q$um��-���7�;e��=i���\�$!
�Y{��H^��}{6�n����[o�J)���Yi>|�CJ?�S?���kZ�cE��:~�w7���@6�,9q�DVJ�����������wH\������{f�s����M�{�W�}_9�-���<;�����^�=������m:
����^�,�]U��0��%��[������lW���7��.���lnrn���t���gs�q��������O�n^�n]Z�jU�����#���c����k����;e�]�=�����N����t���eK�=����+_�6�����-I����NG���/����f��`�����������(jc�D��n�Pob� "	'@���R&>o�@�+��3J�O��&����=
z=��w^V��g�~��:�k�UW]��&��k��J��������������������q����������P6W��sV���������dK�w�@���=-�c��5�#�4��wD�Po�m����vXG�$
o��I��wr$�d��&�������n<��t�{�ds����O�$=�����$�dH"j^Q�$���8TfV���<�L��D4���D4�$LF�6�����u�L������'���]����������'qh%�D49��_���Ce$�l��V6����D��fj��L�s�{������?����%������\|�$"�ID�+jc�D4�����DB���{����/'�x��>m�=
z=�x�+��|��>�I���"�������%0�~�F�H�fK4��8q�S����rS$��0�6�0L���=����G��>So������3����=���CY)����g%�����LkG|i0m%$n�|�D����N�$"�7S�d8�`2�{P-�"�(��x��'���zmZw��%g�D�!��yEm����'�$uG*A��:��iL&�'���q�d�~#
�Y�4��@OW|�K.�$���-��2w��������=�a��N�1q�c������Mr0[��0���0~��_Ho��t�x�����:�����+.��l��4�C{�5�#�T� �Ck	��Ku�$���V6����D��fj��L�s�E���>�������y^6WNL�Xc���X%
 �x�I,aqqq�`L7A�_��Qs�}w���)���2�OQ�S��5��/--�����6��4IDID�K�67/�ls�ls�x�p����?��l���������
��O{\��L��=��_������}z;�&���~������S2���D����N�$"�7S�d8�`2�{p���|5]�G���=[R���?>]����e/xJ���$"����������D�IQ�%W"�&e#����x]�>�p������\�X�X�n`�	���"�TW��E������
`U)����`^����L����	D�������Ie�����#�|Q�%"����	u�K���R7��E��7v������]��lz��Q;��G~�����^T;�`�I"��;�N>���8�V�mU	Em{I��Q�z�(L��I~���n����"m�
�������w~,}���dK�E�To�;������l	uuNq����6�K(j��:��~��~������'[R���[�v�ncz�k����v�<@�+b�nq���Cm%��&���"�3j"Q�g&'����| l��]Y�\�9e��6�f����;S�S���)�����J��w��g����w��>|4[Rn��g�����������P������h_�8�8u���D�����Dl�7���%���O����C��+�����O�� -������>���==[@�$��"�
��N��`O7��I@����D�x,`��sgV:S>8��������*m����
�\v�e�i��
���k���00M"i�_��'��wo�����f���/�$0��AD�e��Cy��D�"0�n�P��"6�S���K/��6bR����9�v�����wJ�:z"[Z������/������>6[@/ID}t���m��X�X���=@��IB1�Qo2Q�#'�@��J��o�n�Oo��^�$��/��f���n�g�O^o�'���~�i������p�m�����-���?�6����O��Z��0����IwN�+�%�)��P.�>���M��M�:�GL*��:�j���t��|$���_��T{�s�I�]�������-��$�
��2m�Cy�ij��|"��;:	C�)��D�xM�d�|P*���;�+_w�@V�u3_���Q=U~�i���\��k�����lI������+?�=��[��-`��<���b��C����]��S�tP������_������-����</����<��l	e$q�l��Ku�s�$�JE�N>Q�H����A�6�f~D ��`���^��N�K$&����i����n���lI�-~W���/M/]��l	���t�te���[I$�G}�����Xz���-����<6���nJW\vA��~$q��(�l"A(���1�c�;��� E�N$u����=L/}m����&��D"`R>������������lI��Yzn��?��t��WgKV���4�!]���Hz�5��O��P����/{z����|�����DD�H���H��d���1
�<������n��\7����M�6e�b��q{���KW��c������{��k:����?'[����@T'AH"+�}<����D��?�t�����g�7�f}���m�A�$M��:��+��2+�_�}�q���G���}(�����OI����z��Q�K�����s��I��`Rn������c���x [R-�7�����/f��AI"jI����={���B��eKg������4��o*�s�]we�ru�����/|=�����|>[R�5?;��/N�|�c�%L��8Q<��||)bN��b4�a��)]z��Y����XI�;��������ugz��������<��x�3�-`����T&<;v����o�����q��6m�J�n���t��Ww���6n��=R.^S����[?��t�5����Z���c���v�nC�'?��l	��79��������s"�����9�kuT�������'���������b7�|s������X	>|�W�o�H���U����������������dK�$��D`&�{����5�dT����5��o�����{����k��6[ZO�&^��~��2P�o��=�_�����9�-)��y��_������=#[�4�����������yU��u�yP�N'�A�	]���)�s�uS��L�/�������~����g���lI��7?+�����K�>)[��$5�(0��-.���F *R�y@�o�1+�@M�����O�D"�&|�k�J���"������T�[?�����.L<����AQ�sE�E�Q]:���\v�eY�X�����H����J����No����?��g�%��r�����_��|��l	M�D�������bV:%�T����N�<���m��-=e��p�M�6e�35�@����n�)�;S��@������X��;��T��o�KW���l�iR4�P>V��E��nl)�L������P�����J��J �����I��|>]�+M?��lI�W\�]���^�^y���%4IQ��u�7�7o����]h����=g������z��q�wd���|��Y	�y�y������O�/}�[��r�{��5o�0���wgK�&E�EBP��z�,����3���:���dQ���X	x�[����'�u�=�-����^�����0�{�c�%4M�����EP>�S&?RQ��ukV:Eoq0�
6d�3]~��Y�9m�����������o�dK�]��OO���������-`��G!�D�|�tE�F*���h�#����^���t�M7e�f���L�?����o�����r���K�>)��[.J?��Y��"��a;w��J�+J*
m��=+���8X�n�����w~,���/eK��������~jcz�cVeK�v�T�@�O�wF��{�������)F��������G�%�^�����_�����'fKh�$���|��<���s��������t���_�7��m�����-)��'?&�����w���-`��&��%�|BP�3:�]�6m�J@??���so�h�����%�����/����#���0���d��Y	�W7�xcV:�]w����{��N����������������_��l	�`��=Y	�7�]vYV:��7���`v����Mo��������T��xf�������)��E�8p +=jqq1+m�J����k�Rs�F����NW^��������%������]��E��'=&[��X^^�J�l��-+M�HrZXXhd��0����\ZZ9�����~UI;6l��DnW]uUV����/~#�����~�}�dK�=�q����mLo~�����nc�W�	��ysV:]Q�0�;��#+���;�����$���.���:	D����lI����������eK�w[�n�Jg�'��H���eK��cG��Q����,���a~����p���f�b1jPS�?QO�����L��}��t��|4}�S_��T����}�U/{z��I�D4���BE��f�Sv��������`xU=��H����Q?��B�uV����i���JG������p���uo�0����eK�5��
%��H`��}���t���N��E������Q^</�_W�u3[�����T,F��NQ���i��#���w�����C�[�NdK��������?�)=�����0)��F�Q����|��*1(����������?�9y�d'��:IE��x~��_Q|����o�7��~�����T����Jo{�������l	�(?�����)�N��������I�������%���b��������i��NQg�x~�Q�����s�wd�r1"Q��\7��O7����@�������Oo��3���?��lI�<����W]�^{���%L���h����BgA��!��^�z�P�`Gv��uF�h�=�[+������R������|��v�%�ds���L��L�n���t���gs����[�n���<���G�Y��,0�:�N�8�S�����Yg���Y����f%9������P��CG�%����7�f}��xf�d:�
�]7n�J@�5�#�4��m��%�;�H�s��N�r����t�,fT��i���c�j}���$�����3��m�=)�[*���%~���8�m�|^���9|�p:~�x��n���j��Nh��#G��c�:��k����Ww�@��{0�=�������{jv�~����+.� ��-w�}w:z�T���.Hk�����v�56����]
��N������B>�(�cY"�S�����zb�H���a�������3����;j%�{��uW�t���/�UzG��Q��"��O

���-E'w��V�X4
b�{E�N���?eI>!������W�u3�&=
PtJ����>����c�������_��M3�@0�$5 ����<x�0��+��L2�s��Wg%��������~�������-��7��3:	D�sv��y����L�����|�t�n�����s|��XX>~�/n����������5����}uJ�4��|.�������}([R���==��������l	�FQC����7���7�������hF}&���xo�������p�m�����-��s�]������V�:s�yf_$���zE�(�A](�IU��3i�@#-�*�\]��^E�TE�n���N)1����}7n�H"��GA��/|���_��������T[�������O����������0�$5("@��n�&������'7�e�z	D��q'E�)M������?�������H������q�������~�y��U��"�����RLU1����xnYjZ���++��/y����Y���N��T%&u�Y7�a��D��)���1]�+Mz����j/���t�[/J?��gfK�f��������_P!C,����q%�{H ���'������������^rn���/MmxJ��� bI�����bF�����.�;�b��}��es�����
?[^^�J�����5�D��aaAS�����9�~�]w���q,[Z������/_�����?>[���D4"0$y�#1������%��z��������X����lI��Yzn�?���t�VgK�X��fed�-[�d�Sv�����������L�u3_"�(:�k#�H�tL����t��>�����eK��=�����I�{�:?[���D�H�_~yc�DQG�%�t��C�u���lI�'��&���)��?'[�!F!��'��X/?r��z��j�n�Ot�T2Q�>��!������~����g���lI��~����������O��0K$+Z7�(�61EBPq�A��1u�b�:������wJ'��*���)���_����l	���(D���Y	fK7��S��'���O�&��%0M�|�����?���&[R�)g�I;��
?����f�9���-�61EBPq������<&�C@�g��Hz��O�����%�~��g��W�8=���fK`~;
L�|<)���R<O�����OW�����G��-����+�����+/zZ��Y�p��:��3SB����={���r67��;w
��P��zkVJ���/�J�!>�%�\��Q��[n����RD����
6~�����C���������Yg���Y����fV}��_J�x����#��%���^Ho~�����=#[2?������:7����8��R3"�������������&gii)���/�Ki����c^����m��<���cG6w���f��6��%m����N��������;�u���U�Vu�@��9��;�7���k����;e�]�=��Mz��o���{$�����-����^�~|���������NG���/����f��Nh�Xc���X%5(iB���}��l��DA���D����0���0��������{�����l���{vz�k����=1[2_4�C{�5�#�4�H��eK6��I����4Y����DT�$����wr$�d��&������)�o�r������V���K�>)]���s��"�&C��yEm����FQ�D�rI"j��7����t���g�f\u�U��K/M�6m��G�H��A.ID�K�67/�ls�ls3K�������}(}���%�~����$�3
���i���/
���0�}�O�d��{\I"��7�t���g���kiS7�S���oN�^{m6���_u�I��9o�����W�`2�������p��8���~��������	�>�`���_��������.����<���&LF�w�Xc3��_���VQ�D�rI"jF�u��u��������
��D4�$�ls�2�6�0�6�0��~��N���Z���+bm�������/ID��i���/
���0�}�?.]�������������'�7�t�y�e����� +���O��c���Z��>����:����=�����/����g<yUz���6=�1���)�>������:0<��f]�������H��^�F�$��������_�?�����D���>�	��o�pE$P������"��(V4�4I� �+bf����8��__O���+��_����^s��O �wF"��={��;��Sl7�`$�fT��md���_����5��2�6# �ls�ls3������C�|����j�|����^�.=����%��HD��i���/
'��"�f^:����"�i����\=�#�4=ZP�uOZol��xEV�m�V��w��G"�z����:]{���\}���J���w��������������t:v�����������;e�]�=����������w��u�W�%����U��?~Az����-��{�IG��������5k�t�@��~�.��f�q$�(�� �%��U��������O��#�f���a�9�a�9��V������{��z���?���K�������C�gZ�8�K��o�y�fM|�Q�uz��(I���'mcKE��6�C�k��>|�;ID����Dcr�����L�v�Z7S��8�`2�{�}�/]�G���������=5���k�w���l	!:��&E���`<��W���.��I �������Jg�$`6����JW��m���������_�"�NtP7/8��N��m��5+
&��E��Y7��i���t�K/�4+@}?r,�~�����&������K��E�V#�(?�mFS�DT.zg����=�5��}���-�HD��HD0�����9��9��������O�*����������������l���w0h���q������"�f^:��D�-[�ds)���?m��9��0�����Y�$��t��[���H�)�.������ln4U��5��G���c$"�#2�d8�`2�{���������t����T{�s�NW��������-!�HD0b��+jc�D4�|��6�)���EQ���i3L�K���D����0���0���L��?�pz�{�;�y0[R��^z~��}^6�ri���LkG|i8KKKi��}��<%�;�k*�h��mi����\������6��$IDg����$��#�&���0�=��U~�?��s�_fs�����NW����9�H"��kl^Q��0F�|DR���D�@���/�7\s[���?vU��?�	Dd���Y)�����4�z��(#�DrUW$�������I>���k�n���W_��`�|�3�7\{[��g<���v�"$5�7P�k���������J�!z��_����]w�o=5"V����I��7^�.��i���w��:I,�(F�E~t�-[��n�X��Z\\�Jgj�n����^;s�
3
+���s��w~,}�Fgh�U/{z��-��~���fKX�$5 
�@Hz���:e�=�������0��{���n��v�}���w�w\����g<![���V�Nb����miX�b;���'�;��1�O��������.��1��%<0^_x���_��������T[�������O;^�1���U�RV:ID
��sgVz4�h��=0�f)�IO�t���������;��-�����Oo��u�'QzG��'�pzG~];v�H�)�Y�����G*�f�L�Y����;��Jp��?��t��>��i����OI����������8e���u
����@$EQ�|b���7����[o�J)]|��Yi>�g���K����y��i��
��t�����N"���[�n����~��1Z�=L�C��'Nt����Og�%�f�sf�s�q��?�L����fs���������O/x�������k���Y	h���q��F���G�%�������'�9b���b\�\�O�)�
���f��6����������t�5�ds�)�
��~r���������w����K�V������#���c����k����;e�]�=����v���t�{��?��}���~f����~�9���������G;�.� �Y��S�%����6VID
*J �,$h�$����Zu�M7��/�<��$��%�f���a�9�a�9���~4��=������-������V���]
���i���/��(��	���d�&����=������E<mqqq��k��q�DT��oL�]vY67]"�p�u�
�)]p��ID0n���p��d8�V����t�{���|���j<�	��W�M�{r��aI"��kl^Q��0�FPO$�L�X��h�"�=��t�5��N �������A�j+�hR�S�Hb�Nm$��{�(K��S,������0�1�H��R�1J����wo��n��@t�=+�����@@_F"j@�g�&�h�2�`���"U=���|t��7g��5�1��2�6# �ls�ls��?����������j�}����_�.}���dK�G�`��i���/
��6C�`e2QW]uU���K��Gm�����_$������lnpE1�6����c$"�#2�d8�`2�{+���=��{����#_��T{��k:�����eKh���`2��W��*��E�n��mi���iqq1[2=��\���Quk3@�ID�K�67/�ls�ls���'N����P������j?�=��7�v}z���a�3�q����uP���[
�K+�$��Egue�E�t�8�Q�k��H"��p35L�s&���r�x�����^q�w�����t�9bM�D�!����6VID#:p�@��eK6w�����"8�����o��l�)�	R�~�a���l����Y�7���QuW�i��f�$"�mn^�������
w}��N���|8[R����s��g�s�9�a�3�q���,--�������{�9�$��I"b��`2�L
�����p����<t��<���~)[���_�6���ges4ML�Xc���X��1��f�S"Qe��"	'��/��bY<�S��+�mS$t����w�:�s�{�����w~�V����:����h]>�!�`e����������N z��'��r�"�&��auG��V�$��"�5H"Q[�#B%��s���V���l�����Io�/�R�N�/Z��t��.J�_�]�������o��0�S<m���G�/��'��~+[R�u�zNz��_�����l	n�d6�y�0E�<�/F��M���m�@�$�\�&FE�M���O4��m[�^�z���o���S�cqq�r������'�{��]g|�i�_��zkVJ���/�J�!>�%�\��Q��[n����R�u�]���l����{�^�J'N�����_��:K�=��0�6�0M��/=����C���_��T��W>;]q���9Fq��wf��6n����&LkG|i8��-��a�clI�z}���s���t���Ny��ui��U�2��#G��c��u�k��M�W����v9�`2�{���G���{��t�sgK�=�i�Ko�����6>%[B�����t���N��.Hk�����v�56���U���#�t��6������w���ID�����'rU�=�:O�$�f�x��Y�L�_~yVj�UW]�.����i���|$�t
��D4�$�ls�2�6�0�6�0���������C��o�
�VY�������K?����-aT��=��_No�BM�D4�n��H�Dt���fs���U7��1��h���k��H"��p35L�s&��7_������|�=�\K?��t������3'ID0b��+jc��2�|�M$�����W���s���tJ~t�����AF!��nQ���9]����w�e��NM��R�����P��������GU��BE@Qd������6%��j��������-X�X�\h��-V�E��bo�Z����+��
*16mk+"fep�M�����������s�����_���w��9�L�d�<���w�������68
��[���������TWW���{<v[�pQ2���~RdOI�����;����_s :��\���Dn�J1"��xUq��f
�Ed�GC6�DJ����h!*k��������ErN������3������#����7Oo1���������I���M���\yH{4A��@b��
���[2}�j�����Jl��|�,��&Bx�������'��`rs�����:3��$���uv���E4���jDp��w�mF�=z�7���N��]k���v�Jl3/)���Rl��O�Dm�����,�~�z3:��Y��t{?l��G�e�����S������������G�
�"D����Z3�$H�f��O]}H7�D�}��:�����������[������;�Jt���3����'�
����"{$�7o��@���g���3�x�Tb5�����S�+��h*x#��c�ANN��\�8�a!]m�nUm%��OYY�k��xA�Y������������}<�/_'+�y�� '+)���O555f$2a�3������f�[�~����K��w�]w���%���}�����g�����������o����(]3�K�d�� ���sNh���W�Og���N(��H�:2�T����z3)--5#nH�>N���t9���s�4M��E�����K�Y2�������.]*S�L1{�Z�l�L�:���C�f����D��{����$����qQQ����������,mmm�qaa����~ 8�p��/��(�v�����N���2��!���Hkkkh\PP ����1o�kt��{����d
���;�?D��T��L��+�=�c�+Ycz����6�tV���<V��"J�dCD��q*Z�Gi�-��NX"�.BD�E�6>��0l���g������7��Jl���p�����=x�7���k��Rb�~���Av"Dt(��m���f/���GG
�������7{���u�!"�|�������x}�Y��Y6l�m*�w�2��B������tA���F����J�(I�"�~��V�|~����1����3'j�'����	 �
!��b���it"
.BD@���e �8��`�F,O�����X���m@��r�e�r��cM����w���C)1^�_�� ;":\�BD�C��������s/��&���b��������K
�w/�������>��X���E���
�Dr���4��� �6N4���]���8@���E��F� �]{��f�7��y@~��z��������d��J|�JDI��K]]��s_YY���uRi�����OX�U��Vk��WTT��O����=Y�(��]�H��5K���.����5��������`c �8��`�����{���5����M%��*���#�R�����k��Rb���-�zH
V"������A�����^���Vd�����s/}���;�hI�������g\I_�yi�;��� ]��z���{���� R��+�	)
���;�����|�	���<!��r#D����������n��!��"D^��s6�aD����|����Xz�+�]V,��1�T������}�K@� Dd/H�io!������BD�?�05��=��{����#���o�J|��<\���0��tG���F������0�-v"
��2g��PpH�)
�m"iXH�E@�E��Q�K$@@6����G�����E��J�����s�`���fHW��_��_�8@Tp�Qr�5� �
V"�c�D�Y��;�+
YW�����jF�����\y��f/9AX�(�U��#�<"������$<s�b�t x�������7�3�H^��`�������s��-�Jl���K�������8�3_���CR/]�8�����JD�a5�dV!R���+�`E��{�?8���?�I~��f/�)�=Af^R���oM��JD�?X��}v��"�#�)]y�������Q2!"���=D4d�3��������2{�K��U��-��S����{�������6{�T�����/��[V�&m�������}����\��+G�rc�]����h���o�I%���{���{�=����Y��������BD�-]�T�L�b����-Z���t����!D��S�������������7����Jl�z���C���{�AE��!"�����T�1hHE�t����G�HY�o�@�u���']�(�A��V����{���v�������7��,y?���k]/��Y=���r���������"@Z���Nf�����}�M:�l4<�=%�@D2"�w�=��L�s�����
��7�A���X�(���nR���o����`�W��XW�Q���9sR�XX����I�������=�\3��=��TTT�=w��qvb�(�L�h���ft(�<z��w�yfA����g��sn���{:�OGG��Wu�=��A�9�pp���-���o���.�8T�9y����.��Qj_�a.��b�@���b����Y������D���������I�>������,_�<4N����;�����S&b%��f��%'N4{�=z������Ci�g���f���zJ^�8���JD�?X�����=���U-i��V�k*����P.��C���V"r��{���b���&��y�� Ju�+�yi�����?�t��Ll����v����=o��=�v���>�l�����6��?m<������������|H�a �8�������ee��������G�����]L%��S
���
1{HG��x']�8��g}L�Y�Ca��s�D�����E��N'�K�U��8��C��������s/����=���f���#S�mla�yI�9�Tt���kt��{�|
#
�h0%��V&O�j����^��j�'�"=���Y2�
bEJ�@<�����W
��o�3��x<���R5}"@ ��e�W	���6����J�����=����7��8�=K���mp �|�PY0c,"@`"�C:�F�6{��n�x(
���X�C�O���{������;����VQQaF��yYg�s*��gt���l�"��d_�����_]{��+�g*�7��L�*����1�g��k�-��P�7c��h_E������d�������Jl'<Rn�v�|���@0"r@)���uU"��k�(�P�#Zp(,�q���v�H����Q���:3���Z������^�������9�-[u��o�F3������A���]y�0��o�����M�����uU"�=
k�(����6Vp(�����L�r�J�^�nz �=�����W�y�^S���,�o<C�u��9����A��Mzs"h#f������pS(VH%�qz�d=�X��r��d=�6����q�4���#���1��\��������3�0a�e���?�|��X�}�����g������_JJJl_g����F��������G��@�p��9��6����$��a��*sT�\��b?v@W�Q__oF"���f�
���I���n4�-�)2p��������e�2���{�����?MMM���Innnh�[������J^^^h�[�{�?8����;�e��f�����JlG�+3/)���u�� S���Hkkkh\PP ��Lx��F����J�(A�	%KFs�����c
��X�k��jXYD*�����u���>�F��Qp"��/��9�p�[�i���{��s_������N�!�4	o��I�>���q&r�[��!8e7~����>L
��s���7���[���f����/�9'���?�T������>��X	%I/uuu�4|R������k'����6�c
�(����]�I��z���+m�������qBDP����l|x6�a �8����On�G�y�������oNn�4��x']�8���o�,k��zgz�T��/�qu��dzj^;�e7~����>L
��s�����}�Z}����6���{�H���'�=dBD�?�5���=VBD.J6Pn���a�IT���>6������������������Q: D�zK�.
�;z����������5k��}��f�}����l|x6�a �8����w���m{�����-��p��C���>��t���_J^���p�%�Do��������t�D&�����F�(5�74q���X{Jv��e�����S����$��^�"����p����s���v�=K����Jl�������I��1dBD�?�5���=VBD�P��P�����6t��M�T����6bTwC>N�J���{>'����5x�XW�OZ�An!D\���`���@�q��9��Z�:���/�=�<9�L�r�T��>��t���_rWx�:��������do��s��}����<�y'�2e���f�i?Jo���E�y(�w������?8�p����Ol��?������	'��i��m����>��X��G�i��jt���u_�w������j8H�F�h���^�orw�q����>�U��z�s�O�5�<������6���8z���f���f��v�����.��-�S�C'y��=��������7�!�X4�d�c�7�^_'����c#sh�G?�p�]w9��	�����X��x��-{��w�q �������	2!"�Iv��U<��6���7��>��F���=�4zb���d�
��*������0���'c
9�{C�A"���y���n�!��j=�����(:/�����i�'���b�cj�����[��l��_�
�w�Jl�
��7�!���T�<��d
m�����8q�J�O����=��y�_�|3�L@���p�(�9s��Q�*Fa��B>aZ��}b��������.ZoJ�V��}o�< ?z`�,��FS�-/7G~��"���R9��<S 3"���T!H�D��IM�'U_$�������)
��W#�+**Bc+��?�B>a��U�V���>���5!���]����~�jy������~r��g�?s�����x^�F�mF�2M~^)r����_�H�a���*3�O�F����+++M���Q$�����V���>/DNB� y�m���i��G�e��6S����K��12��^�@�� ���9��ROM4}���1���^���)++3��YW8rzl�@����F�i?'�+Y������G��2m�j���o�Jl'%w^s�\��a�@� D �-]�4��OCC�E���4}�����5#wt���9���oX�8���7#�%z���j3���c#Xt5�D��)M�8��bcb:H�C�7�M�_�7��o*�M��	���3�����@v!D ��=��b[�l�\{������JKK�%��m����{���#Oo�x��U��vG�M���ar��c�*]�h��y�s�m������t&M�dF��<6��iGC�O��)�_��\jo����&�SLL��l�'7����-���;_�|}����0���G�@F��Z�6m��3u�T����M����VE�8�e�t���}r�=������xO��h�Yv���o}a���.2��hxH�7///���g�ja+V����xa"�nX2�����yyl��>���t:�'u�������s?^�(^_�e���e��/���]����
��7�.�}�/�����t�R3���'+���'A"���v�u���f���&�"Xvc����SF�"
ix�	
��D@��2e���>Pw'���})�����V����������a_�����_%���4�!"k���ft8�Da�8Z�l��;\����O/�)��r����n*���Rf�"��E����>fA�J<:�UUUImm�tvv��IoC��`��Yfd��QX��T�I� [�e�{2}�jyn����)}���O�K���
P��d������{V�\iFd���3S��IDAT�~r�,��&�g'�����d�N~�,�9V���a��N�C���2~�xS��5�,��t#7UTT����<6����0V#���gI����
�c�G������g����6���S� ��F���jjj�Hd��	f��{;�����wb����gew�x�K�kZ=�������?JJJ���Occ�ttt��������{ H8��`�N���y����^t������,���K}f�����!]�����������{����������kH���7o���=���L�4I�/_n��D���X���>�<��"{KC�5�`�����+�L�2��j��e2u�T�g����n����C0�?���*�����_�����o��u�G6�{��[���~ [�k3�����+_-?FNv�����M���y��a�����%/��0'���>�����V����l|x6�a �8���sw����zy��}S�������W������o��[^\�S^|m�4m��E*r��3���s���U��y�I�>N��/x/������C��YoylBD�E���bF�6j�(3�N���^�u3�����{���g��+;�H�����g���Y���!�������-z�������0d�Y�f����G���UG��N(f�= R����r�
����>#��9K~q���u_�z9"2OUU��WQQaF]V�ZeF����6#�yyl����G� U���.����q����9�����+�;����X�(y�~�]{��r��w�={v���&�5k��J�H"��������`c �8��`������Z��@k�����"WO)0{�'X��N��q�/e/�jB���2~�x��uE
 UVV��C�W�]-��U���o���s�=���-77����l'���b=w��������R������_PP���-��7J[[[h<r�H����x�s�G6�{+������G�
�������dp�#Lp��M����541b�������������>�������]w�eF�a6:@P-��&���78j�}oj"����23����xy�t�a�L�Ra���ft8�$LCBn�5k�����cK���r666o�Hv����y�E�������-�����}�Q���]�,��&���^4R�~�d9a`/�c��%�E�������
��#��U�V�fS�Mg'�M������~�z3�93n��X�H�&�}�/�|b�����]~^��7N�K�b*�+���n����[6�\������yylG�Ut�!��?z�x�%�
���v��;W��/�m*��~��=�T���'�
�BD���.�T^^.�g�m�X�6#�����u	��5s������m���x��T�����=���������{��c������=#
i���[�������A���
UTT�Q���:3�>��D�c;e},�����\


fdO�?��H�$@�~@����~��u���M%�/�7D]{��q����"D�2m ��;������d'3�i�G6������6p����:z}�]��6|�5�N��y@��g�<�����iE}e��S����M�M�C
�����������������TWW�Q|�G�c'��Y�I��c#���_oF���D�����=�x�}'�M��Z�h�@f{}���]kMt���)������)�E��E��FM2������k�(>m�h'*��u��:z}'�4�H��6����*��|`*�}��A2��S�����������t�:?�D�&M2���S�����oF���t��H����<6�c����W����=%�������p�)��taLJ <V�Uf.|E6l�m*�M7H���);e���d"rI�QUU�.�Lk�0�<m�8m�xEO�������~�F�~���Dw���2�k�fx!Z�H�9�+��h0G�U�TQQaF]����a�h}���~���7�<�q������.�WZ�l�@fz{����e�7�Jly�9������+J�w�<S�"D�mXD�����
�0UYYi�����:�F�6"��1�D~6}��^���[��G^��wu������0{�v"
�h�h����t"�u�em�;�X{`��[�S��B��f���-�c��+++3��yyl�����eR:����-��|Y^xm���6��_h��/�s���"r�u5!
isg�����^�$���6#����am����o�,i�_>f�������9Y.�M9�f
i((Z�������D��0^��
	i@*2L��I��a���������q�v��t^�UZZ�K���U��+m�����i��Gd��vS����K��12��^��D����4�k���<��T�����&�t��C��������Jt#�?J�+�}���D�UT��� �u�;����qi�,'''�i��O�����5Te��/�q��N�X^��� �����@&�g�.�6�����L%����;�+W~~��/"J�u67��e�UQQaF]R=[��R$��A���7��/����<�v��D����
���m*�K���f�%�`�5�c=v*��D�PN4NDa:	����^�;��yyl[*�Dz|
� ��Z�Yf��V����Tb�����N�S���
�
!�$Ygs+++3��X�D��-�t������e�L�=4{�������{�J��=����/?���r�Qy���+��9
����<v*i((�����}��^����8@��3�]��VZ�`R"a,/��`� �N�E��	�d������{��#Oo1����'�^U*3/-�99�
�D��e�mz����S]�1�X4{�����-���&;wd*�}}�0���"��PQQaF�A�6&�M�9�M��
���y\���l���c#�t�8��Dz{=��T���.��|Y�4�2���O ��x�L8}���T D ���D���MA���k�����C��1H:���?gn����r����h�a�pO)����Ud?)�������*s~]~�x�tv�b�\\ ?��I��OOS�B�(��Z�����6mt�@�6q����-\��� �,|�IZ���E��w�TM#��l* ���������$�����z�d�����L���<��]S����>r����%�CL�!�$����Q�y���Qb��������@������_��?����DW:��,�1V���3��I�&�������(q+V�0�C�
��{�4�O�����j*�]>q��|�X):�hS~ D������Q�dgz�6����������*���T�+;��,�9V���T�_***����U������Y�
��W�?��w��?��n*��8���>m�|����D���3��Lo�6z&O�lF@l[�S���U���>S�n�����5Z����@�AYY�u�;w�u����X�����|#���I�������N��F�3�7>5��9s��Q����n�4@��H���f f��%K�.�������^xSn��u���vS��;�����=����$u��lN{Q��������4{��}"�=%�[��Pn��<��M��QG�I��%r�W����4����� ''T�d.�v! m�TTT��5��oUUU������:������/7{�F555f$2a�3������f�_��=z�����J�,[�LV�\)w�}�����g�����hhh8�:��/��Y����Q:::B���b���7�� ���s8���,�>����v�����3�=�{���f$RZZjF���}�K��)�#�JE�R�q�7o�TWW��R:A����2���.��khh���2e�S���_�^�N�j*��w�������k����"���
�x���Y���B���B����x�s������}K�Y�,Z��7��sr�qI���HS����EZ[[C�������x�^����c%D�"� Q�h�����E&��P4�^{��a"BD�E�6>��0l�����m�<���f/�A�z�D��1 5xc�N��q�/%'Z�(LPBD����]w�e����/�N�w�� ���s��_����e��fG}���\4B�z�P�!"���g�+��p�6f�A�,D�wt�!m�� R�D�/m(���[���s��F������c	���P���^��|�)�Qi�J{V��^\�C�����Q���r��S	��\�L�HoG���6{�L�b����m($���-{��{^����L%�	�������t�l���������C^LHI{V�3�@������Z/o�8`*�}��!r�N��#�1��y@5:{�6m���z��� ��M��=������B����7��.�p��z�(������@z������_�}�x������TVV�*�d�D���+�D��Nj��������j*�
�w����F��)������ii���i=��!<xg���)
�$$cI�6���� �v�Jt��Z ��e��A��"���-i�����T��H��l��_�
�w�Jl�
��o<]�N�o* (��6}��i�g��)f/��h6�o����-f/��y=���<I.=w���LAo	H-?�<$�I� ���y@~��zY����[^n��eEr����W��� !D ���9���C��TGG�������s�L%���3����L@"tR:
����I�d���|G�^�Z^xm���6��_h��/�s��� "D �u����� ��-m�^{�a[������6������{�J��wM%�3J���ce���� Q'N4#�������~�� {��w����������}m��7&���cd�q�LUN��B���P!��	X�j�%g���f�lSSScF"&L0��������o���+9��M8�����S���3������Q����g�}6�~��B�W��Q}����H_������K�d�� ��-[��W�?��S/���T���Y����R��������<���}�K����ddbo)U��;������+W��w�m*�8�Y�D�g~�����I���C���"���
�x���Y���>�\XX(yyy�1oq��p���g���hI�ly{���Vp��2��9��������EZ[[C�������x�^����o$D�"m��������M%yUUURYYi��M%O_H�|
�D�0�]w�e��i�$�3BD�E�6@�q������/�+?{�u���>7T���fH?��x']�8���7o�<����+V�J��d'BD�q��Y�hQ��CVNzW��Q"_���?��d������s�������������/3.-�|�	Y��z���{��OR�DD�����$'"��m����������8@t����a&O��-� �='N4������GI�8�?�-���
��u �{t��z�(�������  ��q#@���xM"-Z�,�|b����w�<��;'�E�9�T@&��! ���H��tE#H�e�o��w�,k�v�Jl����o<]&�>�T@���4���-S����D[F��If��9s������^j��7O���k`��UQQ!eee	��h����J����c�RMM��L�0��2�~o�����N��an�Bi��Yr�]w���E�]���>�q?�l����sPW�J���?������K�d�� ��-��?j��S/ukw�Jt#�?Jn�r��p���������f���t���_J��2t"+7zKj���f�l����T���t�R�2e��;��A�f����D{X���OSS������EEE����Vss����������������D�����������W�5�����P.)?������EZ[[C�������x�^�����$D�$�U�4�RYYi��'Z���6������k���qg���v7@����@�(y�~�y�tr�k"
.BD@�@��s�L?��xg������a�^S��������(�c������}�;�������*DA�] �"JL����)/�&�{�C��A��{�?�{����w��%��cwW"�S
���K�h�����z���{��OR%������h�'�"mZ9
)mp�m����|�^G���q��c#�4��.����������E�:
}�������L��e�g ��r�J3J�v���~�����������c	�E�L�<Ae��N��J:kUx���
�"�m��t��H��[k���<E���\���7#���}�����-��&�������F�.+2{ �i����������C�������z�j��������z�������0�T@� D���6ytUk�H�7�U�����s"C9z�y������e����._���u�ck���u\���l�fj�5k��g���ft��S�� �<����;g+�����r����=�
***��_��^=z��k��)ft�e���8���o�u�^���{M%�/|z�,��49kT?S��B�+�h�(^ ���������p����|"Y�>����<6�-�Lm'N4#�Dk$Ek<2���7�C+6����=&_�]=F&}j��H�������JJJ��=�&��5AXm}�C���u���7�Jl��������+�rD�\S��Q��+��u%'+*�u����^]����������h+yyld�k�����3�����B��TZZjF�l�{_����u��_�2��J���3��%��@���W�k���Q'����7#w�u�]ft(]�(�yI����,/���Tb���c��O�>u���lE���+�dB �I�;���������5�m� /�����m���&�A"
i(�N� s5o�+�����}C�����B���z�
�eeefD�H�&��I��iO*�q4��A&�g���2��
����r���Tc��E#e�wN���i* �"rA��6���[�'���� Td�M�U��r2���F��5{��4�(m��
1cd������yU6���T��x�	��o�$G���F'<��kL�<���I'����IhsJ�Yv_G�d'�����h����D�NN������J{X�L��M�{���/�m*���-w�:U�z��� ����� ��7��Ep@�3������M&r�}G>�4��d� ����VSScF"&L0��������o��'����9��X�r���8Z�G����e������0�D�>�l�����6�����\�{������k����b����A�9[���'^xS�z�������F�Wh�!E����aw�k��Rr"?
-_���K2�:y�X�:�t�W���S-{K�>���'i(�rJ��_�~}����������������&ioo����$7774����fikk�%///4�-�=�z�=��.yzu�	���|��>�������imm
�

$???4�-z���{�����Mm�TTTHYY��t_��t�������������u��b��5�����!"{A��nE���A�(��F6�a ��z?��&y��7�^t=>���MW���g4 ���>��t���_J��+���{H�E����T�{�����b�E���0����!�O,]�4fx']�w�b���w�� ���sH�����;�^Z��
3�3��2��B);���H!"���g�+!"�5z�����������dV�����������^J��E�(��F6�a ��xW=� ���m���c����(�1��
�yxc�N��q�/%��J8�����]��;b�g��V�~����������B��A	Yi�(������BD�?2���R��������^|�
���J�^��@�����>��X�$�K��7D���������3��2�{�>��]�(@4f�1�`�)��!t%�D~�U�4d�t�%^�G�1���c���H�;�$?~p��Q^n��e�r���@\��\`7SY���Vd`JW!r�
@���y�\���J�.S���3���c��~G�
��������3�.n��t�Hv!�0�[�>��G���$c�?���w�,uk�3�������7�&_<g���F��s��5��i�&�MgkK7vM������{�j2���� ���yO���U������W�?Qn�r���Y"d/��.Z���_��-������������O���^��c����HD[{�,��&��7��{_�����I��j�>�(S��Q���<�D��:;;C��d�t������d����M~��im�0��f\R(��h�����uUUU���tK���QEE�%G�o���q����V���$�������/��^|�Tby�Q2��S����
�s9������l��"������r�� Jus&U��b���9[��[E>/��SYYi��=v:���1#��C��Qf��_�*W^y������^����#�<"������������Af}�	�����D����@���9��u���W����z����>�WN/8�T����p�0`�{��f�d��tx�4]�W���\���H�>�[�G2=����]���c�-��4a�3
�D��[�t�L�2����e�d���f�{������&ioo����$7774����fik�Z����P���Bc�����������[�^|}�8�������d
<���"����qAA�������U__oF"���f�d���J7Iuuuf�e��9f�Y	%�:���<�������pN+���-��Hv��������r66���"�]�����[$������;��;����+�M�'����=[�o��.g���,��D�\t�� �R)�����������D��}��p���D�{��o������H
!"�eb���MCCC 7@0���]~��}y��#S�n��=C����}8WQQaF�c�lO{G:������Fn�o���t4����Fz������r�Js�d���o���W���L%�qEG��_>V�,:�T��i��[���B��f�����^f=f��)//7{]�9��-9
Y�������UUURYYi��=v:���1#�s�=��2�s�=��h*i3����3{������	EEE�����.��3l+]��G��@�p��N����H�o����Jt�?�8���Ef�^���f$R\\lF�$�m��t���_JL����=
�"�d���ub:;�����G��|�r��ur<����c�-��4a�3
6��1���^�hO(�Z���rssCc�������Bc})/�I]�T������VY�x�<�����5�i'���R���EZ[[C�������x�����DJKK���{��n_�����'Z�H)��@d��.�u����������6�3i����>Vv�![�o��.gccK�-���lll��E�����-��Hv��j{v�{r���(@t���J��%��ac��-���l���CC4� r�=
�8	)��r��k��i�_v :����w��rI�	��:~.���t���� ��dG�K$���<6�^���*w����b��eE��/�0{�DNn������
�����OS8���������Cd���UM�������g���!G�
�{�`��9f����NP� 
��M/�F������@�Y�x����6����9*On�����s�7�tr���DW����ix����������&�
L^�***��}^�y��| W/X-Kj��JlC)?�����/7��t��_:��	"����6wt��0m�mu/DJg��l9yl��E�J�`����~���1#�	&�Q�������{�����7�C=d��7��
9r��K����fv��������H_������K�d�� ����sx��v��7���k;L%��!G��W�����2a�����UZZjF���}�K���+����Z8��=2��1kO����|^u�q�����G/���D{Kk����.�H6m�d*�;F�!O<���r�)���������=4.**�������������-4.,,�������8��~��
y�����5���d�%ErD���"�{@����Hkk��a�����F���q�����3����"Pd}���nC��X�z.zyl?�"R��r��~��fH��7�,?�����!D �8��`������P��q�^S������-W�����Ca������}�t�_Aa7���g����6a�S����9���O$BD]�jF��k��i�d����$��O����?+�&�V�>�:�:~-��w��K���= ��w���jw��-�JlG���W�#�9����sH=�����G��v��%I�;s��5{]3��)U��8��-��J��w��xyl?�"R��D�"��l��@����g����G^�]{�f�����9^~��"��!"�;�������WDr����?k����Q�������������[doi��!f�����A�m���
�}�i�H�#@6�������{��6g�_<exO����2�&.����~�G��{���5����!�$Y,nKU��������X�.Y��6f���s���XVWW�(s�<���~q3D�!Y���l�`��-������%w�������/���}�{������t�����Jw^�_��?k���||9�l�H$B� }�Q����=���M%����-�?-��C���!�����~?���5����!�$eB����HF����6����=g�3�E�����v�H$B�u!" � ��9[������"�^����Vyy�|�����X�I�>���x�~a�����"{Wn}�<��"{K��{�u���ke��)�i�&S�1b�,[�LN9�S�---���Hn.����q�Fik�ZYz��������p����)���E��q�Tb+�[��:RF�8�T���RO�imm
��������1o����5����'��2����6d���t'����3��@4t"�f�����/���Sx���������"��o�����a�D�SO=U�x���d�����}��=�2y�dw9�7[$�������"�]���m����Y~����/�w�����dLA_��E�"�]�������r666o6��t�tV4�e����U�Tw$�������!�U�J.Z��t7����3	A"t"���}mr�����}�T�5��,�1VN/�k*���	�����U�B��***��{����8^;��J2��T8@�m+ {�o�#��^#�v���6�����-���4�"�ms)��i�IC<v,
�$������ � @ �4m�+�/zU��������d���r��^����)//m�'O6Ugf��mF]���Y�9���3���[���cg�Dp���c�m�_���v�Jl�$�o8]�N�o*�"Dd)�D���L��k;B�Mo�3��.9w����'I�<�6�e��If$�b�
3����OUU�EW]]mF���;���X�Iv�<v& H�X ��������e��S�-/7G��r����R9��<S����A"�!@ �,{~�������@��D7�_F�5S�b���0�.NW#��q���.���5|d��G��b���<v&"H;��MV���>����a*��Q�7���E�9�T�!" �$B$D2��$�x���E��G��z�(�l����x*++����l�����0��fmE�������}���G�NR^;S$B$D�m������������m��7&
�yW�"�e*���D!@ �T=� �]�������G��ce��MN����Q�p�h��y��E�8999����9s���u�
�D[�������.Z8)��cg*�DP��-�Y��L������7M%���%��9E��`���'BD1hc@�[d�@�]������$�n�d�ww}$7��V��������>�`�)2f�1��<�~���u��RE�4�0��f��}���q����qt� ]�(R�����I�o]i�����d��"d���o���_�-o�7���|����3�������8� Qv"@ S���[���UY���T�;������;�T�
�h ���F�����x5����T�F���w��cg2�D���A���r����#Oo1������[�%���PrR7�@R8A��B�@��]�^(@���M%�����r�(��di (����]�H/��Q"A
*i�(||���Y%��cg2�D�����������eM��[������	�4�` D�0��"����M~��ik�4��f^R(��h����4h�!!�4���eNW�&|��;S$����v�m��~]=�,��]���P~�����|SBD1hS��Q��:nn���
A��F�@��oiKh�������-S��`*����X�]��
H�D��2]���d���R��]S����>����&���k�E�@T�2"����C~����*D��K~��S������-�2"d�{��"?yh����#S����C��3�J���M ��� Qf!@ l�C���W�/k�3������3�J��G�
�$�,�����| W/X-Kj�OR��<R~����������U�V��$��d�5M���{����{L%�Ig���c�_�|S�l�W�7o��R� Qf @�L���o�u�^���{M%��g��o8C>u���|��������'������������VWWg.�E�(��+����p�Zyw�S������_-6{�%�����rz�pi����
�A�`#@�L����r�����d*�uD������
_-�#z�1[�Y��&I�Y�b��
�d����L����h�����������=���[bB9d�D�D������%��\-/�������������>u��dBD�� Q� �	~�x����9*O~�����s�7~ H,����}�&�~[/~�(Z;L5��^4R�~�d9����y���s����"A��@����������]���e�������5���j����F@�!H���^\�C���Z�~�S��tXo�{�����O4������� ''T�d.�z�'O6��Y�2�&M2���k����������IMM��L�0����-��"��~��C:!@��������%%%����Wcc�ttt�DT\\,=z����s��9�}g������i�^sIt���_n��T�:2�T������DJKK�����C���y����������k���f�$]{Kk����.�H6m���(R��;������=4.**��\�o
�Bss�������������������$�{�
����;Q�Oi����z---���H~~~h�[��g��!D���������A�
B�H$J?��!" � ��9����m�P|��{ ��"}�x���Ef��xc�!�`�������^jUUUIee��C6I��A��C��=���aj��{Hg�[���%��~�nS�m`��2��B��)L%}q��G���F���q�$�s��1���&��4����D��n�~Y��]�D���D -�?>����A��H�*X��
�G����������8U�$��xF Dn#D���f�C�$J���Wl��k>0{���Z�|�sC�@�I�$u�����5{@�!H� ��������e��S�-/7'49��W��1G���N9�
l�)2�X�j�}b����b�
�'I7g4����3�0a���[n�En��v��T"@�~��������H_������K�d�� �����5����e����'_n�r��V��T����z3)--5#nH�>����zK�������?���-!,(���k��E]$�6m2�
"o455I{{{h\TT$����1o577K[[[h\XX(yy|H�=����|G=�,{�w='�9������H��e*����^KK�����������F���q%i��y2{�l��c�1D����D�6@�q����V��7�����M%�������%Ci*�
o��!D�Y"���*���4{@���["H�z��C�������t���!�o�?�������I���������R��z������I*��0��Z��Y���r�=�:
��:@�K��d�@�[�=D���_��_�8@4���d�5�:@�6BDI���jkkn�
"A��k;B�-o�7��.=w���'I~oe���[b"d+�D�A������e�������� ��x��V��T������p�� ���e�o�=�^�h7���O)��M-0{����� �� �l�+7���<��S��o�|���Q��K%'�p!"�!H�
D�����Q~�x����G���W����b*2A"o @&XV��\}�+���S���������e��MV��x� ���;���Uo5{�
8&W���O�=m���d��E�A�ko��������m���NS��{S�G�%��4�!D�s��A�@P���#��������Jt%'���������M@6 H�D��5������f��������^�\z�S@,���A�� T�6����*k�w�Jt�$�_|��;�Td�D�!@����-���6��>2��.�8T~�T)>�hS@<���A�� T��~W���Uys����W+N���Vj�d+�D�!@� [���\�`�,��f*�
x����'��/��
�E�@J$�D����&s~]�;:M%����w�8���v������g����*M[��Jl��,��x�|��cM�A�@�$r������-r���]�#r�'�-S?{��@�D� @Pm}g��|�k����L%�������Dn�j���G_�_R|A�(6D����C~��y�/�L%�a���3��g��78A�� ����[2��������Jl�>��,������L�"D�7�� D[��/������y�T�;����|��R|�����#@� ��]���^��Q�v�jl��h���?����G�
�A����� z�iW(@�a�S�n������c���y��$:"�_���i�W��/�c*���-w}�T���'�
�@���u!@ ����;r��ke����U��Zl��9�D] ����M2�����;>4���t������#�1������A"D�����!U�������J�|}�p��l���sp[�j��:���7o�!��m����z2�<6���A"D��-{d��k�w��a*�
��S���N����
�F�@��� "At�cM�����^t}������,_��`S@��0N24�S^^.�g�6�O�X�"T��O"�/�
odk�������������M�M%��q�������2�T�BD�J���}���^�x�MS�����e���r��cM�FW��0N�4�d�����N`��c�[�$"@� y�������e-�[^n�\��"���R9��<S�Wr:?���
��E�Auuuf�}eee����������D&L�`F���[n��o���e&D�������hII���,�����(�qqq���A��a�;��'w��^���5��>3���tE�uD��8�9[}}������7�k��Rb�\�&�[R~�������X���:�ov���VKk���J*++��=/���l�-�]�V.��"��)�*�AE�(�555I{{{h\TT$����0��477K[[[h\XX(yy|�H�=���������e����I<g���������Lv8���kii��������@���Cc����>�>!�$�5�2i�$���H�����!"��A"D��l|x6�a���Sn�M�����7��'��/-4{��9o��!D�YR�~��^4X�G�HW��[��i���8E��] (����c�%[{K�$"@���aj��{����#���o�J|��4\��`��C,�{@�"�A��}v�>� �d���6\�l�!
�dD���/�)��r����/�p������`4�J:�]��)�jM��GZ��#Yo��c#�4`�A
�dD���/���v y�Q2����|B�(���B��"��D�����C��y@6l��W�c�������i2{���Z�\>q��@:Ie�H�����t5�������	��'M�d��}$/�
dZ�����+����������JlS>{�,��t9���� �%���24��n���H�	_'�iM7��h-|Yx�"H�l�)A"D��~�ny��Mr������/��s_����&���k]/��E���Fy��-f/��}{����"�;s�� �����p�(�z���Kv��E�-�T����kF]������9c����g���
3�b�b�����2%HD�A���}r������l*��=:On��T�i����c��!"�g'�`O$m�tvv��Fz��Mk�-_�<t��f�G�)zYx��X�z�T��-�A"D�����e��k��g���m{M�PZ���zz�����f\����5�=�Y0c��Z��TO�_�Q�/�����b����F���{O������_�m�	���k�}����:3��4���kyyl�/�A"D�e��)����Ww�Jl����7�!���Dd���4��3���<��qB�����&N����9���H^7z��{���	m�������������J��1][P�D�����B�
=�L���"��u����o*��{��P���G�
��^F�u��~�S�����O��Ay9I�w���fOd��9f��=7+
V�������� ����*�=�����&���4�����@~���d@����"r�u���4x"Y�8�<��F����J����J�6��1������u
g�uW��D��A�@?X��l���T��@k�Eo~��!��o�$y�9�����e��B��Y�6�A"����@�ako�-���%�=�d:�}4;^�#hA"DHw�k��iw������Jlc
��^�\R>�T�.%�:��6D�QQQaF�B��Q�
�Xg�K'�hc������u	WP�D��AW z���f/BN"a��\=� � 1���tU�DY�:v!�u�XE'�{��M2�Sd�'���]���c#�%HD����?4�O� ;>��Tb�|�PY8s��x�� �"rYYY�%��P�[��z�f����#�fHdCJ�2�r��{\�+$��;;;n:��9�'��D����e�����;rXn����:���*DpG2A�0���EN~)�:n`t���cZW>���A"DHgk[>���\-Kj��JlC)?������X ["J�5�c��-h�(Z�)�r��z\�'m�-_���u��k����i!8�5HD��_t�
�w���%���f��]W��G��c��St�����FEE���"'n���2�_��J� "��G�yC�[��4m�k*�M>{��������
�!"�4y�d�D^5�"��]�(�5H�E8
��nA"D��~�ny��-f�p���a�]�\sq��q�����;����4~��e=����-HD��j�;����_���d*�uD����%r�W���|>�
���ft��t��CD���dX����23J�u%!'��9s��QV#
�t	 ����0#ol�����n����(����K�5���t	 @�Z���e��W����7��>}r��������3AN��B���P!��1h8������_	'm�D��S[[{��m��iw�d�������M�_��}�����W���>����^SScF"&L0#�r�-�����n�R���n)))�}������#4...�=��A�9�lu����i�^����!G���^�������DJKK�����C)1��F�����C�^U��L�������no���M������[doi���f+�{e��i�u�VSI�O<Q���~�N�0���*�����_������R��u/3h�����-/l�o*�]rNo���������o��|��=4w���"D��c�h���@Qv?'A�DY����O&��vc*,����q����%�� "�!D^��s����C.���������$/���m9��`#Dx']�8��c�S$�;����������}"��J4����5�R"�7����[doi��!f;����m�f*������g����?����#��n7���
���|���o*���=��{?�o�{JR�^�;�~�)h�!�6A��v"m����|�
Y���/|^�mf�}��y!}i�GC=�B�@:�������s�Gf�d����Q
�h@����e���R�dz*�z_c��t�A
��*lE���/���|r��Q��^r���%@p�D������e^���;,�����u����h6�Di�����~[�N�Y�(��	�����6��rm��;���t��H�{IV"JN*V$"@�XX�6V@��s�h��������=�,��i2jXo��
�a �X��N��q�/%��Q���E��]o���$�3r��N���r{� /���������kF�e���2e���i���o���l�29��SL����E���>x[PP ����1om��Q���B��#GJ^^^h�[�{��~��w�FY�i���6�oO���B)s�� �8�����
Z[�&r������RA?/F��v��"r�5�b�M+kc'�]�������"�k�Y�c6g���_������I&��*����e���!D^��s�������/�=�<2�,|�f���@�"���}�K���R���_������;n��+D������oN7��7zK�� �E]�I�H?d��O �pMMMCDEEE���inn>�a���B>L
��^�=��6Y�����W1n����P�9����8����	#�!"�0����g�~>��p�66���6#�[4N�0|q����}"9	)}�4��� �����H7��I�����:�BC(vA�0koI7;�	��[��NEE�u���dw/��`���}4��&DH'��@~��z����������+J	dBD.
�b5{b��u'@�n+�t�~��=��n�M�w�Y�Z�	��� "�h���{����!GK~o%�MC=v����N�A�>��8����3������f������23����Fp�$"@�t���������n�S����~���3���9�T�I������n�8
�u�A��� ���;���)@T]]mF]������=���5����]�
�h���9�
 ��{n�|���d������r���f���#N�D���N��g�n�>Tw�q����3������c�5E3w�\3����,n� ]��u���i�;~S/{���jl��<\�]=F���T�ir:������P!��IZ�jU(d�3�����G��@v3����ice��D�X���~=
��=��9�?a��ym��[0�����D�jFH��w�)��w�������Zn�������aF"=z���F��-�3��� }�:�l�������'[��&<���������_F��jG�s6���7`�9��c�R)]�8����}e�_J�~�������~�X�/�������	���v�Z����d��M�������$����qQQ����������,mm]�/,,�������8�����]�hI�l~k���6���e��rzq_SA:��R���EZ[������}��H}}�������a��!D�i�$����6d�)D4d�3B�,X �/6{�L�>]���z���wB�oa���oe����6��?����>2�H���~�e7Q��-����� ����_h��J�����'�1Y�_�ng=���5�<�_�'� ��E��������,�<�����/e���K
�G���������!"����g��a*H�-]�)Ruu�%O>�N�P�S���=v����#U��� R����7�,������:	\$
��$sa���}k�Go/����|�@��� @�t���}r��k���'�\9Jf}��@a%"8f�i-��Y�����dV"�~/������kFp����*w�q���w�M7�m��f����Y����t�9��Y�Kg�����u�=��A�9�L���j��;�5��W�2G�#c
��=�q����hF"���f�L��e��k���H��4y�dY�b���o��I�|�r�����+����D��JD�?X���^�Z����hI�����;@f^Z(��4�3�= �X��+����C���[�(����o�r�-r�����C�|������������p�wVII���,���|xY?�����`�F&��+��#O�!-����3'<R�����s@�\�y�[_._��0����@���>��t���_������E�H���c��%o�
 �"D��S���K?�����C5��5�����@.)b��{@�"�A��}v}>��m6��e�������B��-���:3��z���23B6�������[��=��_������n���%7_Q*�}���p�X9i�1��8,������S �V�	����E��2��w4��A�~��5��H���c#3h@H�B�D�~�]��L�s�����>����N� �"rQ������X�"���6�tsS2� UWW�Q�WTB�������m�g,|E^\��T����!���y��qR1nP����_\{j��@z}���������t��p)iF�:�-���y�/_~��t�z��/������o���Y~�����G�����&+>�hS@�"D�
:���n~��� �u���
3�������D�[��/$"@�O�=�M�~�K�t�vSq��s��o9S�����r�o}ax(t���I������r��^�����NCd�������������Y/a��^�����o_x����^��\�aD������K]]��g]-Hgz��IHI����'!�hx�?���we����xY��;�n���9�_h���.+��}{�����#��<\���t�s�g��9g�C���k]/�� 5� �}f�\��Ui���Tb�|�`Y|�i�����
 ������ ''T�d.Bv���6N��h�k���f�?��x����������v�����������"��za��W�>�kjj�Hd��	f (�~)))�}������#4...�=��A�9�����G~j���n��83tP/���aR1n��d�a �����H������!]�8��m�����{K*��KHzK�?������kR���"���
�x���Y���B���B����x�s����P-i��op�O�������H.��q�����R���EZ[[C�������x�^����8��\`}���k�&����4{�l��%�m�g��o=~� �5@�$t�=@�"��/��9�t�ko[(<�t�vSq&?��\��a���
5���9o��I�>���t��8Eo	�!"�|���^�-�����������������K
��G�
2��z���kt�]�Oa$I�<�����n����=.�4$����"i�� �����#iHH�yl�Zv+F����c�m����R�D�3X~}�� �������cF��?�.������58}��#����L�1"r�{�����A��!M��7k����{�RQ$=N���kY�m�
^��+�����e���w�Y�O�9��,�9V���X��i��$UUULP8���w�z
O��mS��dho�k����
&"@|������0�6������P�_�o
*����ui��V�e���W����~]Z��5����%7_Q*wL#c
��*�d���F��_����Jl_:w��{�ir��cL��R���2��0�]�GkzYggg�C>��R��V�c�u��ko�,Z�,3�"/��i��������0B�i�T�d� ���d��^#�{v���6����5Z��Z`*�3���TVVfF]V�ZeF�G�7�	o�q�v&�0O��t��^�����86�<��6��m/��U�M���,�z�|�sCM�*r����j3d������l�����NBv����gO�o*�s�����6@���we����xY��;�n���9��,�9V���X��i��df�V��@~��zY�l������#?�r��|E��9:�T��!D����*3)//7#�
�����j�����K������A�B��;���1}L������DV�X!���3{�lP��������0���(�'��p�|�3�MH!"D6z�����d�]{�d��f���yq�NS�Og
��F��7���q�L�&r����g��U�� S��w�]�5���y]��o3���>i���z��|���#D����Z3��1N�D4{�L�=�M�~�K�t�vSq��s����%�n�� [�$u�&M2{"����H���]2�����o�Jl#�?J��{��U7 y9�
rrB�H�"�����f���PEE������2?~��C6���1#�	&���hhh8�:ZRRb�: }566JGGGh\\\,=z����s^��+��#O�!-����3g��'W}~��)�c*��s���z3)--5#nH�>���DND7w���u����D�KPB�������I���C���"���
�x���Y���V�(,,�������8�������S[�^|�Rv����Pz���&������"����qAA�������E��}v}BDI����Cn�������h��F�6>��0�P�e�<��fyq�NSqf��^r���b� SA<��@���>�BD�����d'zK�?�����?8��o���d����J�.S�����2��B9����p�~ D��^����|
 �]{�d��f����n��rs����oG��,����2m���D����7�N��"D`������o{I���n*�\x�`y������CMd�]{Z���_�_<�,N����B��7O�}z�
������6�����U����������L��o��Mjjj�Hd��	f (��������H_������K�d�� �F��������oH����������U�&c
��
�9[}}������7�k��Rb���gF����4#dzK�?������=4.**�������������-4.,,�������8���]��,Z�,;>��Tb������dhoS���^KK�����������F���qi�Fl���`���@�q#Q�[���Om���4g��%W]0L*�
2$�s6���C�@<��"����p��v��eI�v������/a���8���#D��^����8|
d�]{�B��X�J�Dy�9����7�#@@�zm�n���W��8Rn�����BD k=��6����d�*�3��,�zVh�@��~��
��/�H��=���O'�o<]�>�XSR��:y�]�6�,^�"���j|g��'g���.+��}{�*�&���Pn��:y���L%�^G����k����vd�\SR���[���N~����e�^S�o��^r��r��12�����l��oo��;_��o�i*��=�XY|��r���3�?��@����M-i�_��9k�������#����I��A�
�������A��]����Tc��G�m�9YNp���"D��y�$''��Mk�����f�z�{{n�\u�K�t�vSq��s����%�Oj*@|v�+�������u;e�������6��J����f�*_�8�T��@�d�����L��Z/k���M5�3G���3��u����=Md�_>�Q��j����CS��K�
�{�;MNq�����(�[���N~����e�^S�o��^r��r��12�����l���Yw���?��Tb����7O����
�~���ko�,Z�,3�"/��i�������/!�4N*�
2U��{nk���~�nS�M��]��;�T��D�(���J���<d�Z$����Y�8�c5����^������3�3X��,�|�PS�c��������_V�Z%�������C���'��������>����Y�l�����#G��R��|E��9:�T��E��_^yW��_-���"���j|g��'g���.+��}{�*�DhGC7���2{�lS���+Bu�N"��M�
 X����L���<��=S�������������MH��@��o�#?��:���_���{M5���z�f�c�S��T$JD�qJC9���Sz]����t:6��hk���k��S/����jl_�4\��{����G�
��@`���&��4�������v�j|y�9��G��7���q�L@2�D�&M���Z���<�UUU�K��
?NV����u#������]2�����o�Jl#�?*���a�!"��l��n{I���n*�\x�`y������CM����kF]4��|�r?~��t���]��
@���#���,�����o�3�����xY|�rzq_S��Hky�]�6�,�c��?�n���9��,�9V���X��i��`]�GW ��p"�e��}b��c��.��uk���cH��'7��Vyj������|���Q2�KE�#���"D�mL�<9����$���d��-{�G�����ui���T�:���|E��1m��)�c�@��@N��d�/���
uuuf�e��9f��������g]�'Z�'L/� SX���<6������2m���J�.S�m��r�
���3�
l��\n�h�@g^��}
8�ko�,Z�,3�"/���T�����o_8B�i�T�d�@���������������,��P8\/����D��`MEE�u�����cH_����m�.�x�Y:;M1�k..��$��S���K4@Dh��<V�M���%Y�j��8s�9���[���'5 �i�E�CA��!]Yh������x�`N�u�#
,9a]�������j��+��|YjV�k*��)�#�^w�\R~�����t�1D$�/��+�����l���M5�3G���3��u����=MHA%��z��O��UWW�Qt^���si�����e�����V
�J��6 ��t���rrrB�H���m�V!�4iRh�4���hm\ �jjj�Hd��	f (��������H_������K�d�� ���-{�������0g��%W]0L*�
2d
�a �����H������!]�8��m�����{K*�Kv�s��������T�m�_�+%E���~������I���&D)**�������������-4.,,��������t���q�,z�Y��1���8Rf\Z(g�t�����= �ZZZ���+�ZPP ����1o�kt�]����]mm-�$�Fl���`���@�q���m��S�e�����L^n�|}�p�|�PSA����7���k��RbtE���g�����A�Cj����M�������c/$����Fo	�!"�|��GP����|C��&����������=y?���= �����>�>�&I�<���@�����&W��R�D�3X��,D����3g�e��icRW�-2x��4&���m�~(��r��Q�#r����Dn�j	"d
��uY&��j��'���,���"�t����������c����e`���
d������TWW���***p����%��|Y��a���v��ce�
���:�T��@�x�~�����'m����L5���z��W�������>�
 �t&]�HW+��������$f��v���6���k�u�jl���H��;'�	�4 {"����U-i�_�^�a�������/!�4N*�
2UA1g����<��jK�&M2�v�0�����t�_�����_��^z�Tb+�[~�|��DS�ON��s����P!��1�Z�J�����Hmm��?������1#�	&���hhh8�:ZRRb�: }566JGG�,U������{ H8���c5[��Om���M���,W]0\��i*�6��@������Hii�pC��q�/%F�3+V��5\�|���8�Y�j���������+i@)��|���~��-
:��x-�X��a������k�����]
�}�����������^|O;Z�\�������@���&����<z�����E����!"��l���`���@�q��v�{��S��e����:sT?����dLASA������t���_JLd%�BD*2@������Uw����G/�������!C��,��i��?�GZ�j5����C����rZ��@|�R�����}P������_|
�UUUf$���@6���G~��z��C� :���|E��1m"d���J3�P(ECE�����@,������w:�U|��������D��df-��l�D+ ��9�Z�����Om������3y�9��I����CM��9+�I�>���E�F�jkke���f/��[�'�r�W���~��-�{��f�k---���Hnnnh�[7n�����x��������_����>�{��H�k;L%��92����g�
l����i�&im�
��1B���Cc�����kt�]��KV�Zu�*D�t�3gN�4{�>BD@�"��/��9�:��l�_?�E�����S�3X��`����T�Op�F��!��d
�TUU�JQ��~��U2a����]H��c���������!���"BD@�477�0uaa!�R��s������%��{_�������2��B1�(S���= �t��p�H'� D��F���q�@D���������PQQa������ R���!" ���2l����]��<��fi���T�9sT?����dLAS�9o��I�>����{Kj�����Z���T���T�t%�����}�n�&^���J����u�?�P�����%����aj��<���;��%���o�J|_�4\��`��2�{@�"�A��}��<`m4�-[f���h��F�6>���w����G��"/���T�:�W�iW1n���q����we��/L����}��m#���_f
�X�Ia^�O��"�������{�����C���o*��<�(�qIah" ���!"���g���S �]{[C��_�V�(/7G�}�y��q����9�
�
����x�����I���c���Vl���^s �����N'@8D�D�X�V�����U�M���,�z�\>q���$f�%�0��9s��P�BP��xd
Y���+#E����cH��7������G��b*��=:_n��Tf}�Pr{x��+�Ir:?�,Sd.B�4���3{�+++K�����-//�kkk�o:�6�".J�.�~�^;�jjj�Hd��	f (��������H_������K�d�� �vG������6K��}�������U�&c
��
�=��@������Hii�pC��q�/%���=����4#oM�<��~��*�|�r�w��^��������_���cyyl��[����$����qQQ����������,mmm�qaa���������������hI����4�8��:@f\\(��4 ����^KK�����������F���q�["�;�4H���3�Y>V�4����s|/��=@�"��/��9���-{�������0g��%W]0L*�
2 1��@���>��t���_�nv���*E� �^GY�����4l5{�l��E'��<�N��:�xyl��[�A��������ko�,z�IjV�k*�}oj�\z��d^���#D��^��!)����"�k$���kxyl�����l|x6������*?�E���n*������'
��'5 9��@���>�BDHW��h��>��?O���"yyl?�[�A�����������B����#S�������K
�dhoS��{@�"�A��}v}>�Gt6��4Q��*A���������"�Er�t�������*W���n�.<g�<|�Y��,�A �>L4z��L���]��	�^wB>^@t���E���78�k�P���� \@�qi�h���f/1��6���mi��d������ ���yO��Y�q��?�5c�g�:V�+�]V,��4U�*������b�����4<��s 
���M>�U��z������Z����eI�6S�m��#�g�=Y��G�
�d�t���:�Y��\�,6y���+ug�����6`�!+����\���~���1#�	&���hhh8�������u@�jll��������Xz� {	�p|�[��#Oo�^�a*��K��`�T�h*��8��`���7#���R3��t���_��%�MMM���59KQQ����������,mmm�qaa�������������o��d���t�`�yI�����^���kii��������@���Cc����>�>!"��Z�*�o]]]��D���u{667���;wn��r"��d�3����M�~yy���~/��7=@�"��/��9������S[d�����L^n�|}�p�|�PS��9o��!D����loI�jNv�����>L
�#�so����%���
;M%�^G���C|�8S���z���kt!"��;���d�p�[}HW��3g�!��!�DW��>'�n����F�6BD@���e �8��=V�U~�����C-N]x����C��4�[��@���>��t���_J�N�V]]sb���g���-� D��S������oo�����@k�{���=�X�qq�x��P���G���F���q��K���� ��6��M*
-_���u_��4���������	j��'���,����[�3G+g���.+&@xD'K����"������m�;��8@�.!�}�dD���  �4������6�TEE�u�>��xyl��-{�G���<�AZ��3����%7_1J��v��)�c���"�C2�_�����_��^z�Tb+�[~�������K��\PWWgF�� N2[YY�9R�����P2��%����{yl�Ck[�����l��;���w�����hI��X�����S�//7G�}�y��qR1n���J���W��~��?m�9�Z/o�8`*�]z�����d�H&5R%�S�":��	"���������9s\]�'XW\�����=��7Of��m���&�����������y{�tPSScF"&L0#A���p�u�����u@�jll�����K�d�� 	�9�~�nyq�������m��~�h�������sN�/�Gc����*�~j��?�n*�\x�`����2�oOS���0l���f$RZZjF���}�K���8T:�*,��455I{{�{1EEE����Vss�������������V�s���=�	��m���G�3/)���`*b�uH���imm
�

$???4�-z������)�$����+�dZ�����,��^#�>��m�Hi]/�������yO��Y�qc�Dg�:V�+�]VL�H���j3�B�@P��_��5?�q���3��� @��Q���������@,��| ��K�C[L����No�����=�^~��i��/Tsb��^r����i'���>�
���@����w�{����$��B�N1&	�h=��Z�Tj�X@i��U���XM<��m,j��H�Jb��&D�+	!���6{����}�y&;;;�;�;3���������k�����~����-[��j�*������Hw:�#���}x��-�3
�����[�C��]T�������Q��Q�����NDDDDD�������X~#�e����w^����?���{�f6������V\� ���V�X!����������!�q�kx��n�etKT������ri�!"""""""��6>yL�e0�B��8��K�y�����/\�?[�,��/��{�?���7�[Gw��9���b�t��BDDDDDZ2$�B���.��
�v�Z�V�g;w�������c��1;�d�����L������c��5r���.O?��\��y���$	�������L��\'>3���LD��������G�F���GC�v�0��m����h�i�[��?K����W__���Z�F�T�u�r�}��r�Q�>e�����Z�DD�����x<�.����d2��DT\�B,�
0��7f�Y]&��y�@���7q�'��P�'���V\��Fn!����Q�>|�h�����
���.Qq���_.��w�\���U�a��2���d��R����B���:v9�,�455�%"""*��.�YX�s}V&"�_=����rM!~�L�k����>��j�}�W.��-DDDD�WWW����F�T�u��&&���j�*l��Y]&�������6x35Qi���c��o�������3p���`2��HD���=��c��H^�:��,Q�I[�n�\"""""""J9� &z_���
�`R����*��#u���K��%`��-��gDDDDDD���i?���ky���f|������1@DDDDDT����&��G�&������X���y�r�9Z�5�\#��H/��s��Q1������'�H��b���{"=)�s�������N���y�[���w�FC�Un!�������0�|�D��?���ND�g���j�H`7"*v""�;i����W����_����}�{�E����X� *���;i���
/W�!��,��$*D�h<a�
6`���rm��W�c�z�������G,X�����7/�y�����9|�7w��TV'�r�L��_/���*��H��>L�o��OT<U��:�D4Y�-i�!""m�fj�������/���;���}������&�FD���=��c��H�5^�:��(Q���4Q��"VE��j2���������c��DDDDDD��%� ��8����kDDDDD��6!9K���b^��4��KDDDDD����u���x9������K """""���&IpDXHL�D�Gt��"Q���$
Ez':�ev����V�b�������z��������KDDDDDT����u��
�S�kK��/��w?���>t�Wo���M�������%��0D4I�v�R�B������y�1��+V��K�+��������&�T��R�������h�2kK�5"""""�R�sx���n�r�)�et�U&���j����r�	CDTtk���K)bT��d?&�i�<6Q!��J��q[���~���~�5<��[F��'����X4�*���0DD%�~�z��]o���rm8�/s��U�V����yl"""""���bx|�i���W����[������TW���=����[F���p����/VT�f1��DDDDD�GM��b�L&�6UJ���?G:��s�N���a�����y�f��[1�MDDDDD4����W�7~�9<��C�w���$�������_�������"WM���H6�����n<��On��k��g�`�U��"""""�3�aD%�c���"=��/Wo����k��|��3�b������h4':���-�p�������^l�S��P�A��^\'������������������O��?;�p4!����2�����To�[�����H�"��Y�l��Y�j��22��X��|��DDDDDD�������Y�����������8Nw�����W��E�r������������h�?�������}���2��.�w�����f�������*CDTR"��y�f5��~�z�u�����1�
���DDDDDD��#^���!�p�����x��>�'Y��&��(��7_?�x�����Q|�{���k����]��-�=U"CR�.���$w��~�i�\{��r��������G,X��}�������H�Z���?F#��DzR�s�����b�K]8p�'�N��L��];ku�Dx���\,��*����m���r	8����B��qX_"*�-i����x\]nkk��dR����:�X,�.��7f�Y]&�A����/a���2��j+���<���z�e8�{D���GTz�F4U�[[[a�X�e"*.�/W�wa��
6`�����������J�{z��Go���#����I�D����������[�q�[������E.�T�s�N�����������?����W�]wi#�w����������2�Q����u��Z��l�"��Z�~=��Y#�h*�hqD��NDD��D�6�s�������m�t�7,�N����q���C�n����'���<��9�����9����r��r�}�H�8:Q��k�������������T[�o05��D�
v""�;2������<�g^��[Fg2p��������[F�s�H<��J������Zc����0DT@�s�h��`�h�b��H�""�7��L�o�=�������m/�����r���5����F�T��j��:���x��#�w,�3-��Q;-n��[�*���������!��$�[�|�\+�LM�-i�!""m�fj��� i������1�%jp�
�h���[��s�H<��J�!""m��Xx��8��@� """"""��y�����A|���7v`R"�������;.�C�]�]7k�� @���qw��c������H�� ���3��J�(�9�:��������9�P�c[,��}?o�W�?��G�9_�}��DDDDDDT���D�`���rm�V�Z��W�X�.DSG�#�7v""�7v@ ��������:��������:q�.�������i0�&�~/�=�����A�)��:Ht6�zq�^T�v!"�t|&�7�FT<�Z�a}i�
}�P��Dmi���X�l��Z"��n������c��~������_?��Z1�]j�-i�������D)/�S���-��;��;?��%������G�
�{D��NDD�`���r�""*�\]�D!Ai�2G����k�.l��}��E�g���r��*z���!""}���D�����j7����g^�V�O��Z�ZyY#�Lw����%�����A���:�3����a"}��}��)�:�K�����{��r
CB9��DmI���7�S(��,������7�����yl-��D�
�����������|?���rml�y��y�<������6x��CDD�`���r�qxFdD�C�D&
�)Sz81���b�xN�8�(��������}�������7���K����?��
���9	"0$BJ����3@DDDDDT��A��t8%��$�
e��-�I�����D���F
C������'1�^��U��&����e;�x��.4�b���������	�s��z��*����<|��m�
Qe��H��]�Q�MvaH��,DLd�6"""""��.�c�� ���>���{����B�@D���.|�����?]�/|�|�uq��CDDDDD4q��o�K)��r���ev��"��\�Ce�5��0V=,{�x~�cg7�KS.�<6Q��j�|�����`��2��]T���s)���Qn!""""���!�I��T���T\��L�������(?/�����n��s���<�j+>n��{�L|�����7^��j7[�Q�d��F�.��Q>5�b�����F�9�;������+��&�g�}F����DDDDD�����_~���E;���::1H��>�
�V��������!�����/�
=DDDDDDzs�'�M[O��^���^�o�?�X<��Y.W.����|�����������������xV�X!�&F�A��h/[v�(�+SZv�l����P��V�c������o��S/w�-�[�R���]j�4"""""�l����K�R��-�|GDDDDD4U�"��c/>������8��{�������s6��.����"�����IDDDDD�����Ha!���d���]��o4���\��b����������a����{ "���#+�q�]a�,��BDDDDD4CD%�o���"""""�J�����?������������^�gb��|���[W�Q�DDDDDDD�&�.E��f4"�$�K��B��r�=�H��2�R�c������v��;N�-�k���+��\�������(7��&)��B���H�}Q������������;�{�����1�w�.������_��ko^�+��=DDDDDD�5����-[����}�dm�����L&����� |�te*�������&���N��^���~�et��r��\u�DDDDD46��
l���r�������&��}�X��>p��x�����Q��3~�.3n��	�}	������e3�v��^""""""�M�0Jv--W������*�������&�dW���=����[Fg�q����sY���"""""�CD��=:�h��2k���KDDDDDDt�3����_|�E|���b��g����h�y}������[q�l��CDDDDD�����h���.]*�R6l� ��k���r)%��(d��L&�������&""""��-���'������[Fw��Z|��K����rQ~"*������T8h�nD�#��z\%��������L�^��?|>�����������;~s�;q���b�-
�}u5.o��=DDDDDD�e��5r)e���ri����F�������kc"���g$""""�J��8���������:����\��_/BS�]n!"""""�CD�=B�AM���!��[�n�\J��.��}������H��������/>��m����oT�\�&�y�4l��B���Kq�������^"""""����Gt�A�\��e?.���@�Lw�b���.D��{�\"""""�z��F��}h��r��4����.�GV4�-DDDDDD��Q���2"��e��&
=�A}���'G_#""""�J�����������+������gN�����w��*|��mx�����}d.]P-��Cv�F��D}I��2�X�B.��zR:p$��y��_r���__�:X1����wKDDDD����=�/�`/Nw�����xM����`Q�Gn!"""""���
d����;	���C.
��D�(������+?z��?����0���=�WWe�M����>����"���3�����S�A���mk���-8�	���$W�H�y���IDDDDT
�N"[���r�������.����*�M�!�P�F5�.��(��[�nH�F���GQ�5*\6Q<��y�\�������K���^+��H/8p�}t��9�g��|<x�DB]�?>�Ff����XG[_<��/u��7,�N��.�������z�et<�����0�����_.��w�\"�B(�:�K�':
e6��F�+��K����F
�U�|]��f�����z,����Y[jnn�KDTl�������xN����\�K����W��.�\�����r���y���6x��?oi#�=��������!�"�]�v�E���G1@DCDD������e������b���������:���/���5�����a"}�9L�oO��qX_*P��};V�X��K�XA�J		�������kp���b[k�����&�DDDDDZ�����3>�r$"����|d��27�/r�-DDDTj�R������JD��Zca�������a#QJ�0DSCDD������e���vh�^<���B���@&�f5a�e
j���������9L�o<����!"��)�:�K��������K+&@$d��"�CDDDDD���!<���p~��k������Tg�[���H�R������JD��Zca����Q�b��H�""�7��L4��������/u���_n��%�����ij��b����s�H�x�CDD�S�u��([v�FX�~����<F��>�<��2kK�\s�\"�b;|�0�������
���.Qq9r�XL]nii������X<�?��<�!����w4����-���="m��#*��G�"���s����bQ������i�5CDD:���1DD�o�y�(���v��v��-�L��Z��qh�2��p����s�H�x�CDD���A���;��%i2a���d��P1��5�������~.D����Q�:t����������TVv��������2�������y��k����6x���0""F0DDT�5^�:�� """""�:��~}���O��o��k.��������+����[�i�P"a��ri|v��)�Rr���&""""zx�q|��^�;@�������Y��U��������h�p4��������������=u]�a�w�4�������/]�/��o]\'�Q%(d�HX�f�\J��a�\��K���A�<6M]G�����~{Ln]�����r>>��6���G'"""""*CR�����h<��+�Ql��]rM�1����O?-��k��V.�^8p�����r��Q�:x� ���<�|�������`?�����/�E,>���N�	�_>
+/k����Z<�����0�����_.��w�\"�B(�:�K�M4�RH�A�b*t�(-���j�*l��Y��,��2�k����kKD�hooG<W����`2��e"*�C�!��������lV�����]g��/!���3��.������j���<����s���>�h4�.����b���DT\�5^�:CD�E��k��5m�_����*,��CDD����i*��
a��]��R'�v������58$&�F#��&�7��D���D�S�u���+�k���7�UK+���}���Ibp�������kl�<��X["�CDD����T��Q<��Cx��.�el�z+n��I���="m��#*=�����Zc���Y�."""""�)�w�t�����e������I�f��q�;gc��_��}b1�q�4�DDDDDDDT"X� a�B�����5s��K�.�K���DDDD45�|������D�[�����D�""""""�MN�����M��G�{�M�aO��31+.k�W�j�����Us����{�����������K.��Z�J�&���w����I�y2/>b=;�#�7Zg!���&"""�������~���et^1��u4��""""""��
u!G�"�k����H����X����~�i�\{��r��������G,X��}�������H$������hd�����c�K����Y�=��['��9n\�4���gy���9L�o<���m���r	8����B��qX_n�PL�dw�)�\�y&c�������e��66f��y�\]1����������x<�.����d2��DT\�B,S������<�WSe�sx�����-��Yo�����.��[����6x�������F����VX,u��������U�a���L��C�o�o^�J������b�����$��U��jhHtZ8�#��/��D��s�H�xa��x"�rR�� �Yk���k#]���*��K��%"m0DD�
�LM���m'���9*����+��"����x�i��Q�1DD�
�/W�waU�S�!�����c_}��������I�������|���o���0O"""""""*�]�v����4A�
(���c��	�|�yl""""�?Q{���� �[�������,��Uv""*S-�H����H����f�K���b'���Wn����j�!�yhf�]n���D��s�H�8:Q�����%"m��6���Ja��g��c���~���������yh�i}%<����s������H�5;U�}�|x�����{���~�R"��]��WN��>����2�����
MF0������L��>�'s���^T�""""""�M���;�a��^���$�����������/w���������
�g�i����&bqk>�����W���>���W#�Q1��-����?���O~s7~��Y�et����]�#+��"""""��fH*��m��.�Dg��]X�v�����U�p���b��er��=���r	���k�������.X� ��,���"�H���������=i��7z����x��.�e��<V���+/k��Y.����&�7��D���~��w�yr��
�\�8�/�M��o��-[��-��_�k���kD����6������T����d2��DT\�B,�dk��y0���2�d����g�O����xM>��V�65��#��=��;|�0�������
���.Qq��Xx��8�#O"@�|��1D�(����������&��� ~��>��q��N:@���:|�����/]�O���bDDDDDDD�D��Dmi�� '�y�LDDDDDTZN������w��������S.@DDDDDD��!�<�D�%
BU�h,���0���s�^h�D[�������_�	?���8��{��e�������+�O�]�k.i�{��������D�(��P6Q�b�������4~��S��}{�z���]��}�,��^n!"""""����0V�h��Uri8Q��a�\#"""""=y���<�����^�������W��X��~���x�����v�x�s���K��������x�[������~�����f4���^""""""*Q-@4ZmI���vDDDDD����0���
<��a�etF��?m�>z>�]�������H"�H�;v �L������-�_�^>b���k����'����y���@�)��:��.������G�7�G�>��������]g�����d~5��C�������%�:i#WmH�r��D�)W�������:��o��3���-�5���s)��t��BDDDDD�_�!���.�,[�Lnj��5j�'=DDDDD������mh���rK~������G#
R���������������|4T[�����������j"��_����������&$jG"8���9�}�A"RGDDDDTX�D��E;�����br��>������.D���BDDDDD�o��l�F*�d����D��o�KDDDDDT�D��^��c^�e|�����H�O����=����<���
���.�gb���_�m!~����_�IWDDDDDDeL��F�.S����;�M������7v�����-��;����n]5Gn!"""""��"�0���H�i��-r��������� ���cr-�� �'���@�|�������7v�g�O��?"1~mM.|��-�����o=o��^�!"""""�r�=�\v�h4���]�v�%"""""��������u��-�{����=K�d~��BDDDDDT9"��K�����Z�J.Q�{��#r)K2)���5Z�����#���~�e��6�Z���;/�C�]�����:�E�%"""""�r7�Z�xkQDDDDD4�#gjx�G��1�\U.3>�������d� sDDDDDDz��(
=�[vg���`0Lh��a�<���cW�^���7 """�\D��}��rM1����e</���&V`�lA5>�g�������[��""""""*w[�l�K���������h*����
��1�c���3��7wcw���-����\wi��BDDDDDT�"*�+V�%�����k��,�����/W�0��q��M[��5)��C�{^��Qg���c6�s�eX��x��8���l�2�DDDDD4�����'���o�������{w}�Uu.��v�_<.S�?����M|��H$������V������&�U.���"�P��3��x�x,�DDDDD$<��G.i��%
X�W���/\�������r�����c��w^���'�~�/�%����q��������o���/w��cY4�����%���7�-DDDDDD��!"*�����%`��UX�~}����K�3s[�n�\J�O&���;v��2� �s{{�R������n��
������>z>�rA��CDDDDDDDDDDD45�9<�v���������y����{ "����+f������r���if��5yO��-��n��
C:�������Z�x���$�%"""��+K�8�]�R3����/���?s	��|`y�]��N""�
���]���Qu.����������rA��x
��y�������C9Fw�0������"�����-DDDDDDSCDT2�A�+V���[�v�\� ��$�|.M==��\*�;>��_�{���<,������r���#���������o:�������X��u�}���W�3���I$������V�b���������Et ���1��� Gj�����+��{�]��.��[����������$v��)�R�.]*�&'��P>��{��W.��������7z����X���������`n�\"""�\y�z�BlX�����,b�6�J�q��'����.���+x��'��?�	���O���?U�b]l������������=����%9zW�s����n�[�������,��f�[�������&���$v��%�R�-[&�&g���r)e��5rid��6�U�3=!l��?��O}k7>p�s���b��x�@��<�O�T���%""�����M�����m�:�%?���y��D�z��W�e�����g���8~���[�'�+��X���-[�[}<�/��h�1�\SL���(�y������{�\�UWMS��������:���$2�>�V��K��e��4���_�^.Q%i?������~�����"��,?��T�eK�;��$�y"""����G��}o.���yrKZ�=6�N;�;�x�x�8�h:;;�k�;�>�'}�a��Z����{����'}+������Qyy�����k�DK7������6_��Eh���mDDDDDD�U�+V�����s�\"""""����x��_�*t���]�n��n���!l{�gz���#3/F������_������B��W��� ���^����)��w�qv$������������g���{����x�<�|q"""""""*����K��q����N6*���A����
�z�j����Il�'�3�����K��E�/�g^������~�5�{�.��������x��^��1��qzs�]��N.U��;�R�1���WyP�{����)Mtz�����BG��x%�'�/���DDDDDDDD�����r�8��S���1D4��/xmZ�v�|f�x�/&��D��&�~2�Eib������B�v��K)�	e������w�3��>���A���_�M��G|i�><��I�9<0�����}]��V.U���<�#��@�8�7�� �8�8.Q�g�y;|9:�����(�2�K�z�)�q����JI�A�k?cM�&�|1���g�xzw����h?��[�C?/���6"���Be��F"
c"L4��CDDDD�������O��~���q��^����~�l��	�G��j��:�\�&q�U�So�~6���5""����3�`��urm���������r���W_}�{���,c��#>�)��\'�'�KDDDDDDD��%��������v����x����_�	_���x��S�������%""""""""M�Z�
���G2�<7���C��M�������#	��f�r��=x�?<����
�����t���x��j+����z+��%��W����p��������M��w��kDDD����r)%9v�cT���}|��ij��������h������q����"""""""��~_/����w
_������x��?���}� ��u{{��3J���KDDDDDD$�"�!������,�A'��9�"B5k���k��z�j��P�m��Y�
':�[�n�sr�=d�����e����2_�������O�%��k��KD�8�>�`�����DT�<�D"�.��?F#���%BA{�(�{���>���Zf8�����-U��
��Y]��=����^�k��uo���;/���*�F��s�H�x����W8i�k��'�b���|J9j&CB�'aT�	���Q���,cRYV��u�{1�I�W���S���
��3�Q��'��������S��A���&^}��sF���Q���.�����Ie���m����2�{�mfe�&un����aRgT�Y`P�Mfk��&��xe��\�}R�����,����g/�i�c�i��f�����G��Xoii9�/�?sYi��[��<v���>�����V�F���$S9�q�����\'�6��
�*��������x<uS|[[L&��LD�u��!�b1uy��y0���2������]��
����a������+0��&�h�x�i��Q�>|�h*����
���.Qq���_.��7��I��f��(D�e��]rMK�.W0�e���
���dw �	1DDD��!""}����w��{���W�Nv���?m�Vaq�G
�y�;�9����'���<�_0��&�����g�5*5��D��s�|�8��3���\�����)�6)IeJ���s2������,BUq��p�u�|�8���+u.������<'�.�'�����$�����p��1W������@���~�Q�>^L��S�����!���wfCR�b0B������?�I�(D���q'"���x��'>|�\#��r����4��_hM������D�
��������/�|�!����O�����'K|o(E�7��
������6x��CDD�`���r}�b���VvH(;�3�BD���r���"}������G|N�JQ���D"�C1:Q������J�]�i3b�t3�fZ1o�m3�0M��n��~���#gS���e�\�V?����amE�~t�=�����c�����Awt�2�&5H����_�_i��C��[��g�	���Gmm�\�R*�:N�����"������6x3ua�`P*,�

��!e5��*����&���F��s�H<��J�!""m0DTx��8Q���F���h2!��c�{����I.Q)������b����T�%�~&��Q���5��z�
Y����77�����`�K~�6������+�r����|E">t�C���������Aw|zc��QD*�
DY�]{e6�[�Q������� ���������c�����e��Z�x����]����q�+VL��S�c�CDD�`��H��z�����u:t:�.��������1[=G�Q!��#��=��+U�(@�����yu��0}���9��^���!���U�a���Z��2�5������!��>+O%�q>�Ep�'U/��
f�����64��:Oi;R�nD����#a��N]���\o�E-6\8��v!"""*����'��w�z�6���������'�B|R*�q�2��q�yo�~�b�F�b�H;�Z�a}���Y��h�(�&4���{�y�Rc��Hi�7S���i?�O����P�I?�|��a�b�1����Y.��&�l�I�g���!�����p�G�Q!��#��=��+f���g�+';�f�'"mr��fk;���%��p��>,�M
^�:CDT��_����� P%������DDz!
F��Y�`��}������H$�eq�h,mpEkN����{��
e.F�+����������-U�`�nG�\�����F�����#MX�S���S�&�;�������o�L�	����'dFwD��E��?�N�B��2�7��!�����EW|@�Q��sY;�Z�a}�F�]��H�(��(_��5�yl-0DD�
��������
G8x��C���"��u6��z,j`h~�[�;���N�7e����i�q��_1
�����g��w�Q���#��=��+V�h�#b��u��<��z
�}��FT�"*�\u����e�.�3�	M��Q���C�o8�>�`�����DT�<x������W�Mr�������*s�
�J�i��c��V�U&"*��tU"��C��w����8�u
���
��M��c��QD�M|�.�����|��f�\!���_��qX_��d�>����	�5�\������Zam�Hic*�L-<kWC",�

�������@�
��r���56�wtw}�U�;��k
�1r_+D�!���
�A"m��#*�B��^��	�v�Hd����~����o�[�*CD�������(��
�d�.s�2���h�=�\9��Y�!�7������o^����P*8�����L����P�B"0$�����4GT( ��T<��������8�ug{��-�B�*tF�a ^�A!��f�ZJR��0���8�������w.nQ3�d��2O��)��2�'��k$���/�?�,�mbP�x\Y������T�����_Iy8�+LYU&���W�a��Q��mb�lS�P�b�X�I��8�-�_]V�%`R�S����z�1b�$'�������zz��N��/��/����o���-���_��qX_�\D]h��u����7D���iP�\���^��<�VX["�CDD��*7S�����R]��8t:������
���

����Y���.����9<��xM����d�����;/��V^W2i��Q�2D��G�����r-S��L��L��5�VWo�-7�%��*CD������d:�d?7WH(�1��f3CD�-X�
=D����U����:�jw�t���g�rO�]07�]���n\�Z�����v&CDD�V��po��<s�:��l����($�Bu�Q���|nc?�G�-��]�g�,Yz�\�ZDP*�� 
"A���B2C,Q�E�IY���U1�RU��<&��#.��'�+�K��D�J,'�c$���T�*�+�R��S�'BTq1���X:X%�WI��1��Y�cD�J�;��TQ��!)���>6�������8Ff�*��}�ze_�
���������kD�P�u��([��M�xj2��1e���
>W�ck��%"m0DD��J��:�|�VCB�tP���)J�|���iF[�K
	�kr�C3]�������86>yL��A�r|���]sp�����D���GTz�
�D���D�e��� �S�I�>���w�#U4��
�!"*��U��4��=DT
���o^�{Tt��#��y�7"���n:�e(=�Y� m0DD�oz>�{�;p���8�-�B>teP(:�x�|T��3�E��C�z�)��	3k0w���4-T����m�:u��VV=��>t�\#=*���M?��������]�
7�����0DDz�]��6�Q�k+��U>�%����kKD�`��Hz��z������P�I�2��!�����A!���Le����z��[����G���Wn���9���v *2���s���
"����q$2� ����q����n�A�U��
/W�!"*����D�>��2��O�IE��"}c��H��r��?��#�sD���H4��.�i�6,n�`QK�:_���{�������9��w�N����3��;`@w�
�����)($BB&1��P
v#�j1{�E�j�0Y�#G6��!��L�2���P����K|��������f�7��l?\���K��qX_"!���&jB���;W��7D4���CL��W�ck��%"m0DD�
��L}�'t.$��mZS;��P:44��jW����D�3Q�D�!�����A"m��#*�B��
6(_V
���Q%c���r�q"���5��hA�\����}��
P����xWj,��CDD�V�7/���G}jXH��v�d�F���E��]��J<��x0DD�o�p�������q����`*($;
��5�Q���|F
��{�`���j���:��{1�3��)�j���ya�&����6h��1�q���q�����U!/������z��k��������~Z��CDT��_�����L�u���O>��e�^r}�yl���D�
����Q�7S�A!e:t*��'}j�!�uHK�Q
�A!����M�?��������xvO�����M��\��W/�U�Qi0�@�
�{D�7��K�<�o�q�\+�{.��%W�$��*CD������DvxG���K�)��z\>�\��{�����EAh�����]��qz���!""}+�B�I�Jw:��{��d4�A!Jw�*��������"� :�����uu�1��V�C��^������jP�L�Y5(�4�;,�B���kJ��r����3����C��������|\3�X5]>�8~������R�eR���)����!�������7���m������Y�/�������B��H��&h�u�|��r�H_�����	����PN!BD�yMe�^r�z�<��X["�CDD���f�@(.�B��Pz9��sp}�m�DX�u.44{�S���h,�o��j=V�y,��y�_2i��Q�M6D�����m`��6� |��~���������kD��!���U�a��JBtZ�n���X�-���^4���,�z���!""}�"��'���y�WtR��`�"`�U��jP(s��?�H�""n�kOa��#�7���yr��f�!,����ys���r�6
yw���1��Lu
���Bgt:��*����`N�����5��hMbfU5��\��y05�U��{��H��a'�D����7���������~�u���A�xu���Xx�����k��%��`���DBD�����55��W�ck��%"m0DD��R�L��V;��Bj`��':�r�vfOs�����5�QW5�[�&�A"m��#*����zN`��|�.x�}���i��6����Saky��v|����5���Q����0DD%�=��h���7�$�:�]��=D�����"����ftJ�J������vJu��u�K�!�
��'�[�^)��v��������\+����]]�p���V;
��4�;R���t������(�B����"(d�a��
s���sz+��s`�����y��K��ae�S��Cw�5������/������k�����wn��[|������[q�>-����\�8�/�X&��|]�7�3VH����kKD�`��H����hG@v���>����,fC*$$C3S����e����I2i��Qq�0�{��N�������U�H,�P�&�)S8aQ�V�v��P����	��$2>��R�������������kD��!���U�a��JN�}v��������b��+�f��e����+����B��1DDTZQ�K�(��z#��XQ���b�x��1*]*0�
9�{�����!]�klrQeb��(��W���W�8^ ����v7^��e���i��r����G�����Nt��Ju���@ �����L�Mv���U����ra����5��Y��:�h}�����}���sq"�&���-t��Y3�d�Mr+U�b]��6�5�P��%��o��EIv "�+�:�K4��J��%"m0DD����L-n�Dg!����!M]��J�������P[�K�2�����0�@�
�{D#�}�=__|�^�����)�P"�@'��-����)iW&'�	�q7b(nGGq�w~�:/f��E�U��
/W�!"�2�B��1DDT|o����=xno�Z��&�+oYT�����������}�Dw��.C]�����V�ucQk*0����FGSCDD�O~�C<y�*��)u�:}�:=��f�������:{�0��j�������7bCo�F��3�V��$���7t��h0�0����isakHu2�����l��:N������k����.��Y%�R�*������;�Uo����n�������2�k��%CD����6""�F�7S���CC��h	zIS�]�0�6Kt�!��r8*2i��U���C������`���H�X!�e�N�N@����]��#�`���s�#M=�[�����Qea���r�q""*S,��CDD����c����\�����m��#��6�B0�P$U4.��j.�����Uj���s����!"��D�o=�$�2����^�;��2�Aw���u$��8����������0��ft�����`R�aC����3�E�� �B�0f:l�=m6l�-jPHLSqG-#*G���`��x��v���Xdx��9�X�:�K[�b�e7��D�W�u��h,�m��k���k�_��<��X["�CDD��u3�����QHt:t��.w��f�����mMN5$4�[�.4o�'o�&}b��H<��\y{Oa��$��n����C@tR^�A�J��U���P8�P�"�F 15�.�+H����_�q���}��`�\#�^�:CDDe��"}c���8��COQ�����9���,n{�������{��7����3Q-3�jXHtZ�L��SwD��0DDS��?�	��>o��k�%����������t����p��]��CtESA��N
jP�t����3t�.����1�f���Y0��V�����}|�*]�/�GC>�������nhF��������T�u��h,�Nfm���Y.Q���C	�ET�D��c8��I1���������N���Mh�7cv���R�F����Dz��="m���b����w#�C8�E0D$CX��%��
%�'���5R�9�u�H�n��D!'�������k[_\����kD�!�=�!���U�a���L1DD�o��<$:M��H$:�&;�p�l�\����#�����1J�k��h@4��h*�����g��k� >�f\�~������B��O��������No�!+�EP(2��>.T�@��$��gQo�F]\t
b���9u�an�c�un�4�g�X8:Q��k��%CD��Y[jj������H�D0��a�N������uf���\'���X��h����4��("�DB�j(
!� �!  �0!7"+�I�2��M
��A�D(�����5[���W�/��*k������Q�b��H�""*,����^�k��5�����y���������������XW��T[�bs;���+�ehqK�&�� /��h����S��s��Z�-p�E nGWt��	
��h�����D��0sk��X?&�Q��LD4yO��qX_���:D���	���b[k����jg��]",�L=1t���^���MuR�B
�������DDD4qA�D=�D|��G0F$W@������U
�D�'aW�s�P(��3��j�a��a�n����3:x}&��g�T{"qj��+<[����r��2��X��8�)�����!"������b�1�\�q
f|�]8���?s��<����2��{O��s��:���P�,��CD��MU�>��,�k��=��Yo�e���YN��PW��P�3������0G��Ra!��.�ID��Q��k��%CD��Y[���k�������B���0����(�O���I��>_T����jB[��3��������j�5f��#G� K
�������.Qq��+/��.����^?B�|� ��8B�W�P����)aQ;��) B@	��(�t����������������R�!X
ae[Ns����lP>3�����rU�����f�25��:��Kx����7��K&�J��-|K�~P�Uq�Ok������Q�b��H�""*��O������B�N��������H$�8t�/���Y.,j�4$BDDTxzE�q�����?��jf��a���]00�o����Dg���M�r>�����S�7��B�.���1���lWm��P�\����s��F>��J�!"��)�:�K4����&�����>�<��X["�F{{��Q[[CDDY��8����t�"0tJ�+��X��V���j`h^�S��5�1g�C�%��:t�\�a��y2��������y�g���!�� �!O �0 3!�� ���!�`�� D�T(���91 	�����r02F`3Da7*�)	���r�8�V�.����q7��v&\5����#��T�]a�X��������6p]j��VV=��>t�\#�,�5CDD:�B��1DDTo����yU����lP�������ID�����O��]x�+���UnTk����^P�+�lwN����������� �
"��#�#IenB fA0aW&�2���M�P��P����Q4��B�jP�
�N�P�7����P���I�kf�gQ9��}��a���j�!"a<����d�_����kKD�`��hP�@D
���������
����=Mv�)CC��h�~-���� �6x��$�Q��=o�������/�G A(�D0��&�)��!�2]�D���P����������������T;���T�W�'��(�������D��.O��g���Y��
"�}�q�d�K���I\>m����[o�kD������U�a���L��C�o��'�b��r���z,��-n�������JMO!��8�����6%q���5*'�h�}g���`�+�@^"!��!#q�@(nD@�\�"�)Sj�2_B�g����$Z��@���YN�NtJv�.���H'f��hr8a�����1��=3�z�2�� OD���}��)�:�K4����6l���k����_S;w�������~�z�Y�F����Zcm�H�T%�A�������i�l2��������,�6��D�� �6*�����B_�q��]��������	%�j(��"��*s����`R��\��K��������]
�`3��P���1�Lq�M"$:Y��e8�TW5�];5us`��g��P!�vl�}o.�k����Q+��q���q����Q�a���""�z���!"�������T���t����F��]�Bj������������<�M��EWd��]6X#��-�p���JTh>_/��:�����C �?D0S���(S0fAH����@�n@��S�D���,�B��7t�Nv��C��	c�l���]���0�,l�E�u#��xa��x""��H�H�|m���������-�k��yl-��D�
����Ec	
�C���p�u�������MX�p&��S�y3y����""�F9�{A_7�:���=�����O
cq��Z8!jh&����6enG nW�S�	7��	��AEaB�O�$����M��p*dH(��d5*��n��*�h�5��p���U�B���?z?6���k����Ra!�6&�����VWo�-7�%��*k������Q�b��H�""�<Q�z��?�5�-������jpHt�����DT�@���=���\�
RW�����\3;�O�bW�L�D\
y}������� �(S�X�(��J�n@"��?�R����S�������c5��h9-;
��B��.L����
&R&2��������1DD�o��OT<�Z�a}��R�Q�������^��<��X["�CDTI�|Q5 $��������2���:���Dw!'�7���;� �""��d��h������;
�����H�p�XA��l8iR��"dW�$D 'B	7b��(�8�"�Lv���2��H*$��@f#��6�v'��upU7���	���H�K!CD��H��a'�D�_;i���
�v �)�����U�a���L��C�o�/M�Dg����tL�BmF��[��7��y3,X~Y�V�f�
�DzQ����;;��Z�Qa���b��s}����T\G�`p]���?�����?�A���$�1#Bq�q+�I�2��n6S�xi�#��L��Y������;��&L���IvR&�X�k���8���������\�8�/�X&"��a��]+������)h�C�<��X["�CD�W��B8t��vj?�
u���^�����Y",�B�LeR�=��7J3�@�
�{D��Do��,|��	�`���PX������`E8iC0aW��B��nD�vy4*6C�?���a�(���j��|�p�@+\v�N�j��S=5�s���X
"J�����6p]j%+���m�C�U>��!""a��H�""��?r.$t\����X>������Z�T��2d����D:V�����3��N����H��U}����������?����N��}����A�P����D8aF �*b��.e��h�&�B������~>,��s�:�1DD�o��OT<�^M4D$d��F
�d�|���A�<�VX["�CDT��'3�Q������y�`*����6j����Y.���?_�� �6x������{N�7�`��_��F���'�j�����"�2��T�`�-�D��l��a��i�e2�R c6��S�.��j��j��Y�*��j�f�lu��Q�*V�H�����}���sq"�&�u���#X2k�,�In%�Xk,�\u����=D��MU�xrhH(#,�i[��7�����k)�y�H���~�7{��S�L ��u��MI����r�0�����������O����F�H��1�Y��aG0!�@.u$3��|�g�R2bp�H�|�=-��g�T{"�B|�J�����������0����>Q��k��%�dBD����.@"��k��Q3�b[+�-i�!"*'�P\��Dg�@j�t���������B��hkr������q�A"m����&���@�	������@�F A0�D(nDX��	��@��C
��.n$�:G91"���N�H������dL�a2�i�(�.�.W5�kg�S;�$�$�(f�(S80�������$�ga��as���h*a���""�z���!"�t=������0t��p]��9T�s�7��
�y�E;��L�o�x���4��}�\+�������!�R|�^��w���	�@7a?���8�QQ�0!�L!��J:A e��Qi�Q�F��he~8(�2p"{R��T��������L����4�`�{���>#�8y��l�;��N���!""/�O��qX_��L&D$d>?��5�yl-��D�
��H+�}a����e�t�y!�45w�	91��0�R��<��j� �6x����"AtGo�)���y���!�B��I�^fNMI+�IQ/���J�U�Sy���)���l��f+SL������l��f��b�����U����������I�ht�
�P�5^�:CDDe��"}c��*��.;$tB��������QO��&���6�7/�[9���{����U��o�����K<�>����F �A<��])	8
>8M>8DH��LM�����W��p)sg"����R�o�:� ����Q�6eR���R�l�}������`|}������~����[
g������w��|��R<|&�7^�'*�r����Dc�l�H��5(��t	*��K��%"m0DD�p�L :��2�~��>_��F��,Fra�,e>��v�YKs0�@�
�{TJ}���8������F���V>~��BI3�q���PB��P�S����@T^��_���S�����Q�1��.@I8�fe��iw����S����&T���!�6FTLi�����U�a���L��C�o������BB�y���w���V��k�x�2����QN�p`���'��w���M�[�g~6(GCN�7#��(B@"$B@8�<�3�K����p�L���d�g0�����rz���P�=���l����r�B��&q����&�0�+��[����D���D���U!BDi"��}��a��D�g���X�l��2~�<v���D�
�������O������P*0����h��cQ�Bbp61����v3g��H<�(_��3���B�Y�C}�#�������\�`U�%EHtr!�(N�&�l��n]�D�,�!�!�1*;��yv�n��N�nwjjg����]I?""�k�������C�oQ9��!"*W����Y�y6�����p�G�
������3����x3���6�uP��������i��m�[Ik�>�������8|�.������)�P���:����M��N@Eq#�A4i���P�u\�sS����2�P�TG��r&�r.��}1�MF��&T��p*S*�3��G��#C?��)�+g��g�`��rm���r?������\+>��/�CDD4�����MT�7�

�����G;r�vf5:�6��

�r���klro�`��H<������}'��}e��#�� K"(@I� ��!�v����.@I7�I~&*'CN����B@�@���!�I����zN�]�lv��n8]����O]3�zy4���!""m��Xx�=D��i���pP'dH(=������j+��=M��y��+��:;6>y����T���So�~6n{��.Do^&a�#b��u��<��z
�}�4]:ht��p"B��C��9���"Q�#�b�T�C-rXJ���S���$��{za�a�d02�!�����(f���L�;�
a8U�c�c��Lp�����r�a�y2B?2$;�@��Z��B����>�kl��(�dR^������[�a��*�c��k���%rK��}�H�xa��x""����D�
��(bp7:(:����������0
���krb�,�:w��`��H<��_<F_�1�z;�?��@��`8�����Z%LjmL��"$���%��A�����~8���jg6cX8�a�+��)��C���68��]�p��Q;m<5M�HD�/�����Zc�1DD�#,��CDTl��Cj8�������z�����d��U�����b�1�\S�SjUE�!��h$�yyj{a�&<~��#��m���
�\��f��
��{
}����������'���IY���lW�.D(�VG=����#���T��#��dCN1����(��iJ*�.���N\�Z5�#�?j���)��.K�w�����O�oP~��/�����&��{�-����jTL|&�7^�'*���h,�-i�!"��'��B�r�O��^#Z�8-jG!:��R>����D���W|�='0�{��N���x��2E�)�P��v
'L�����������K�X+?V���D��f}����p�c�<�)	�����LN��n����j����98r�8�=�c��H�5CDD:�B��1DD���t!!1R��Gc�k�Q��:4$$;55L���=�p�����gb�x��aqk�\�7/O]?~�~l�_)�2�^I��S��U����p�Mw�5�(z����4��������&d7 3�	�2O<�!�@��x�r.�����W�������������C��DHt2���(tD�mq�LI8L�EA��@��U�z�\�����0[���TDG���o����=��ul+l��������J��(���D���D�����%"m0D4u
��jw�v�Y(���:����M��B3�+op���sot�������	�@/�A���������PX��cA0aU��2�D .B@n$�V���0b��yb=�!����B�"���}�d3�z�N�E��r��r���zj��j���|��#*=�����Zc�1DD�#,��CD4=�a!!1���G���0,$�,�gq.Vm�z�<&�� N�go{��|�l��o^��D����q����Ab�x�}�������D��.t�@_�������j�#�!7 7
��e�Hu�� �[e
/�b|�52?cd�:CN�W������f��!:��yNs����Z��9P��F��5�M�B@��``H�����o�{�.���E8i�{5[�q�q�������w�=���a"}��}��a�������6"�����~*�

����b��R��\h��D��.��D�kj�������p`'_GO�I�5���Y����M�4~�|�����.@g����@��@8�`$���	�	�Z��P{��! 1(^*TI��U�!)�d>��.@�^&:�zYv����]�g��Mp��]����nDm�8������=��c��H�5CDD:�B��1DD�B������� ��+W�n���Pz�����'�`�1�����s<��}-�v J���S��?�#�I|�?�3�B�X�c��7�����}�]���ua`��H�p�X����*aV�V��vve�D@-|xI��Q��L����5��S�N�uMpz��hs&��x�7���;<{��z�kQ�E���i����������B�,m�}�H�xa��x""����D�
��*��&�
�::P�i�a3�M����P*0$����[�������g�+';�f�e���lGpI�4\����Tl�z�%b�������x�����y��Ot�*sQs(s��	(w#����mz'����~8d 1h�
ae9*;%`7%�P^�.�N�nG5��zT54���YI���GTzi����c��HGX�!�7��R��z�Q�z#��XQ���b���@E���!!eY�w��oW!!WHH��D����D�3Q�D�!��(_�yy������6p]je2�����Q+���m*l7�X$������s
~_?�D��GE��`���S�T����2������s)�D��W�{7�1&���jO$^h��������n��\#]2E�����Fw=�L�����a"}��}��a�������6�j��j@�pO�CepHL����e���jw���d�!e.j64o��l����C����zO���T�>�CA#acq�IFD�faE0nEX
�:�KO����X��`H�D�lJ�n����[��8�p;�p��PU55�[a2[����{D���6Xk,<���t��"}��!�7�z���<��W�-��-�jq��:\0�#��K$�P�A�!��<N
���i9j�����\���Y�u����_kW/�S^k�j�����S���[o���=����o�k)��S��>_����� ��0��$�1(s�q�E��[�JK�r&�N1����,�GH��aT&3�e\v�:�Y����������I=����6��K].�)#��������?�7�F�o|&�7^�'*���h,�-ic*���\������O�*tP�O���&�6�g���B�f��CuU�a:���2��c?������)�X�����X~��B����t��w���� �! @��2O 7 �4#,����Q��'�� x��d���1��[��!r#���<������	�������N���F�
��{D���6Xk,<���t��"}��!�bw�)���H���	�;���G���z��p�����\Y��T��Y�@�~
]��0}6��^w��/J����e�#�c��J�&������F�)��^Nne�F<��K�d0$�4��4�������D��(F:K�nN�e��i���]��5u�PU76�`����	{�y�O�'���y��q��W�4��Q�|&�7^�'*���h,�-ic����T:v6���OR�!e������]����h����\�0��������+������5���������VWo�-7nP�J	���9o_|�^��GJ$���IY6+s�q�:�:^�����^��i�����D�����������P��+?;]v�r9��q��S����Y�HT.��GTzi����c��HGX�!���"�sx=qD�3^�C���k������=�xR
�]�dH(=��v e�i7
�$$BB�r.
QS������j��H��:�����mGpI�4\���������.���c�=�@~��
g���c��H+Vc�7#���8�1��I8-b�33�$,;��_pj���G�V�������=t���/�I|�n���/6o����r���}�H�xa��x""����D��J�s
(K��tg!58t��p$u]A+�n�f���25��.C��:o��,��}o.�k���.v���Ct����DG�D,�����������o� ��(���`$�P��p�,;Ye ��H�Q�����"$@e9$C@Qe_,58�)	��������
n��fj���G�J��=��c��H�5CDD:�B��M��uN�>7Yb4:1*]��z#j7!��PFX�dWyw�^g���|������m���JVV=��>t�\o^._�X}�G��9
�����`�h�x��2��%�%��;�	�rD4Q9G��.���bW���T7 �� PSD-z�Q�b�3�	.�
N�O��Q����^el�|�����j�w����;`�~�?L�4�w�\#�|&�7^�'*���h,�-i��CD�T��E�u:t����t�L@��NS����B�����v����7SW�{~G"��� ��d|
i����[o�k���{
����u!��/�C@tR^�b��`��P����B	[���t(sr#�Y���`1D`7�����0l"�0���A�g��f��.:y�r���fj��}����Q�1DD�
��!""a��H��J�H�>w������;����y��F�����@���
RS���j�� ������}4H�B��a����XpCk �Q�x�rqDB>�GoB�>��>DH
-�C�	#B	BI��lE0�P�!"��*�8���C������a2����DH�,��!H�L�PB���N�nTW5��zjg�S�Js1��������{������[2)�@�?�}T����(/������,���>&�U��/�CDD4����Q�!"-k@��B��B�e`���}a�WF��-B"04�[]nkr�����Sz���+�xQ���4�A�+���@�Ix������G0B G8�D@yk�Mj �P����'���w��	�gH91$p������`x!��`PvCKv��	N�
n�.W-��FT�����A�����GTzi����c��HGX�!���"����b�1�\�����xp�g.�k#��sa!e�^�D~���CB��|z���*��?z?6���k����A�zmd��������k�����"�x�O���>_/�~���b4�xA�H-�XQ�!6�v5�#�@��[��Q��Aj��T(�(��Cp����q8�P�
p�S����5pW7��~6<5M�(�������7�p�~����������_��7�G���k�[�*���������!""kKD���Q)j@�DR�,�~��C�\
���J�}j��0�������P���t�7SW���y�z�B�Vx�\�K��I�
���v�������D�+I
|BI3�	�
'�&E' 1�����������\ 1�����B@N���)�������v��rx��j@Um����������J�!""m��Xx�=D�6BD�<�M[��5EV1(oY�������]s���]�TH(+,��������,�
e���s��#IM��@t��K�Z�1_wb���x���>���*������=�S#�y{����
GV���Hh	Bj!��,����&��\�*���J)�O�B'�!�����B�"B@�T�aJ�nN�e��i���]��5u�PU;6��;����a���?���������:���}7�~�~���b1DD�o��OT<�XX["�F%���UZy�4���B�)?�O�q��_�� wjdZ�-��D�,7���������2l|�~lD0�����?1��o�E�����P0�R���!�Z�u����M��D�(�T�����N��C�����f:����df��*���J�!""m��Xx�=D�V�!�7�z����*�
of��}a����Y���:$,��0$~�Tx�>�8�D&��@��/��~����6\9��������4�g����#��1��	�F����(s�������"@�`>"��(��h����?����p�p���A5��CsvcN�Q-��N��U��iDU�L�6��G���)�{z5�FN�e��x��v
/�{���������w��L���,�U&���������!""kKD���Q�k@Zj�!:�R��Y���5n��W��Z"�>x{E��~o/��!B�0��8~sv!zb����U��b3�����	(�1���l�x8�	8��G���������L��TO�2��M���J�!""m��Xx�=D�V�!��O���'�Z�2�j0H��!������-�rRIl|�Al�.�2�4��x���)���;��P��y���������w����?�C0A0E 
�����6X��?A�H�~T'7�I^����)|tA7\�jTU7�����z���Ao�x�&$����sHZ}�K4�?��jsj���v���-0��(L�G��FT)""�7^�'*���h,�-i��BD�P�Y��?��yM��BN����U�N��7S���O$�,�}��("�Tm+�4(s�I��lF8aA6enEDv'�'������b���u�
eP��������+���C��� �]��Le2#���9lvX�.��;5�����.�FD���=��c��H�5CDD:�B��Uz���o�F�)�\�����S����Y5�6!���SO�/��Q��LO���B�j`W!����#��������=Xr�Mrm��y94��p�N��@?���B��	���I�
%l��9d��N	0lV�����)��[����#(������?�������t�\�R�s!�#����0T+�!j�`q��DSCDD���D�����%"mTZ�(����T������.����{��M���5	��H�K��n�aJ��l����
9�X�18I8-��,&8,8.Tyj�����z������ �6x��CDD�`���""�z����BD��(:������{ �3=a�l��#����2<�1GND���A��v�F;��&l;v��Z|�_�yHG!eR�b[��_����G�����rM&��~~��jM�%�&�$S�@�����=o+�����(FD#�`3���B����b�1	�9�����Y�r8�vV�UU���&xj�`2[��DX�g������.r��h�,V��a�H�x�/�CDD4������Q,�D�7�oD��)�>��>�,+su��|�3(�1�	$7	��v���
�����&�����R�7SWr��
�b�]��� �lF�	H��"��W�1��O�0S �w��:�kg��~��<Zi0�@�
�{D���6Xk,<���t��"}�K�H�D8�K������<�p4ug>nX��~�\�[m���w_!�
��i>J���sc��;�G��=���3�{����9�������
�� F:S�?~X��hb44cHB"���2 �a1�i���p*�O<�3Q�8W��6>� �
\�Z)��UO���!��T@ �7��D���D�����%"mh"�'�j�G�{DH���P�J�S��(���M��Q����&�jKO20����?
*����a�P��`88r�'aR�T�'�� ��O��X���18e �
H�D��U���pZ���w�n���:x���S3w�<�~0�@�
�{D���6Xk,<���t��"}+��(
u�E�.B����Tw��8B�y���_3G#���6���}��G/�[�3����*���p��R	�z��?����������B��2��'V�*F��"FO�$E��v�Q)I�q���!���XW�KU9�0F��"�Z1�yP��*���ee�)��[�p��p��p����j@U�,T���G+O�>�8�d���I|%jQ~�����F�����&�7^�'*���h,�-�DDc	����7�^_��X`1�{�x:D�H$�uJM"4�[�X�S�����J�X����Dw��&g*8�vr��D���>g��B8R~��`0���������g~O��q}��wvc02����{)�����b��"@V�.x�upW7���	���@�S�D���GTzi����c��HGX�!��b����CA]��B��P,^���/�#����k�RWcS�?�5��Y���W��k)n�������e1�����n?��3��W�B�D�'S��ZX	'�$L&RE�	%�j�G�7�M�xIDATUB	���DL�WJc��hd&�(t�@�"FB)�a(��j!���8:�R����
��
����FT����z�<Z�za�&����6(��g����w��W,�Y�Q)1�@�o<��������!""kK��7�z���<�����r� ���-�jq��:\0�#��H�	���T�'r�3�:�`���B��P��A�(�@r7�}.����&7����V�C�>�^��^��A���4Po�����y�5��w����i�#A�� �6x��CDD�`���""�z��m2!����2� ��y:(Oh�-�\�?�(���^]B���(v��A�>����s��}�r�����SY4��@�i�:�����C0D(VC?�x����~&���B?	����s�����k�����=����MM������Hu��������r��tT�S5
���p�T~�P~������R�eR~^��)?��?����}�����[n�K�Q�1�@�o<��������\�8�/���(�<�M[�����|�l��.�h=�yeO;��1��IX����X*�����BCz�Y�5��o��������z���G�G
�@�����1�W�!�LAX���%��Q����7�Dd�|��y2��nc�������Mw�5*���s���""�k������Q�b��H�r����h�����:Oh���-*���sB#��#>~d�q��t|�Q���������:��u#���?�G @P�b)�.�x��!����EYd��	'��~���&_v�V����b�����<��Dq��l�l�8�&(s�n��eu��t�������u�aw���Q���D�v��~��(?�nh
���@ �7��D���D�S�u����kK4�=���G���WnIqCXP{
�\�8�����&C�Z8��������UrKe� 1��
vJo���T�oy�"z���{
H�P)�������P�	e��FXM&8,f8��-v8�8�pV���j�c�:�K���z�"�6y�G�9�,��%Ko�kT2i��Q�1DD�
��!""a���-K�G?j=V�y,�kD��St��k �}O�G�4�K�����Jzk�a��C�rm��Mo��\���V�nd�D,����j�'��E 8�`(0�P,�L	5�N(S��^�n?��mXt�I��O��ls"�<�
 �*��1�����(�8L>8A��]���Q�(�������
p��p�@.7��ZTWO���V��(�W���<�m��V����)���;�i�"}�9L�o��OT<�XX[�T����C�Q��9/�S�q��D�Mn�lm�l���k��c���P;��Dz4�W��3�A�yz�X���`o%P������KZ*%�}������]'��o=&_���D���GTzi����c��HGX��bx������s{{�~�/�#�eQ-�^T��z�V��.�-(=��&�����<6��564T[S�+�[�k�[�#
��[v������#���_t����?��@��`8�b�����2����IZ�����6������
���To��
3��p�as�%@���3�V�1�yyj�����}���sG��b���������D��s�H�xa��x""����4TfM����a�v�f�O����@t�����&�z|�r
������#�/SC?���D��Th���$#��mQ�t�e.���eu[j�S��A�^x���
$����v���>T
��}l�>�����O1����8�\���1^-��Xw�
r�
�A"m��#*=�����Zc�1DD�#,�P����HsichX�,����PG�#�U�X<�N�=�?��3�B��Q9�����f:j�N���I����]��m|�~lX)����$�Z��)��-���n>�D����H��v������*��(j7�a3�yP�[
a�k�m����k-Um|���������k�x�2�������<���Y�>�B���6U�x��a"}��}��a�����][�
�������q
�gc��y�{��zm#�9s[/���F��_��3Q�&�������L?��[����b�1/�1�E����Pg�A�������j����\,��?�������CA�m�(S4��A�qDH��D((R�Br]n3�����3��[o\(�
���`���
�	���sVy]�*?���}"�8B�P0�S^��	���D�
�6��w��W,}�J����s���""�k�����0DD�"F�{��#j�h����.�m;v�Z��}-e3��xE��s���e�����(2U*����*���A�PP������B<A�1��g�����n���,�W|^��D�GU�����Z\��:����sc�<�1	�ITL������p��pyj�t��S=C���$�:�K���z�"�6y���{���=�7/��a"}�9L�o��OT<�XDm������'p$���M�f�����v�g�X;�g:��u�������=����@W7�Z'u��H�D�=5�n;�z��uM$��s7^�G<]%�fJ]1M_7��~��m�-x����=]0�
��z������z������S����;� P��PjJmK����l*���b$7����g��qc������������ �J�O��q��j�7����p�Mw�5*4���s���""�k�����0DD� 
C�@$����q��}��,
A� TND)�=hpV��s�>_�C|%r�M�p��"�PmS��!s���>V������s>�~�!?�(��8B1��&�fe]�&j�&�p)�S��R��������4����$B?A��b7��?"�c3�����$�+��d��bR&+6;�7�j��L��z&��Zy�������6p]j��VV=��>t�\�7/��a"}�9L�o��OT<�X����3�Y��X�c>�t�s>q���r���~�>�����F:�d�������t���E(���D�q��O�U�������c��������]��v$���\��'���z������g��B��|���v�stS�[�2��������
��!��A(�E8�C8D8@8E4Q;�D�1D�IDI��������.�7���/��'�{��gX������f��$:=~��#x/oQ��oh
�Q���#��=��c��H�5CD4%l����o��-[������c���X�l��R�"�h,��HQ/��y,�����fb���xM-M�`&�A��yQ�F����0P���?�v������aFCM*4�:�i�nxL���|���"����>�	#BqBI��lC0iG(�P�?��9S.���������?���������c��n�1a�
�N�.W<u�n?&�UY_�}��������GUQDXw�
rm8��L�o<�����0����>Q�0DDT<;w���]��v�Z�%e��UX�b��Y#���m[��;�'T�Q;X1�+W���~���L�������Q�n;�\�q�w$���Ql�zW]�9��F��X����W�9�&x�x����g����>�*+l�~��}����	����0"Qe��0OQ5��D4i@4��F��b2)�����#b1��Ve=5E�)��)sH
47nc� U����L4��0�a5�4�����+�xm�@*,�{D���GTzi����c��*�(��[�nXx(�(��{��e&b�hlb�g������8u�jOa��g��8�����x��Z\�����gMw}�U,��V��������3�-���Fg����2���B���^�s�����A���-�%�-�|�H�`2!a4��<0#$�;�#���&���$��h�'f���)�?"�c��~R���'
�(���$fl���|�tX��;\p:�����U����Y��S�u��7���AI�?��?�r?���w�:
o^&�7��D��s�H�xa��x""*18]vx(1X]9�������S���C#Z�m�cF��^o{�����;�u������3rmb�������������N��c���~�O~�SrM������:}�Go���;��p>��1d�=���x��+F��-cs�f/�mA8-!8�a�MaX�qXM1X-Iu2��W�xy������A��yB�bj�g0�q���\�0�2O�K���2%�[������{��Xx3u������}���sq"�&�j��c������%Ko�[��x�i��Q�1DD�
��!"�X"@�|�r���;v�u��!��m|�:�?Om��6���h���������k��S���O������z�%CV�g8�sx����V4.�a��GQ�a��>�gv���6���E���%�%
�%��M��I���S#�E`S'��O2�(�L�@4�WJ!
H�:�D���ML��[`����2����j��fJ*�k���N�.g5\��pU���]+�J���G�����r-����S>���FyA����[n�K���������0���&�7^�'*���
o���cN�IT�y�f��=�h��+��(��F3�c�����a&x��f??��D�&r����w���xYIjK�����?��`�V��������do�������sw�u�
���.
l�����������|pYBp[�����������T����P��R���z=8a0��q���	1e.�=���My�����6uM��PO,���8��>V1��>E�����p`'_GO�I�5���Y���w�@���G�
�{D���6Xk,<���bey��8#u)*��x�BD��>=��:��a�l�m����w|
y���sx=q����ibD�}=�����`qke_H�>��Wq�%�/����F|��V8�&t�&�
�K@���E���6������$&e2�0�BR��="I��t '�	7�I~��x]��0y�gr���1~�!�
�R�F��������~D1&��E�'	��oo7��Ya���"��Q��V�96U�3a4[������D�v��~��(?�nh
���(�7/��a"}�9L�o��OT<V���������:}���'W�q���:��A|���Z�DMd����h�vV��/�p�GnJ����s���.@��rF�G���P�6��A�n��y�!�N�&�hcuu]��/��������k#���`B�$B=�<�����$��l���'m�����\�Sb���A�:N�#��������R���D���G�
�{D���6Xk,<���"epF.;lTN��l�������G��t�0�����j������Grz���Op���b�#v��g:��u��������X��i�q���K����PQL�����y�p����������t=^�������k�Qo�C���Z�NkvkVsFSI�I�	QC��$F��!�t���P���H�j�]����D!F��t8����pU���W�?�����4m|�Al�.����UO���!���������0���&�7^�'*���
+��� J�6*����7���2�vg]��)����@��:�5�m����w���#��'��k���������@�P:0T��h�������������$�8��~�27�DG��Z3y�_������w'#�I���F�2�����
�x�h�1��"~nf�����a��F������s�H<��J�!""m��Xx��8��e�vG�e���u.;�;�8Y����[��@���q(|��2��
2�P��Hq'_����x&�Jn�����������m�-����W\G"1R�/����<gz��Zx�c��K��P�}h���Zt��`��`1�!�VI�q�	e��v�P(iG(�T&���I������CL��Izc�r�8�&7�a��{}�v|��;�Qn��}��w`_p��A���#X2k�,�In�o^&�7��D��s�H�xa��x������Q�u#����DZR'�I�q,�uz����L_�^Dc	eJ"��[�����u��	�
���%�ga����l7(�Q�+j�1���T��.�&5�W���!<S;�(��XF�#O\v����X���)���eU����Rc��5U/3�C�������s��?{v�y����h�#��-cc��b��!n�	Yf��,����\B��7��3��������������0\&hnZ�!l�$/�-/�*��v����}U�����SuN�9U��~v���S�~Uu��V��?o�!�`�t"���kl�ND�N������[����a���"�"&�1��nO<���5�e/���^�������s��O�)�����g�}vn��c�����.�����d�o���U<���[w���C�M��������a����>��y�?0�i��5��[�����+7�A��G�K�]�����]��;���������d���;t����N@M�}0������G�����x,;�{3;�gs+Ysh�����C����g�\vev���.��9�����7��-����{�*��������/~W�N�l�<xv���4���?�]��k�k�}I�����E?^�a�0lb�M�>tG'"hO����g�t���~���������u���
m� n8��l��m=ig��dou��L�5����[�u��'&�������a��<�7���n<<�zzO��b|O������+��S��N�����l������������=��yI��y���m�cjX��!�`�t"���kl�ND���	�E�C]�;g�����^�����������{������M_���/������?������d���-���)�B�����=�k��s���i��O<�������/>�����NA=�5l~�SPt���������k��]y�����e�>���x���_�}��g�S�������'�C���o<���N@O���O��OL^?���1��d����<�]r���C���.}�3�+���yi���7������l�E�^~������������e61�&�a�4�Cw����_bh���"7���O��WKO�N�u�m��*gA]��5���'{4��+:�<���N<����N<����lw��m���l2�zr�����]����l���0�>��gW�w��l��}��}��{�g���O�C����?�����+��{��97�c(���C��z�=X=��`=����S�oo������3����D��������fJ��y��Z���3W+>�b�X����g�m�'���_���������E�S"Y�o��[�����g/z���;�z4P��g��O��O�3���w�|��#O��T;�������gW�{ �t����=MN.���y2�=����,���]����dOl����e�nN����=�q {���#��G�<�=<.�7u�XBW��\�1:��O�������g�w=�����o�����}����(�t���.�����~:��_��*w��[_���}wd7��+�W�>~��&�a��0��}��ND��4Gt�����o�_�V����������~�
�_M�i�o����rR-�����A�w�U����~:��G"����=[x�z:��{�I>��6�3�����N=���]���S��d�������'��wg{v�����/���$;p�������syv���O��a]���C����b�C����D�!����<�ND����eDm�'��G�!�@�r���fyIY����y��ox�������)��?���dw>�-y�����'.� ��y0{������1;�����}�f��>����d�+��&���ve�{���=��[����q {����[�~&�����'�o�"�&����{�����&Ct�����G��ozt��e�&�'����d���O��{��l��+��z�^Py��?8���_�����y���m������_������:����0lb�M��i����5�#���,s�������vmz�;�e��mW������<@��@�i�h�:����R��[�;{vm�V/�����3��N=�y����x�g2����v��z6��;�L���2�o��o����7��o_��������.��4�w�����O���,�����>�=��������g�w��:7������C��z�=X=��`=��W������������G����u"J�]G'���_�>\nt]��-���b�,k{4|��7f?�����������do)=�h~�������GvUvO��g��dxt3��s`�I?=y8{b�E�����O:���l������!�l2<���d������������g�\rev�����W<+��i���-��;�|�8:�ND�����3��<U6Y���d���ac�?����_����_�x}m��e61�&�a�4�Cw����_bh�?��z����f�ky�uu"���|}�?r�������G��V9cwx���|g�'{2��w���k3�3���N=�&�r�v���������/;�o���l������e�]�<|Ev����={W{�������w~L
�!�`=���ND�r�����QY�iB�t@j�O�����}���"�"���������wd���Wo=���/>�������������[���#|2��|���}���N��#��4�����K�mrq]�tmm�Un���l=����:E�����L�����?���e�ld�'����.9tIv���eO{zv����^���u�I��B$�~�C�.�#]Mq��W>�A�#z���a��0��a������Q�g�vZ�R[��$���ikR�������N���f�'����X�{��<�e{&�{����l�d�z"��bk|w�>9y�d�w�������rvmN���<tU����&�����/����l�C��{���m������g~L
�!�`=���ND�r������D�`
���~�u��|�|�}[�o�?o��������������l��p�i?���<8��P���w��������������	�x����d7��t`W<�g��o�����z<;�;�L�+;��V�C�.�.��������.���l������m�u"*���]���_�����_����������?�x�M���a6
�����q����>;�ND�o��o�x�jbJ{��LSG[��j� �!r�����;�����''�)������f�gw��o\������������o���g�����f{�M����}��}�=��8<y}Y�o���K���b{��-����{�*���k���+�����W;��;7������C��z�=X=��`=��W������j'��~���?��Z��^��6�)=��$��d;�U{��^��;�Ce��$;|���%�]�]v���}��l�����;~��w|����]������w��n���+���gH0�[~��&�a��0��}�N_�8�KMy�b'��������1�N;_��������������������x�N���\&��|�w<xe����s��7f�{�����7���
�.4�ND7��o���C��z�=X=��`=��W���+v�����������UIS���H��YVYy���lTl<�=m�g�k���=g�������xY$��s��_s�]��n����������Wf�}������
�x�
�����^����1��W_��/�v��	��x����^�0(�������{?��U��\�eY<w�V9������g���������`�_������+��������W�����������&����5�_���d��_yMv���pn�D�4����~F>6L��#�s�%nQ��!;S|�?��we�����
��������[y!�	@?�D+����fw?R�3\�W�L[����'o/��i���%U�Z���q�Of�v=�]��S�3�~8{���e/:���W|[�
�o�������]z6��W�����o����7e?t����<��m5���o�h��_�@�o�����~�����o|}�o�h��~�;����r�?�������>���mm'�n���|�����9/8��a(���,����2+\����l?���x���r��9�^uu>V��~��l=��-���r�p3�W>��|,��.�;�UOqQy�(n$0|��s��S��G�C��rKv����U��;w����������'N�����w�������_],"������
u[����w�l<�������o<����p�o��l����G���c������d�'�����������6�f�w�v�9��9pY������n�������o�_�����������;���_�=�y���NW����_bh���M7���9s&5_��Z��������w|y�*�z�[�:���V9���oe/�v��o}������05��"^�����������}�������We�����E��6���]����d�{�����}�\rIv�e�e/|a����U�q<�����n��V�3�|4��������,��H[e�RN�
�T�2|���d�y���������i��}��f����x�[��q��d��we?���g�K�^�������pv��We7^����{^��k�<{��/������<���g=���_�
��O����U_���%����,�}���u 
�������_����Q�D�Tg������G^����.�Udf.�������9/�e�D������\���>m���.:�|��yq�(���Q������b~������+�L��N�t"����g�c��$���F��Ry�6�*�H��7ue�mdOf�v=�]��S�3�~8�~���/?���3�]��j~�g�����g�|����z�7d�_��[\�������m�&�����on9avQy�����|l�:y�%sOY���~8[���n�zJN����l���[�>��;���(olb�^���e��o�����|c�&���C�����:E66���W=�h����y�iBG��_e��s��o�15[:�����'N�������f?��|���r���G"���������{2����e�\rEv��We�/�d�&��B��_�~�=_��Z^������n8�������/�Tv������&["���]nk�����_����_����j}���/14���n������n�);s���'���
}���?���M_����,�e����:s������;x��?��x���We��y�����������r�����_�����G_��S_<m���{0�������w��/f���=�{�>�������w��d/:xWv���u
��*����LG�[n�%;y�d��Y�e��k��1��"��-��6+�3[�e�3A�v�������C����}����x���_�����)���������0������LG�����R��x��eg��<��y�E\����� ���\�o��i[���N�Uy����{>����>�����_�]s�K���.�?��*��+�g�N?g�������Qx��M�[���,:�T{2�w�r�~�,+����_x�r����Mtf��r��U�NIE����[	�&b��O"����c�X3qs��E��Bt�������zc�N�N2�Y�%�F�������=[ZN�"��/yW����n=U�J�����������c;��0���z�P����'1h����������Nqo����~�#_3+b.����Yb��������$����5�����t���n�_�7��I�S��/����������'{�C�W>+f/:xWv����J��ID���~{�������������G�WYv������������gw|�3�{�v Vus�?[��"/p��OL�6�>��3��������zz�m�1=�r��N�-����DUy����Q��M����j�O�����}���"�����d�\K�z�%��~�[w�k����Oeg�{y��l����I=V,������������j��MC'a���������\�ua���������}���d�����?zYv�������}���xyv����=�We���Wf���+�=���y>��?�>s�G���yvv���*�����%��V9,N'"F�i��/I���_�>���W5�j6X�E��Se��~��o�^~����O$����j��r��wG���=�#�@[_�8�KQ�����umx��'��}����_x4���}���������
��T�q�
3x��)D���$4M��(:���k���j�s��]������y~������N?/;�[[Cj����@���?������)���>���v��Pt��BU�D���#���D��k�������W"������t���A�N�z��qqG��g�^��(�{����o�7}���WML�;�?Y��{�$��?�Ys���c�SS������\v����=))����w��/f���=�{�>�������w��d/:xWv����n8���]���$"hW�4�P~�Pt����gN}V�����Q���<���{���:E��3�|I��G���YV�r����Co�~����_]��_�S��>����|����#�����?�>���eW>����k_��?tY�)���D�;~��E7��%nbw������ND�Zt$�����&{����v��WL���K;��g�L�����=��o�z������zz�m�����j��������g�<����`1:A7��HT������3����~Q�':;v��	�����O=����Wf�?~�$0'onn�g���^'�:�<p����?�'�g��hv�5����We�\�����j�,��������={�������#Gzws:�G'"���s�{�[����?����3��.���@v�}����s����}��w����D@:�ND4Q�������ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:A��r�-���������x���n���1-���ec����l���"."V!�a5��C��-��8��l�i��l��.��������=�]T�F�H7m��P���r���T�O��.q
�qP��1������r,��/Sn��lX�ec�M�&�7���M���s�e��.�����k��������O��TUyu��f-�"����><��m3�����s�6o����8,1ML4w������"���2��0_��i<4!�a5�X�54��.����aHb������>�Y�eC_��\��Q��9�kj�eC_T��}��������W3u�:�)l+��,��e;O�e��-{u�aUC����c���H��Mtb�!���C�}�j��C�
������jXd�2>����u~�����^�����C3M.�!����V?@=i���� �a59��@�����Y�eC_��}���B�cv��1���O���>��r��9�A;����0�8�mi,4m�������a�����?����x�C��</���z]���c��c��W�[w��i�Q���UX$����]���;�j}wM����7���m�TBqV��0����+;z�h>�����[�W�N�>}Q��{e1O�;O�1�~���b�	1���/r,>y������a�V�`�_W�B�1���
b���>\u��X)�N��s����l�+�?hGz�q�d�E������XhJ4�F��={6����88k8r�H>g5�����^\_��b����������}8��K�6�jMb���>�e|��&aK����ItA�M*�����g�ic^`��A����!���I�u����*�c�/ebV���0�X��������t�n�1�"��=����}�J:���o�!�~(eC���p_���.�Ys���4F���"���*[�2fiM14�5mH�\������hJ�1dU�n�b��~�e|�=�,�'g����uV:}�c�,�rg��t�a��������m�G:}�eQ��[C�Y��@���6��&�20[���B����4m�py:�T+����N�����ah.������S���8+O�v�0$���N,�	�Y1_�N����a����_h�!�����C-����C�u�`���=i<�E����-�a^L���k���<]�e���{�<o���r�b��I��:q����0My����<��cH�����t�u,sm[$��}�I��]�=T��+�]��k�>����c��t�N����yd!�V~��$&����W��4�����������V?�|��|G|�����(���Z�X\�������Eb���#����o�=���v��z�����rN�<��m�w������t����
}���9{�l>���q�Ny�������
	�k3�B����wQb���;$X�4������9s&�V��K�6��-{'N�������.�C����IZ9��g��"s����n��V?�l��Vn�K/������!�N,����	F�vi|1��V�8�aXN�oll\���;wn��V��N�����UI���
}���*�3��G��d��!nv���:�W�
	�i;�B���t�_��cL�:,8�A=m��\��������6cO��t"�5I/���[��N'[M���.cX������h�������>�a�^���Rb��>}(�����h�}�����_'�<������=��J����T��r�L��"7�+hC������U�G�1d~����.c��!T��c��q^y�EL��'�:A,�{9=���IL��8��� �e��Bq'�B4�q�1�(7F/��aX��o�9�.��N��������������,��l�+�?�?q�N��envQE��E�uI�14[~�$�X�"���*�����{�v��2>��t:�,sB]�R���']����K��
���~��)pcc��!��CbV#���w��c�����;�U5��ahGtj�G!�4����*C-�������6����2���mV1D;V�C�i�);Y��q��h��L��f��nW������������{��?��kChG����'�W���1���W�ND��>.�JZ��8�O���2��P-���M.JC���"��R�s�x���G�nM��_&�au��s�hUB8����N�:��]H����<T�w
\WzlC-�����mRqm���%D�����5�c���*���fmHPO��7���N�C~����`=��e�}��!4�~1qS6��}]����O'"(I{"O��#�Z7�|s>�}����&���F�:�1M4.�J4����kEB8������F��c5}V������?�����*�Qt��9��T�;$X��X4�wF�
��8_��	��_���{����r���uT�~(�f��*��,'�U�� �����[�!��2�&��8��1:M$��C0D�f�T�v�x/�&�v�yw��1�����R�����^�� =5����fK;��;v��n t"��Q�B7���EG�.cX����}96�}W1��F�8��8q"g[��U��O!k���������D�|W��b�']j��W�p^��k�b�f�^�{�n��H��S���mH���G��r;�^����n#{��!��Uu �k�i��kCh_�D�������G'"v���� ��|q?����"�4K�y����������;����n�ysUcy�}���VU��9�m��=���H��`�����1�Nn'���� ��X~�����`��:�q,�[���~�������������m�����{��y�@���cG��~����c�*������q,���x���^|Vn�y���,+~C\���	U��&���1r^�B���lJ~�����`��u �s�i���kC�'����@�E�{��
��K'"*�g�]����1I�\��~�0,�nG��8|���|lyb�K�@��I�Jz�N�X���i�8�j��W������m^O�Sh�6$X�#G��c��<��=����`�b?]��2\���x����7��������i��;�;@��u�G��]���KEi#A���^��8�=ON��L�I��mOY�mi��;��!����Qpm���sN�_:��?�V'.666.89���&�]���.���E���rL�x��h�+���:�aX�&��N���0��2�}�j��W�����
	�G�1V~���J;��W������d���.�C����@�^�������,s��.cX���0lb���\9�v�#��0�K������Y�Z6�������Gq
����3�X&��C������'N��VoV|�6d����4]����M'"X�e.�`-S�$�.���2���=1�K����e������������9v�X>�LZ7T�3������=il�A�B;�!�zuq�{�M���!��M�@�����1/�6d�n����x�����m�2>�^}:����'O�����	=�
q�o����e�����q��d(����YU��a�^9B���y���0��~I��I�ET�eo�e@_9�A;�0�7���a�k���j���
	V/�[��d��Nz������c,�	���D�
a��HZ�[����2>�^}:AO����i���N�������J�1�~`����C�`U��1�K/��>�N�)%��]��o�0|��%�������mu���Z6���,���\��'��,�sq
����[�m8=��?��"���'�	V��D��!�W�3N�o��tbo��\����r�M*����b��<�|���ar����r�m�py:��W��:1�1�v���k�l��~��s��O�K���<��������o��i�0���?���q�\��y1����u�=������o1�A_�������Q�s���qZ��S�P�������|ub5��Y���]6����W���|���Su�+�=���?����[�����<��ch�k���I��0���C�]��~O
�g��jR��k�
v�����V?@3�8�b�W�.��x��������,�y�kM�AC7��t���1L��E������l��t����\0fi�9�Asil�;nz�X�6q�N���M�qmH��Ec/��y��g��b�!�z�t���5=�4��=oo�ic���]�',�������;�"eQ��1l�d�������7�4�S�N=�(�x\<N-}\�������\�c��#��=���V?@}��G��}T���U=V<����kU<��e1���AC,z^".N�<���1������n�-����gN3�P���I�o����.���,/�������mq}�����j�p�����S��K�h^���!�b����m�U��i�{U��F\,������/��M���u�"���\�tu�#�	}��w��`���%F��������YC��69Y���E���A^����a��W���K�j4=.��c1�[��Z���c�n,�5��&c�eC�T��}�����s���E��*^�
u��)lK�i���t���e��n���d��i��b�{,{�"11m�;$�g�}v�0�kC�n�8�C|�����w��?�E/�Ie��;s��&���3X�.cX����q���[Gu�� ��{M�7�a�&1����0�����`yqg����!���]��~��mH�z��79������2��ub��6���;B��G��!�f��{U>�(�X�i�G�J5}<"0[���'u���V?�l�G�G\,rR/��{E,����X���7%��[��7�=��0;I���x�U��5��#G�J�lX���q��`��`yE����h�9z�h�j������6$����+b��8&��I�	V/���}tu�,]�t���8v�9>vz�U���D�s:�DUgW�/0R:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#����v[�����0Ky�[n�%w�b�c�������V+�.����,8/r,u�����!�s����hS�e���D;������e�M7���], eG����'6G��N�<��s�����;v,E��Y������<KaV.��������T��9���tK'"��I�&��o�1�H��Xt'9E��mE�UI9`��usIC�h#��:rN�:�3��p�'
U$�%s�.w�0��,�El�H�t�<�$dWe���t�����9�����ND0r��^W��[�O�������s���fv������>e�O����������l��N�8��
��S7��`ut"��K��&2�z��t}�3��M?D�0k��f�����m���;v,�m���J����y���e��n:�2��BM�X��6��^u�X�j�D#w���|l�E�X���r�-��|�<����D#�>]���#��l7�tS>>]�I.�n~
���D#���Q9=z4�b[���.�����p����Y��(����[C������iG�*���'/����1�)s�X�ie�x�y��,k������*L[��7�E��y�m���,�{�>o��yb�i�6�����������*�~�e/�*�(��:��������t�4��ei~�E���m�����*bX���\V,g��6�6����*���fM����N�^��P(CYL_��v�����s�^|���+�K�y�}����Z����-�n���ND�����HI�r������XZ��{UbcY�DD(�%���K4�
���W(��"�/�H�FQv��
�����~������g�Lu�/U�H��1��c��v�U(�������$�P���<��\E��f1�"�0m=f���Z���O����5���}O�	`�t"VjZ�`�������:����u��F|�mUh�w
��~�������n�N�>���U��M��X��.���E������}��v�Uh��_7�Ph��(k�w
����/�j��b������d�+�yby�������	i*���iw�*7G�c����.t������"G��n�������%�eK�����s-Ky��y��{�����n�tY��o���|g���_]�h�O�|a��UH��,�n1]�Wl��l�m�t;�/�;q�D�j{��*;���X��mm�'U���:�i�2k����q��`\�m�ei;v��;Un��j���~_�rN!����1�o.���d{�i�6�Y��X�X�E�B!�{�k�erNaV�)9�mrN�Ty�>9������s�.8�����>}���n�����v��F�o�]��0K:O�n��e�a���u�u�;}��~��
U�6�oL[��y��v:����L���ch{_�6}��N��$���!>+O_C_����\@����1>K��`V���V���y���/�\~y��>M��*?0�;�y��y1��N�������.O�4�,;����i��0��T}��=���!>+O_�O�y����`����w�J�&����T7�w���\q'�s�����+��/b9��9/�1�5�����[*�/�CX�?��]��O'w��wg�T,c�z���a���}�M����S�N�,;>�Z���5+����w�_{�i��L[�i��G�������j���k^�b���.$�0:�H���'m��G�����{^r����v��Z,����B���Q�j��
���_�����W�{�u����9�����fu��ib*���.;[�&=M9��v`h��sN���T�K�Y(Tu��"u-���c�A�9�9�ND0R�$M�=K��i��&������;YU-�4i�%�6!�7���i�t��8?�:��������.G�O��d�`gJ����-������sNM��*���K�&���"=������D�I�	`�t"��*�1jVq�'-#�{�"��i��*����	�:	�h��m
�������K;8�����e����;��w����)�	�������wOk�n��sN�����HR��V��c�,C!��P�D#��A��h����
����Q��	>C�&�J��v���������Z��������o������x*���>�T��L�K���aE��b]t�*r�}���M5y����s_�s`Y:��	��i?K[wo�����X�&
�E�&��0C4�G#~4�/�)���C��m_������H��	h��{��Kh�SE��sNml�&��N!��S�s*�NT�s�s2��`���<��������h�M�hf=��H��P$mb��][O2b��.�u� V$x���� ��EN����9������_TM�S�~�N!��;�����@OE��h�o�����~�[$u677k'vB�������|@t�(w��C�ig�s�t"���o��v�H���<I��?�h�����3�t���b��9w��S��<�:�4;-�O��|�,��S�����c��)������@�xCzG��g�PVN���#G���K�m�T�!h�q��o�����R��S$m��P����}_,��T�u<�����^�)����rI����{;��v��NUO����Y��sJ�V\L�I�	`�t"�(ocH��������S����v�`��P�={6�6�;��Y�4�
�E��v��}1M������p�r�r������g���V��U�C�vr�)�����^�
�{�Y�]rN����O'"������p��<���w����I�������[n��������9oL�b�o|�����|��!
�J��R�4_�����B��]�.iN)}
Q<I����!�T�s&��`d�����V�6dW5v�%�(�4�
��&k���u��>]�Y	�ET%�V�al��/����C��Hu�S�����\����T��s
G����K�e��Q�_ZEg���s�&��:������vR!U�U
�U"AQN<�����hL��Q���o����z�kHO�}���M�����/V�7u;���
M�����Z��sN���T���S�����>-'�o��i'�s�?���H����]���#\����n\�0
���9>�JP��������%���������p����N�1MZ���y��t;,{������t=b��=+>O���	R��n��'-k'������j]���j��9�����%�6��%����j�9�,:���w��������x�~�p
���l��������M8w�\>�?�.���u���J�zT}?U��)-��N����V]K��X�X�X�bhS��Y��y��/V�m��vJ�#���X��_�m��
�V��`'��by��Kl�im�U�[�
��A��(;�a[�l��s�s`u66'�F&'|��#` ���h��;FE�u�h�����-]�F����:�,gy�Y�aU���������H��&�;���ml��k�B��j���,;t�/.�m���i����+a���r����_��*���:�/����������BzM�������^t;E��N����H����a��e�j=���.�rNrN}Uu.�ID0"��Y}bQ�>�����h��k�s�S,cu���E�Q$
V�d����\4YR���0�}��W�������*���}bQ��sj�#�uI;�Ti��*�2�|�<M�]����s�^��5�������T��$�j�/�F�hL����[V*�)��,���Y�"9P,{Y�G|��}�m����\w�B��i�������m��2��/b��/���{�z�OU�����Fga���b����]��vZ��(7�cL�PH;��Sw�3�~,K�2.�(;������X�X�b���!��mLN���OY����#rq������<������q���^S���T���$"9��`�t"����FN'"9��`�t"����Fncsbkdcc����#���y�%�~���x��ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#���ND����n���N��!�����_TG�{��m���O�����-�fqX��j��g��C+;-��v�i�X�i��z�Zv�V���'�������8Y6V���F�71ky���PvU����3�x�F��U�����6X����\�d]qC|�J]�C+�����T���eb}^���*
�N+tYv���}q1Q+��2�2��U��<��gB�����f.F�X������s������*���N�O��_�D�T�
UC�#ML�w�!�[���!�2#����V��7��
��C,��w��w����}��B�e������a��a��a14���e�t�P������P��[V�a�ua=���>��r�N0�x��1y�9���N�#��.�;��;U��Wv�.�0���x�S�Ue�`�Q_V}��B�e��}O�Gu�0�����:�i=������������y�����o��C`u�b0�EO�Ue�0�d��W��2���"e7=1�1�2#����5t�].�P�n�������/gu�g�e�E�u%;�"qC�Xl�u��Eu�<C*{�s�tPoG���}].�	f�1��13����v�.�;)���8�E�q��&^D��3�����;�N���=4Y�����>;M�8��n,.�u��Et�<C+�j�E����{�������w�����_|��6������������bXTW��>)�S������m��w���Coy�������#��x���N���o�HV��b�n�U���O~������6����l���������.����1�gZ��~���Z��mR�����>�]}O;���4�4_����������y�d<��7�b,��,b"�K�Kc6���gZ�]�a��U�S�M�j�1o:_�} ������o����_���l��w~f�-��w�=��*N"���8���y�Rg����u���jy�i��������������+�Q������\�\;)�u{U��-����)�O�������T���yd�9mz���kRv:�X���x���7�����;��s���������=�P>��e�:�T���P��U��NS�^K�1O���/�ZU�/wV}��U����v"���K��������y�R��<��Nc�-��=�n�6�che�v_dH�FW��H���!���CX��>���[�����O~o�?v[���41��T�`1,Z�V�a,��� ���y����O7?�����g���T�YL�2]�%���a^���#qBX%-{^<�-�z������������K�������r��b�����2C:������m�E����i�0��Cy�Ye��/����{ps�-Wmn�y��W
�YL��6�K�e(OC��i��0�G?���x��6��k���}��]�C|����O[���&uZ��Z$��*���z��z���RvS]�O����/�a_���e����s����9q����8��C����6���ty���=+Wq���u�]�g����-C|��>]�a:����eq�^:}�e��L+���/
��R}�^�5���-��-
GUl�����iM�j�8o�I��d=�P�n*]����{���vM��������o���g���Oe�������g1ML����B����~���X3��zk>���[_���o_�=��_�6�/�b�YL��<T������m�����o�_U;u�T>������������Rg�����M+������e��k��}���y������g1ML����7�U�2C�������&u��m��v[>��������_������C-;���c���ct�c�:����,���e�c������41���yX����������y�@|V.?���������~.������;��x������g1ML��z������8i��[n�%�P���:�����X;���U/���y���C-����`���K���hN
�D~�=]^wT�=�\9�giM���;M���mo{[����%��'>��[->�����`k>V������~O����K���T_�>�$w��1�QR������O����qa^N{�e7QU��8q"���D��}�Se��w������6�r�S�oL�W���w�����(���'�Qi��wO|���?�?d��������jak�����~8�B��B��E���`�������o~s�����;�������_���wX�.�0m,��9���#���6$�������ZvH���;��/���;��������r��*����<1�Cw�o�*���vB}Y��{����>�|��G�y�G����O�������b�����=���*4MZ�$���u�U'��W��64]����<����ZvqL\�n��&���!y���&�����=�5���R����X����Jm�w����>���o�Fv��w������G�w��������������������C�K���T_��@��V)����wq���0�������a�i��/4���J;���[��X��?�P�#���{���w�6�r�T�oV\y���_�}��1I�U�Y�4Nn�'>�����=�cw����.$��V�&����~�VYTk�^����BA�����I����S��o���V�<���Eg$���XYw���q��](���i
�C-;��ucXB$g�����s����(�����c�?��,{P�g]��{}�6h�);D�q�
�1�c���������} '����X�>�?;~���/@�g�����I�uH�����9=�T%L�Zv�G��_m�[��U��R���p>�4��D��vz�>Oz�Z��4!��m^wD��E��:~5"?�����������s�y�����ok�r���r���p�_��o:��_*�3J��K��������r��n�!rJE~I�i}m��>!��6]�W��_�e7q����nP�x�D�Z��������������2���+5i���wm��^��6N��@C�u�<�{�yz�?��2��ye����e�����-�����^����B�����������W������M6�\�4Q�C�������}$�o�(�w����h���!#����z�:������=����g(��Y���������+
C�����#�sI]u.8�Zv(_�w�/Pw�{���(��?�����l���b����O�����'�7�l�E|n�T��l����FQ*�w14������1�U��)��v��uy������Dz��s���c��f~�����_�C�����:����mU��%�������^C�+3>�Wf1}�#��l[.k�:�QWy��</�/�����l����}�������c���#�Bq�[�hs]��m����w�cqL���c����r�6�8��g���yu�g	��KMW�K����S_^L]�zm��^�����(��]���r]��(����o�'���W���{��0�D�����~��;�M�W������?�U���+�&�B�'�������q (
�^|�����4qP��f��ev���u�+���i�Qf��
.��[_������&e�.5��/�w��I�[��}��>m*���u�N��������}�c�*����u�9�4]]�/�����,C-���_O�">�k�e��e������t���~.{�w���M}��QV��y�~���#M������,��.E\����r�vy����*wR�f�eOS��,�}�v~)�S~)��jr�Q��[��'�;�[�I�U>�����v�9F�u�Wf|C�u������Qw�RE���(��u��q\w�����6��w���'E]�kW�����!���8����z��e����R����S_��7h_���w|��2?��"��p�^0O��)��Qn�����g^�?��gI��S�N�c��ND���?�`�����Wq��|d�y��({,��a�
�|���$����:b�Ynb��:��'>�t����r�Uw���Q�4��=��d��W���<x�'����Gx��/mXi�$?��n�B;��eNvw������'E�Q6�
1��r�c�f-w|V>��c����������](o�����&��H���x�e��|W�}��;�E�Q6OQ_.��zg�e�SUg�/w���^�����LZ�<o�e�-�������Bm�=,��i�ci��DO���&�,5/9?���J���MC'�TO�8R�x��#4����g�G��yf����������2�t9b�k�Yb���gY���c$��m��MD�i���>���d�z������`�X��DyQ.�\�/o���]��~�o�k��Ic�s�&� $��VB}�<�e3Uu��rq�����[J��/�����Ss������^���X�7��c$g&�e+�eF�c�V�i��4����	@� ����
��p����NRf���(7�X��X�b��(�\^����4�U�~��~!�a��_�r���9�N��m�������<U�d��({@�����_>Q/����:!\T�`qQ�������������SB��2�eE��7�8�;��?VH���^zLl��2�^�\��e
��i�N����w\���{���i�XL��1�O'���U6��r�Z�uYv�eG]8��T_.����3q���lt�������e���qe�:���_eZ<,+����m�v�X���j,gU{R�m u��y���P��#���Mc �TO�Gou�#�s��9@q�I�1��4C����`��t������e��/+��^/��\'T�_Zn\����yb=��F�m^�����y�8���>����yb�b�8L��X�����=�X���.4�8t�����NE�E���(#�����;����)�J�}?�/�K�^T��j�U���.�>��3���b��V'�����4r����i�����P�j/c�t"b-~�������e��P���jH�Oj���A:=�G�_����������i�r��NQnzq������Q����r��������!��\n�nL���~7[.�[(������46��~9����[HO9Q��i�ER��>���,�K��G\��V4�����u���0~\P>���G���Oy}b�8n��L�����~b�����>��}���#����//���4�6�������|,b���R����B>6���e�.��i�7O:}Z��)/���6��4>�n�Un�Nqucy��r�c�u^���2�&�����|>k����1���|D�s���O�cF�)r$U��~U�J����(3-�I�M��S�����j�����SH���D���r�mG��_:o��Ub��������C�Q_������W�����O|���V�����cX�����(���wP�f��b���.s@���|d8��S_G|��>3�����sK�2��[
E]����~Z�����i}T>'�&�(�Ar�4��C���q�����|djh���;��L�g�������;��];����2)�?���=�}�������W���V���t�����v��2ND�
<.B��%��U�]g�*������N�Qfq���b=R��v!]�Y�����j�/Zv_<����=t�g�'>����5k�Zh���~����G8���o���.����~h������=��<���!���#�����>7���}t�Wd/~������>g�t��|<�R�����������"���+K����j�!-?�w���DY�#�t���~��O�aRi����(��\��Y���~�%���1��r6����Tv��:3~��~�]|_m������;?������w��s�:����^�}��^��S-�7��wS�SU��m��beU�X7V�l�.�D�l�����P����}��+�S�6�������]��/�n���#m�gm���&��Y���cI�s�"��O=����(�/u�-����k��Uu����&����?��d��_��>�_Z��/�����_��;�����������Y�5C:��c~)��������N[��/}�W~e���<'�Z�>Z%����t��kQM�]j���U��Jz��8����D�)��:5�K�������RvjZ��������"��:usK!�7��Wo���v�6�0/V���$V�n�.�D�n��y����]G��Wu��$�����Lv�k���D�V.v �~R�*w~��[�;k�H+�yO�)W�M+����|(���<m�?�c]���}#N4bh�U���uObb���y*R[��W��?����7��O�'�x����������#�6O7Mb�I�NSu���r�'��������xk�t Z�����[������,q�Tu��Vq"����x=�bu�q��[w�D�<}�v�@���N�Gb?�:�����hH��>}W��/d���'�U�g�m����W������/��z�/����Yuf���K������>x_�So��WI���~��>s�g�����m%�f)7��uFqS�������)������.M���b��e�������b]�l���|Y�.�i�\vZ.�;[o�K�S�v������_*K��{�9Bq>�9��N�cN�k���S��R%���R�/{m����9
�6)���\vyWI~i�b;��V~i��M��������.~���E~)����R�������X/����Wv�6��Q��N��A��\�G��$��ZE~):n���������e��3�������]��-�V��R�s����\�lQ��<}��ulY���}��}����hy��������-������;�-�|�?���|]���H��KW�����El�A�N�j��Uz���3[���)���w��'��Eb?,{Q^u��^87uJ1D9i�e��u�u��X�.�{�Q�kq�C��u�:�8�����u��1O�K������bL;4�w���2�ht����F_O,:�����/��}R��L?������}�g��5+�km��\X]�������)�����2��Y�_H��Y�����}����X����H��R�W�gc����e���9N��c���������m�}�7���IK��/O~)=��=�X$��91��AW��8���zl�*��������SC ��u�KC���&�Eg]w�X�>���=�����������������>��������O�;/W�����K�O~j��R}�G����,���&urK��vn)D}uBQ_��r��J��YukZ����TZ��Yb\t"�A��e�����T����qg�4)1�����e��yb����D!Uu2^�EQV�Ji�M�Mza7�2����;���Z�S�L���H��i��e�����u:_������rl��^����� �������b?�s�9O�?��qL-���#]�iq��j�ey���������<��<��2k��;�u���p����<O}9<u����/�k}w/���������X���E��R�������L��i��i7*I��t�n"��J��9I������z6��x?]�t~V'��$y�e�&�����k#������k�e���!����+��q=��q=e����t�5�6��������zm�_Z���}H������z���`Q}�/�.rLq�\�S�n~���y���/�{���=I���-�W�����vW_����:uf|_��������z��-�>uc���: �kbZ�*����]�sO�I'������lR�J�Kk��6�����{ZOK4�+�eN���C��
�1����^�e�.���(+�(���O���o�Qg���C���/���k���v���}��_&�������1��n;��p��W�c�M;�W\��M�g4�M���{A�LkR�w���C�t��2���z`�e�
B�������G��\]�Yu�������R_�����^���_���^} [�R��Y�L�.��{���^Nb��R��@���DF��|V}T���z����u��*oib:�oZ]^�&h�$
���x��DO�uA_�N�S��C���h#�������aqR�/��"T��*�����b]��"�����t��QgH���F~i=�n��\w,"�3��z������N�1��/�����<�����t?^�mv%���c�v��G}y����>�������>��rK�1/����z$�����������L����&��z4��������k��1j�}�a��h��c��c��k��#~�e���[������d��w�6OH��N����`��Zq'��-�V�������<����z]�m�r?�����?����$�CzAR�2'�������i9����8c��m����T|�E�f�k�1(^�~>/n���2�2���4�����
]^��d�w��9Y������-���8���=��Q_^H}�i��y��������_}u>�f�zm�[������^:{��IG��E��f�F=���W��/��B�M�����Y��|i=^w�t���n�oU�X5D}Os�1f�}��7���J���iz�1���u��8�rcX��<�C���o��*�!\suO~i=����r������^��5�ue���Ii�v��������0�?��c.~���z��/�//����t�[������_���X�R�6�-[�fYm�/�*�1>/���tr����{��SU/N�-&=F-�1:� �69�8�_�������!m�x�����I�T�����/���
����{$a�_�q\4�kl���8p�u�	Aq��������z������K��to����.��I�����������������~�X,��"��1_\��=�_��I�Ux�s���-�<�k]&a����;�}��|Kv������#.l��Y�,C��&��N�F�X��=��w��w����q��.����Wlm����f��|O�F;������/�i�}k���K/�^s��Zw/[�I�/��2�w��{���M�����s�nL�������4m�O��E��h�)�v�X�E4���������2��Z�4����u60��/�~P29��c�{�������_*���{,zmU\�U��%U�����
q��[1���k�����u+�K�_����Vd��R����uGS�8�n*�K���C~)��B��m+��.]��\��V���we��_�|���jt�_R_�V�^}�]��B�7>�����VniE���.��i�\�t��r���~���������.����u��?����|*�����/����� 5��
��B�<M\��/���U�n\�+��i�'p���e):%������N����G��$f����2�l�t�������\���y��n��&�8,5R.c�
1������T7������KJ��QW�eX���������>�����k���"�e<�y������yk���=�=��!��M�����W��2��=���5�;��{(�����E����,{���� ���G}Y�����2��;���;���yo�*�U�����2�����o��[�\hV]�l=��@X��d�:{V����R��eb{V��Z�4����>D���o��w���b���.4��:�=��:��=���b�P��!]�e�+B,{�����0��:�-,z<��\~���<��juc?4=n�u���tV���(��G?������:_�����o��o�_Q6�8\�X4�x�~6����e/c�w��Kk��T_����&�����;��������������,[g����g���Ll����P��&���>D�U��'����k';d�b�����y��({��b.�+���q�\T��/�q0����uQf�'N��U^�B���m��2�;p�l�D�s���*c2r0��:�����u�����!N6��Wf{��_���C���i�eE�L��8L�;�9gH�.��5�����'W�9�h��N(.2)s�l�Q_63�:m�e���&��>�)�������/&u��3����e������(��ud���j�%M�����Y��]�E�����U���*�|M�!�!�_j�����w9M���<<�A����"���T������/�q/�X�<�\�2�&tK~���;b���R�K���+�"�c�2-�_�nqX���Z��c�tZ:"������Z����e~�����������r�������,_r�k��nb�eO���db�t"b-����G����W�'�*�eF�c����r���vz"'um&N��7.��t���_������_�����U�����49:K'�QuA�xM�]��).����k��k�W��2�l�Bv%��u�������&�m�!��Z���=�b�#=OSW�p�+���?��hQ�e3�������V�����l��t'������}�5��"M���������*����QG��z�DeW	��E�OU�X5�����*=?��j���:�����S��c����z���S��j;���8(�����A~i��_w�2�5��t��]����W_�}�W�o�\�fu����(��\��\��WUv9���y������Q_�����)�������f���'���z������yb~����m���=���1<:�6��������W���M����i��W\�U���2�	l��'=!^T�?N���8������MN�SiYM��(C��g1����}/���t{_x���W����N����U��7��4��4��T/z���k�m����z����d���a;M��c[WT�F�*1���t�u���w���m����u� �����E����e2��ryC����Y��������9�����si�j[���������L���������".�}=�S��"!^N�e��^.���m'������mu�e�e�&�5�!�_jn��G��m���!���\������:�*��e5���En)�e���������#U7��x���^���fO{������u�(��r�<fs�����Gi]������/���r9��m�~O����Fni=�}��'��{��1M�+��W�i���������d<t"b�~�/=7����n� p��Q������<7fi����|"�*q�5O���A&�����eO
��:���7�����;��~,���7��Nv:M�q�������E�F!��P���������.�9q�:��x=���]�.<����I��2����.�<C��X�:q�c[[T�r���*�4��e�e/�U��4��?9����/�e|��'�<��f��w�P�^�{���������q{v�oh���um�T��l����������ozF��Yb�,���,��i;�41_�\!�wY[D�S�%����QG��X�y�Bz5kY�Zv*������������+=7N�=�yt-�w�q�JLS�k���9��-�g���P�_,��Nw��.6���EDO@��n�!;o^�����r��������b��~Gi]������_Z9�es]�;a�e/�=�>S6�|.�ZniR��R}r�������j��tJ�R�K'"�������?�a�.o�����'1���nsQF�������2�wZn�Bl�8�.��O�6��9AO����Q����,�1���q�n�eO��o�U�g���7l����TR��*��������\�e�Ez�]��r����1_���y�(!.4f]�g���$O}�]vY�����[O*��gy�x�����>�����tc���S>~�]����Y����2���Gu�
za�}��l���J�sZcf�m��&e��E-�����K��h���=�;����y�����w�����y�T����}&lv��2�8������3&eQO�'M���aG�D�n��q��:���b<T������80��n�,C-{���U}�n~�@�����
���2?�K�9�RU��*�L�����$>/�M;���Yw�M�-f�U�'�lc��_�6�������L��^���g�_~y�N���AR~�<�������o�����r��+"����f����3g]�F��>���t���&e�-�����e���%��b���}��a�=K��*�mc3�eHU�@��D�+��������'��.���y�w}�8��1���*U�f�lz���6-7�#~�Z>��z$S���:e����\~����Mz@��=���gqKO��.,��c�@�\n���N�b���C���������3�*6j��������|���o�K��"����J���&"���^����b�i�5��c�����������uO��$���E�;41�8M����w~Q�,��>=���~GU��+�t������d��yE4^>�'������	�es������V��g��s��~����(c���ns�_rU�M�1�����������ho���cZ;J����������M���^(�g�����f�e�����GU�Q�����.;�N�/���Y����Q�u��s|�w�QN�j.�-;�������G�^�9N�k��uL����:�\��hN~�BC���_]\w�Q7���av�w������0������}����!�s�����|G���/��������|O���v
������PwN����E��oj����j2/�Qu�_��MX��>���[^���O~�?v[���41���coR���V�P/���<�yy�*�����3D��|�:�H�1,�-�!>Ku��2����y������������������&����w^��:`��*�
������t��{�������K��K��{��*��,��iY�8ln�co�S���i���(_���������[���|����j��b����U�P��x��6�/�
����{��;�I>���7�������������������&�eqU�x�Y$�f
�,�w��yS]�C-;����c �U������\�,;5���'�����������q.�(�<��c�"e����(�*],Gy:����/�W���������,�w����u�]�g����-C|��8q�\�coL_��N�������/�LU�/gb��,{�����h��[Z��}�n�4��Y����E�N�8o��za�e���q*�O
�g��:���{7�������7lk��xo���7�`Xu`��\�[�79�3�g�A(�orrP��`Z���I�u�a��������G?�����������[C��{;Iy_�a��Yh{���11k������O~r�C���{����!��=���~��8�{�]F�s��f�y�4C-��w��lj���&���67?�/����hM�/�/�j��e�������w�����y�����g����hG��]G�R:�2�,uc��������U�Zv�O��{`���q1�A_���k'���c������>Kz|�u��{�P�.(����=�C��`�n�a���Cy=�)O�����vz~�*���'�����cu���z������{��;����?�������[��}�7�8�aqX�`����nZW���NU�O�K�e������h��[�^����&���a����1����n��4��Zv(���Sy_yj�?��X�r�������0Oz�%$qO���1T����0���c�V�<U'Q�,�rT��d9�2�K��o�y����"����0�u���c���3����A�����<��C�8R_���{��1��0T�}�n�T��2CE�qC|��8���Z������R/�S�;.�>��r�N���O����DyM������u|�._���V~1o��_L_UV��X5�����<��u�e-���t����(�+����}T����1�+�e�by�XvQn��#hS�O����[v����X,S�3�����u|C�oy���<��C��.�0�]qX,S�3�������\����F�s��#���y�%�~������FJ'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"����FN'"9��`�t"���������z���S<k%�0�D#���ND0r:��mlN���y��ND0r:���D#���ND0r:���D#���ND0r:���D#���ND0r:���D#��9������F>�z�O��N�8���X�e���-��r���g������[o������n��;�9r$����w`�������������Wb��y%9%�~���&���I�I���X��`5���Pt��)��VlW�/�V�'��$Uc�(���n��`l"�h^)�Tt��������;��ND��H��yG�H��H0>�W�\���3��D������$M�t$�[n������D���p]u *�H0��9y�d��=:��ND��E���D��H���|���X�"g��t���v�.=)I�����[:�9��tkt����;�mnn�>�8q"����I������n�);}���s�Z�W������@?D�w����a�����������li\5�)�1@w<�v��[\��9s��E�n�������:N�<��04unNW���������;:�U�nqq��H����M��s����9]<mhV^)>��D��O<�9��$:`D���������x���e����v\f�M�2�e��;�s���Nq���h���R{���w�������9r�H>�:�D����tXT��8z�hv������^|'��y���ty�C|>O�|��N�y�C�.�i	�bH�+^��L�cy�M�I�����J��~G�4o��H�4Q��q��R��J�-W�J�8u�T>V���c��lnL�^:�\�HH���1O���NI�:�rZ�����i*�a�I��o�=�B^�]�J�E���������s[C�`��M�X/��z,�$
1�:�	}S7��=Ug����������#$x�!=E�������Wj���t��(�'Ndg����X�u���&G���z�Ibb�(����M�gt���������O��)<���������g�������Wj��Rw�������O'�j;�w6k��!jzw�i�]��n
CO��y��x��y���+u����O��ND=s�m����L��n���O�c������zw�:�}���|h��R�����!�sccc���Dg���_���u":z��V#tC4j�Z,]��8w�\�������C�r�9��]��w3�{������������t'N�����SCU�h�aO��WZ?y����b��Pw9O�:���O"��&��"��2n�����m�^��+>g1�6
u��6�{�����qe��u��G^����y�X��m	@�t"���o�=��N#z|.�S�HDT��m�����!m��R�;���"AYgy#	�G^i5����7�H��Q��Q���P(��<�B�qG�H�9r$��:w��r�|/1���R������������/���R���s���|l���N��_�
:�D��B���)x���m���[�N~M�X���"�3���>�WZ
y�n�z��[�������4����'�$}����m[t����_�$��������n���q�>y���+-.:5���������NDr���|��c���c;���o�E�i���E�+�����s�'��v�+-G^i�b=t$��ND=��'���"Z���<]%����U���A^�{�J�����8E�hss��!^�y���D�]'��Az��o� ]����6$����KC�Nt `�"P�#Zd8s�L^j?�+-N^iq���8q����x?�H�DG��t�O"bGX��n��~{>6���'����FC�9u��*���t v
y��D�NG���)����*:�������y"�t�����t�F�>���Ju;�t�M����D�r���|l��<]	`Ht".����3g�W0_����={6�-:
��_��
�������h@}d���?����#G���N�>�mnn�>�8q"��S�Q��D�_�J�7��RtL���[n�"����F>}�QO;v,�n�G��<y2��o�1�H��I��;wn-��m�J�4��Rt��BG��b�!�JEni�O�#��z�����#[����LX4	�'�]D�g��@��:V@�+
��Ju���O���I��ND=Q�cF���o�9cQ7�tS>Vm�$\������O
�����X�D�O���z@^��vB^�J�'X��LU��[4�Q��K*�h�����ID,�I2j(��:	�&�]L�M1��Ct��DP���yb�<q�D�
X7y�~c^��Nk1���5��D�#�N���f�����O4��y�Lu�Qu����ZT��3M�p1�<������U7I�I�e���#`6y�~k^������tu�+>��N�
���Q��]��$TB$r�'�D�{�Y�n�{�"�3/9�E"�ku��:I�:O�	u����u�'�r���k�y�#G��c��Z�n�=��u��:��hN'���{���>&��L�;v,�������
Da6=��7�Da��Da�
(��}��RD��y�>	e��aX���
b�D=�����>v���wb���{v�+��F�.r�i��+EQ��s�u��.?o������]��C�hb��7�>_�_�1�G��]��{!��H���`d�P���
�M�4]�r�p���u8���)MP�N6d�3�dCQc��6������Bw!��J��)W:������Y���P"������"��]���-��m6�2z\�o����0����Z��t]WF@��J��%W���"Q�q|�x%�	{4�yFx0T0Pkp��A������~_F@r����+]j]/����Q"����|���
�����i����@\_�!@��� ���z��J��5Wzt]/�����5Q"���������!�vy���v�����1�o�&}��K����x<���\i�j��>����S�@v�S|3KjQY.��������f��5]�5������B��m��y������!�O�P'��82�J��y����<���P%���a��+@^JD���RF��w�
��X��Nc���z�.G�w���\	�K�hB"��uk�k}ww��"W��V~R�����>�=A���\	8S"���m��c����x,G�]�>;�P���Q���e�����o�#�6��~C����e@6r% �N?�1;����G�>r%�w"���fSF�8e@M�J@JD�X,��jU��+���>r%�%�	z{{{z����^r%�^JDA�v�-G����� W�1;�P�L�n�k��u9��y��y�X,�o�D���Qe"����5�~�/�?�V��m��c��G�\R"�����@RJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD��$�D�)@rJD����4�7Em���AIEND�B`�
v28-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v28-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From f4299a4080a29e6ddf91ccb25eb5c96900aaf3c5 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v28 1/7] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +-
 src/backend/optimizer/path/equivclass.c | 464 ++++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c   |  47 ++-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  89 +++++
 src/include/nodes/pathnodes.h           |  61 ++++
 src/include/optimizer/pathnode.h        |  18 +
 src/include/optimizer/paths.h           |  12 +-
 src/tools/pgindent/typedefs.list        |   1 +
 11 files changed, 698 insertions(+), 114 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c0810fbd7c8..e68c116164c 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7828,13 +7828,35 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	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);
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * If we need to see child EquivalenceMembers, we access them via
+	 * EquivalenceChildMemberIterator during the iteration.
+	 */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/*
+		 * If child EquivalenceMembers may match the request, we add and
+		 * iterate over them by calling iterate_child_rel_equivalences().
+		 */
+		if (unlikely(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
@@ -7846,6 +7868,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7899,9 +7922,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index fae137dd825..25ce4dd2242 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -68,6 +72,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -373,7 +382,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -390,7 +399,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -422,9 +431,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +519,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +539,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +561,29 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -598,6 +632,17 @@ 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.
@@ -616,7 +661,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,16 +677,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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.
+			 * 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().
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -653,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -690,7 +749,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -760,19 +819,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	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;
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * If we need to see child EquivalenceMembers, we access them via
+	 * EquivalenceChildMemberIterator during the iteration.
+	 */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -782,6 +857,14 @@ find_ec_member_matching_expr(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 (unlikely(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.
 		 */
@@ -799,6 +882,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +925,9 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -854,9 +940,23 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_WINDOWFUNCS |
 							   PVC_INCLUDE_PLACEHOLDERS);
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * 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);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -867,6 +967,14 @@ 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 (unlikely(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.
 		 */
@@ -900,6 +1008,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -938,7 +1047,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1563,7 +1672,19 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	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
@@ -1573,10 +1694,20 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/*
+		 * If child EquivalenceMembers may match the request, we add and
+		 * iterate over them by calling iterate_child_rel_equivalences().
+		 */
+		if (unlikely(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
@@ -1593,6 +1724,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1610,6 +1742,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1684,6 +1817,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1691,7 +1825,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2404,6 +2538,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2525,8 +2660,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2597,8 +2732,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2695,6 +2830,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2707,7 +2843,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,15 +2855,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2741,8 +2870,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2758,6 +2887,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2787,9 +2917,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
@@ -2818,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2839,7 +2981,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2852,15 +2993,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2869,8 +3004,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2885,6 +3020,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2915,9 +3051,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2986,6 +3134,161 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * iterate_child_rel_equivalences
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the iterator.
+ *
+ * The transformation is done in add_transformed_child_version().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	int			i;
+	Relids		matching_relids;
+
+	/* 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. This information is stored in indexes named em_child_relids
+	 * and em_child_joinrel_relids.
+	 *
+	 * This function iterates over the child EquivalenceMembers that reference
+	 * the given 'child_relids'. To do this, we first intersect 'child_relids'
+	 * with these indexes. The result contains Relids of RelOptInfos that have
+	 * child EquivalenceMembers we want to retrieve. Then we get the child
+	 * members from the RelOptInfos using add_transformed_child_version().
+	 *
+	 * We need to do these steps for each of the two indexes.
+	 */
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		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);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3019,7 +3322,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids,
+				top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -3028,6 +3332,16 @@ 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);
@@ -3040,6 +3354,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3060,16 +3375,25 @@ 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.
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3084,8 +3408,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3303,8 +3627,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5d102a0d371..4a42752486e 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3726,12 +3726,23 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
@@ -3743,7 +3754,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3762,16 +3774,36 @@ match_pathkeys_to_index(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.
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (!member->em_is_child &&
+				!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
@@ -3800,6 +3832,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 03c2ac36e05..b8f9c5f0e93 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 178c572b021..b5f8d5fad0a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1285,7 +1289,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1329,7 +1333,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1470,7 +1474,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1501,7 +1505,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1981,7 +1985,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2195,7 +2199,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2219,7 +2223,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2288,7 +2292,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4550,7 +4554,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4559,7 +4564,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4584,7 +4590,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6234,7 +6240,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6301,7 +6307,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6329,7 +6335,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6345,7 +6351,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6416,7 +6422,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6425,7 +6432,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6451,8 +6458,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6461,7 +6469,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6820,7 +6828,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6883,7 +6892,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index c5b906a9d43..3c2198ea5bd 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f96573eb5d6..ce494364173 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +199,25 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +271,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +667,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +785,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +973,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1536,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1530,6 +1577,48 @@ 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 == 0)
+			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 add0f9e45fc..a8cc7b19ff2 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -248,6 +248,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -957,6 +965,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1374,6 +1393,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1446,10 +1471,46 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1035e6560c1..5e79cf1f63b 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -332,6 +332,24 @@ 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) \
+	(likely((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 54869d44013..50812e3a5d8 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -179,6 +180,15 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 2d4c870423a..32e4be0f47f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -683,6 +683,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
 EquivalenceMember
 ErrorContextCallback
-- 
2.45.2.windows.1

v28-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v28-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From 10c482062464b565d3438d293180e5a852007571 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v28 2/7] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 516 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/optimizer/paths.h             |  21 +-
 13 files changed, 593 insertions(+), 69 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9827cf16be4..bd6a79b57a7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -573,6 +573,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index be5f19dd7f6..70864fc1b29 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,6 +434,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index c36687aa4df..1ec10212eb8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 25ce4dd2242..474231499e6 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -319,7 +323,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -330,6 +333,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -350,8 +355,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -363,10 +370,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -377,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -394,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -411,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -420,8 +430,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -443,6 +453,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -584,6 +596,167 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -729,8 +902,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1134,7 +1307,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1227,6 +1400,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1236,9 +1410,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1296,9 +1470,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1308,7 +1482,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1374,7 +1549,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1431,11 +1606,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1476,11 +1652,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1870,12 +2046,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1887,12 +2067,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1954,10 +2134,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1968,9 +2149,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1981,9 +2165,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2056,7 +2244,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2782,16 +2970,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3517,7 +3708,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3605,7 +3796,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3762,3 +3953,240 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely eclass_source_indexes and
+ *		eclass_derive_indexes. If you modify these indexes, you should check
+ *		them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * eclass_source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * eclass_derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 5bc16c4bfc7..4449b8a22ee 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -36,9 +36,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -445,7 +446,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -474,7 +475,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -547,19 +548,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -584,14 +594,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -607,9 +617,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -636,11 +648,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -648,7 +661,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b665a7762ec..ca8af0bd4b2 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -663,6 +663,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 2ebd938f6bd..b86e0816d70 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1153,6 +1153,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 45e8b74f944..db88047f8b7 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -495,6 +495,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5bd..b5d3984219e 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,6 +492,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 9e1458401c2..e15ef139dec 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,9 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -394,6 +397,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0f9462493e3..d28e2034d09 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,6 +1247,12 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a8cc7b19ff2..313d907af94 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -321,6 +321,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1399,6 +1405,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1417,8 +1441,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -2659,7 +2685,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2777,6 +2808,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 50812e3a5d8..a24a5801c55 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -165,7 +167,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +207,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v28-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchapplication/octet-stream; name=v28-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchDownload
From dcb64602b2b3d33f466d15d30b65f379264833d3 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 19 Jan 2024 11:43:29 +0900
Subject: [PATCH v28 3/7] Move EquivalenceClass indexes to PlannerInfo

In the previous commit, the indexes, namely ec_[source|derive]_indexes,
were in RestrictInfo. This was a workaround because RelOptInfo was the
best place but some RelOptInfos can be NULL and we cannot store indexes
for them.

This commit introduces a new struct, EquivalenceClassIndexes. This
struct exists in PlannerInfo and holds our indexes. This change prevents
a serialization problem with RestirctInfo in the previous commit.
---
 src/backend/nodes/outfuncs.c            |  2 -
 src/backend/nodes/readfuncs.c           |  2 -
 src/backend/optimizer/path/equivclass.c | 83 ++++++++++++-------------
 src/backend/optimizer/util/inherit.c    |  7 ---
 src/backend/optimizer/util/relnode.c    |  6 ++
 src/include/nodes/parsenodes.h          |  6 --
 src/include/nodes/pathnodes.h           | 26 ++++++++
 src/tools/pgindent/typedefs.list        |  1 +
 8 files changed, 74 insertions(+), 59 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bd6a79b57a7..8a635a2a12b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -573,8 +573,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 70864fc1b29..be5f19dd7f6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,8 +434,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 474231499e6..96869ea80e8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -613,10 +613,10 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
 	}
 }
 
@@ -637,10 +637,10 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
 	}
 }
 
@@ -679,22 +679,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(removing_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_del_member(rte->eclass_source_indexes,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -708,22 +708,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(adding_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_sources_index,
-								  rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_add_member(rte->eclass_source_indexes,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_derives_index,
-								  rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_add_member(rte->eclass_derive_indexes,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -738,14 +738,14 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(common_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
+								 index->source_indexes));
 		if (rinfo->eq_derives_index != -1)
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
+								 index->derive_indexes));
 	}
 	bms_free(common_relids);
 #endif
@@ -3970,9 +3970,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4008,7 +4008,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4017,12 +4017,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 index->source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
 		}
 	}
 
@@ -4059,9 +4059,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4097,7 +4097,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4106,12 +4106,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 index->derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
 		}
 	}
 
@@ -4134,9 +4134,8 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 /*
  * verify_eclass_indexes
  *		Verify that there are no missing references between RestrictInfos and
- *		EquivalenceMember's indexes, namely eclass_source_indexes and
- *		eclass_derive_indexes. If you modify these indexes, you should check
- *		them with this function.
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
  */
 void
 verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
@@ -4145,7 +4144,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 
 	/*
 	 * All RestrictInfos in root->eq_sources must have references to
-	 * eclass_source_indexes.
+	 * source_indexes.
 	 */
 	foreach(lc, root->eq_sources)
 	{
@@ -4162,13 +4161,13 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
 		}
 	}
 
 	/*
 	 * All RestrictInfos in root->eq_derives must have references to
-	 * eclass_derive_indexes.
+	 * derive_indexes.
 	 */
 	foreach(lc, root->eq_derives)
 	{
@@ -4185,7 +4184,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
 		}
 	}
 }
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index b5d3984219e..3c2198ea5bd 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,13 +492,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ce494364173..ca1d7e91bff 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -218,6 +221,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->top_parent_relid_array = NULL;
 	}
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d28e2034d09..0f9462493e3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,12 +1247,6 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 313d907af94..c068dc1b6c6 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -192,6 +192,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -248,6 +250,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
@@ -1537,6 +1546,23 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
+ * struct exists for each relation and holds the corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 32e4be0f47f..1d9e73b8386 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -685,6 +685,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v28-0004-Rename-add_eq_member-to-add_parent_eq_member.patchapplication/octet-stream; name=v28-0004-Rename-add_eq_member-to-add_parent_eq_member.patchDownload
From a4481ad3f3d4b41c80b39721ebe1b96e2946e405 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 21 Aug 2024 13:54:59 +0900
Subject: [PATCH v28 4/7] Rename add_eq_member() to add_parent_eq_member()

After our changes, add_eq_member() no longer creates and adds child
EquivalenceMembers. This commit renames it to add_parent_eq_member() to
clarify that it only creates parent members, and that we need to use
make_eq_member() to handle child EquivalenceMembers.
---
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 96869ea80e8..e30d931f19c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,10 +38,10 @@ static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
 										 JoinDomain *jdomain,
 										 EquivalenceMember *parent,
 										 Oid datatype);
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
@@ -389,8 +389,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -407,8 +407,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -440,10 +440,10 @@ 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,
-							jdomain, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -578,7 +578,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 }
 
 /*
- * add_eq_member - build a new parent EquivalenceMember and add it to an EC
+ * 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
@@ -586,8 +586,8 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * add_child_join_rel_equivalences() to see how to add child members.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, Oid datatype)
+add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+					 JoinDomain *jdomain, Oid datatype)
 {
 	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
 										   NULL, datatype);
@@ -921,14 +921,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -3305,12 +3305,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

v28-0005-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v28-0005-Resolve-conflict-with-commit-66c0185.patchDownload
From 413fa3487560a3f15bfdf0062169856cb0892fb2 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v28 5/7] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 51 ++++++++++++++++++++++---
 src/backend/optimizer/util/inherit.c    |  8 +++-
 src/backend/optimizer/util/relnode.c    | 10 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e30d931f19c..022f20020af 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3288,7 +3288,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3305,12 +3307,49 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		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
+		 * add_transformed_child_version() 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
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5bd..7b2390687e0 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ca1d7e91bff..fcd57c1f3e9 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
@@ -1608,7 +1612,7 @@ find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
 	{
 		int			top_parent_relid = (int) top_parent_relid_array[i];
 
-		if (top_parent_relid == 0)
+		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;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c068dc1b6c6..9c2d284c079 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -260,7 +260,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v28-0006-Don-t-use-likely-in-find_relids_top_parents.patchapplication/octet-stream; name=v28-0006-Don-t-use-likely-in-find_relids_top_parents.patchDownload
From 862963ef73ee580aa84951e7b515a31d6708dcf4 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 29 Nov 2024 14:49:42 +0900
Subject: [PATCH v28 6/7] Don't use likely in find_relids_top_parents

---
 src/include/optimizer/pathnode.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 5e79cf1f63b..cf21ba29fe9 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -346,7 +346,7 @@ extern Relids find_childrel_parents(PlannerInfo *root, RelOptInfo *rel);
  * root->top_parent_relid_array is NULL.
  */
 #define find_relids_top_parents(root, relids) \
-	(likely((root)->top_parent_relid_array == NULL) \
+	((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);
 
-- 
2.45.2.windows.1

v28-0007-Don-t-use-unlikely-top_parent-NULL.patchapplication/octet-stream; name=v28-0007-Don-t-use-unlikely-top_parent-NULL.patchDownload
From 68c14b66f15f544223cca11f51869e79cfb022d1 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 29 Nov 2024 14:49:43 +0900
Subject: [PATCH v28 7/7] Don't use unlikely(top_parent != NULL)

---
 contrib/postgres_fdw/postgres_fdw.c     |  2 +-
 src/backend/optimizer/path/equivclass.c | 10 +++++-----
 src/backend/optimizer/path/indxpath.c   |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e68c116164c..030fe741030 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7854,7 +7854,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+		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);
 
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 022f20020af..86057538c5c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -861,7 +861,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 			 * If child EquivalenceMembers may match the request, we add and
 			 * iterate over them by calling iterate_child_rel_equivalences().
 			 */
-			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+			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);
 
@@ -1034,7 +1034,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+		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);
 
@@ -1144,7 +1144,7 @@ find_computable_ec_member(PlannerInfo *root,
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+		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);
 
@@ -1881,7 +1881,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+		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);
 
@@ -3616,7 +3616,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			 * If child EquivalenceMembers may match the request, we add and
 			 * iterate over them by calling iterate_child_rel_equivalences().
 			 */
-			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+			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) &&
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4a42752486e..9b7d01a85d8 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3787,7 +3787,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 			 * If child EquivalenceMembers may match the request, we add and
 			 * iterate over them by calling iterate_child_rel_equivalences().
 			 */
-			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+			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);
-- 
2.45.2.windows.1

#91Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Yuya Watari (#90)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, Dec 2, 2024 at 2:22 PM Yuya Watari <watari.yuya@gmail.com> wrote:

4. Discussion

First of all, tables 1, 2 and the figure attached to this email show
that likely and unlikely do not have the effect I expected. Rather,
tables 3, 4, 5 and 6 imply that they can have a negative effect on
queries A and B. So it is better to remove these likely and unlikely.

For the design change, the benchmark results show that it may cause
some regression, especially for smaller sizes. However, Figure 1 also
shows that the regression is much smaller than its variance. This
design change is intended to improve code maintainability. The
regression is small enough that I think these results are acceptable.
What do you think about this?

[1] /messages/by-id/CAJ2pMkZk-Nr=yCKrGfGLu35gK-D179QPyxaqtJMUkO86y1NmSA@mail.gmail.com
[2] /messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com

Hi Yuya,
For one of the earlier versions, I had reported a large memory
consumption in all cases and increase in planning time for Assert
enabled builds. How does the latest version perform in those aspects?

--
Best Wishes,
Ashutosh Bapat

#92Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Ashutosh Bapat (#91)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On 2024-Dec-03, Ashutosh Bapat wrote:

For one of the earlier versions, I had reported a large memory
consumption in all cases and increase in planning time for Assert
enabled builds. How does the latest version perform in those aspects?

I don't think planning time in assert-enabled builds is something we
should worry about, at all. Planning time in production builds is the
important one.

--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"Learn about compilers. Then everything looks like either a compiler or
a database, and now you have two problems but one of them is fun."
https://twitter.com/thingskatedid/status/1456027786158776329

#93Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Alvaro Herrera (#92)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, Dec 3, 2024 at 4:08 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

Hello,

On 2024-Dec-03, Ashutosh Bapat wrote:

For one of the earlier versions, I had reported a large memory
consumption in all cases and increase in planning time for Assert
enabled builds. How does the latest version perform in those aspects?

I don't think planning time in assert-enabled builds is something we
should worry about, at all. Planning time in production builds is the
important one.

This was discussed earlier. See a few emails from [1]/messages/by-id/CAJ2pMkZrFS8EfvZpkw9CP0iqWk=EaAxzaKWS7dW+FTtqkUOWxA@mail.gmail.com going backwards.
The degradation was Nx, if I am reading those emails right. That means
somebody who is working with a large number of partitions has to spend
Nx time in running their tests. Given that the planning time with
thousands of partitions is already in seconds, slowing that further
down, even in an assert build is slowing development down further. My
suggestion of using OPTIMIZER_DEBUG will help us keep the sanity
checks and also not slow down development.

[1]: /messages/by-id/CAJ2pMkZrFS8EfvZpkw9CP0iqWk=EaAxzaKWS7dW+FTtqkUOWxA@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

#94Yuya Watari
watari.yuya@gmail.com
In reply to: Ashutosh Bapat (#93)
2 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Ashutosh and Alvaro,

I appreciate you replying to the email.

On Tue, Dec 3, 2024 at 7:38 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

I don't think planning time in assert-enabled builds is something we
should worry about, at all. Planning time in production builds is the
important one.

Thank you for your reply. Making debug builds too slow is not good for
developers, so I'd like to see how these patches behave with
assert-enabled builds.

On Tue, Dec 3, 2024 at 1:12 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

Hi Yuya,
For one of the earlier versions, I had reported a large memory
consumption in all cases and increase in planning time for Assert
enabled builds. How does the latest version perform in those aspects?

1. Experimental results

I ran experiments to measure memory consumption during planning. These
are done with the release build. In the experiments, I used the
rebased version of your patch [1]/messages/by-id/CAExHW5stmOUobE55pMt83r8UxvfCph+Pvo5dNpdrVCsBgXEzDQ@mail.gmail.com, which is attached to this email.

Table 1: Memory consumption when planning query A (without
partition-wise join (PWJ), MiB)
---------------------------------
n | Master | v23 | v28
---------------------------------
1 | 0.132 | 0.137 | 0.137
2 | 0.158 | 0.166 | 0.166
4 | 0.220 | 0.234 | 0.235
8 | 0.347 | 0.375 | 0.375
16 | 0.596 | 0.652 | 0.653
32 | 1.104 | 1.221 | 1.223
64 | 2.126 | 2.392 | 2.396
128 | 4.245 | 4.917 | 4.925
256 | 8.742 | 10.651 | 10.663
384 | 13.603 | 17.159 | 17.176
512 | 18.758 | 24.827 | 24.850
640 | 23.924 | 32.223 | 32.253
768 | 30.050 | 41.843 | 41.879
896 | 36.224 | 51.937 | 51.978
1024 | 42.923 | 64.058 | 64.105
---------------------------------

Table 2: Memory consumption when planning query A (with PWJ, MiB)
------------------------------------
n | Master | v23 | v28
------------------------------------
1 | 0.190 | 0.194 | 0.195
2 | 0.276 | 0.284 | 0.284
4 | 0.461 | 0.475 | 0.475
8 | 0.844 | 0.871 | 0.871
16 | 1.584 | 1.640 | 1.641
32 | 3.085 | 3.202 | 3.204
64 | 6.099 | 6.365 | 6.369
128 | 12.261 | 12.934 | 12.941
256 | 25.061 | 26.970 | 26.982
384 | 38.542 | 42.098 | 42.116
512 | 52.541 | 58.610 | 58.633
640 | 66.579 | 74.878 | 74.908
768 | 82.421 | 94.214 | 94.250
896 | 98.483 | 114.196 | 114.237
1024 | 115.074 | 136.208 | 136.255
------------------------------------

Table 3: Memory consumption when planning query B (without PWJ, MiB)
----------------------------------
n | Master | v23 | v28
----------------------------------
1 | 16.019 | 16.404 | 16.404
2 | 15.288 | 15.708 | 15.708
4 | 15.674 | 16.360 | 16.360
8 | 16.554 | 17.784 | 17.786
16 | 18.221 | 19.954 | 19.958
32 | 21.630 | 25.609 | 25.617
64 | 28.913 | 39.419 | 39.427
128 | 45.331 | 77.015 | 77.030
256 | 86.127 | 192.884 | 192.916
----------------------------------

Table 4: Memory consumption when planning query B (with PWJ, MiB)
--------------------------------------
n | Master | v23 | v28
--------------------------------------
1 | 33.623 | 34.008 | 34.008
2 | 50.285 | 50.705 | 50.705
4 | 85.562 | 86.247 | 86.247
8 | 156.465 | 157.695 | 157.697
16 | 298.692 | 300.424 | 300.428
32 | 585.713 | 589.692 | 589.699
64 | 1169.396 | 1179.901 | 1179.909
128 | 2375.592 | 2407.275 | 2407.291
256 | 4942.295 | 5049.053 | 5049.084
--------------------------------------

Next, I measured the planning times using the debug build with
assertions. In this experiment, I set CFLAGS to "-O0" and also used
the attached patch that removes assertions in Bitmapset-based indexes.

Table 5: Planning time of query A (debug build, ms)
-----------------------------------------
n | Master | v28 | v28 w/ patch
-----------------------------------------
1 | 0.648 | 0.664 | 0.665
2 | 0.788 | 0.810 | 0.800
4 | 0.891 | 0.936 | 0.931
8 | 1.202 | 1.301 | 1.268
16 | 1.973 | 2.145 | 2.042
32 | 3.668 | 4.000 | 3.638
64 | 8.093 | 8.597 | 7.167
128 | 20.015 | 19.641 | 14.274
256 | 57.634 | 51.008 | 29.930
384 | 114.280 | 94.760 | 46.449
512 | 196.492 | 154.230 | 63.758
640 | 315.037 | 240.142 | 82.476
768 | 466.149 | 338.043 | 101.318
896 | 679.029 | 511.097 | 134.854
1024 | 897.806 | 592.823 | 141.852
-----------------------------------------

Table 6: Planning time of query B (debug build, ms)
------------------------------------------
n | Master | v28 | v28 w/ patch
------------------------------------------
1 | 43.788 | 46.364 | 45.418
2 | 42.637 | 45.750 | 44.093
4 | 43.842 | 48.109 | 45.000
8 | 47.504 | 54.410 | 48.199
16 | 55.682 | 67.242 | 53.895
32 | 77.736 | 98.507 | 66.877
64 | 144.772 | 185.697 | 96.591
128 | 411.967 | 503.644 | 166.437
256 | 1653.681 | 1610.697 | 337.940
------------------------------------------

2. Discussion

Tables 1, 2, 3, and 4 show that the proposed patches increase memory
consumption. There seems to be no difference between v23 and v28. The
increase is more significant for query B, which is 2x or more.

For debug builds, I observed regressions compared to the master. The
regressions were reduced with the attached patch. This indicates that
the get_ec_[source|derive]_indexes[_strict]() functions (quoted below)
have time-consuming assertions. I think these assertions are helpful,
but it might be better to remove them to avoid slowing down debug
builds. What do you think?

=====
Bitmapset *
get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
Relids relids)
{
Bitmapset *edis = NULL;
int i = bms_next_member(relids, -1);

if (i >= 0)
{
EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];

/*
* bms_intersect to the first relation to try to keep the resulting
* Bitmapset as small as possible. This saves having to make a
* complete bms_copy() of one of them. One may contain significantly
* more words than the other.
*/
edis = bms_intersect(ec->ec_derive_indexes,
index->derive_indexes);

while ((i = bms_next_member(relids, i)) >= 0)
{
index = &root->eclass_indexes_array[i];
edis = bms_int_members(edis, index->derive_indexes);
}
}

#ifdef USE_ASSERT_CHECKING
/* verify the results look sane */
i = -1;
while ((i = bms_next_member(edis, i)) >= 0)
{
RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
i);

Assert(bms_is_subset(relids, rinfo->clause_relids));
}
#endif

return edis;
}
=====

[1]: /messages/by-id/CAExHW5stmOUobE55pMt83r8UxvfCph+Pvo5dNpdrVCsBgXEzDQ@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

Report-memory-used-for-planning-a-query-in-EXPLAIN-A.txttext/plain; charset=US-ASCII; name=Report-memory-used-for-planning-a-query-in-EXPLAIN-A.txtDownload
From 1c5bdc574b9ab136ec29a2bd86b43fb484a57345 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Wed, 12 Jul 2023 14:34:14 +0530
Subject: [PATCH] Report memory used for planning a query in EXPLAIN ANALYZE

The memory used in the CurrentMemoryContext and its children is sampled
before and after calling pg_plan_query() from ExplainOneQuery(). The
difference in the two samples is reported as the memory consumed while
planning the query. This may not account for the memory allocated in
memory contexts which are not children of CurrentMemoryContext. These
contexts are usually other long lived contexts, e.g.
CacheMemoryContext, which are shared by all the queries run in a
session. The consumption in those can not be attributed only to a given
query and hence should not be reported any way.

The memory consumption is reported as "Planning Memory" property in
EXPLAIN ANALYZE output.

Ashutosh Bapat
---
 src/backend/commands/explain.c | 12 ++++++++++--
 src/backend/commands/prepare.c |  5 ++++-
 src/backend/utils/mmgr/mcxt.c  | 19 +++++++++++++++++++
 src/include/commands/explain.h |  3 ++-
 src/include/utils/memutils.h   |  1 +
 5 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a3f1d53d7a..42fe11b3ce 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -471,6 +471,7 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
 	MemoryContextCounters mem_counters;
 	MemoryContext planner_ctx = NULL;
 	MemoryContext saved_ctx = NULL;
+	Size		mem_consumed;
 
 	if (es->memory)
 	{
@@ -491,12 +492,14 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
 	if (es->buffers)
 		bufusage_start = pgBufferUsage;
 	INSTR_TIME_SET_CURRENT(planstart);
+	mem_consumed = MemoryContextMemUsed(CurrentMemoryContext);
 
 	/* plan the query */
 	plan = pg_plan_query(query, queryString, cursorOptions, params);
 
 	INSTR_TIME_SET_CURRENT(planduration);
 	INSTR_TIME_SUBTRACT(planduration, planstart);
+	mem_consumed = MemoryContextMemUsed(CurrentMemoryContext) - mem_consumed;
 
 	if (es->memory)
 	{
@@ -514,7 +517,7 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
 	/* run it (if needed) and produce output */
 	ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
 				   &planduration, (es->buffers ? &bufusage : NULL),
-				   es->memory ? &mem_counters : NULL);
+				   es->memory ? &mem_counters : NULL, &mem_consumed);
 }
 
 /*
@@ -638,7 +641,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 			   const char *queryString, ParamListInfo params,
 			   QueryEnvironment *queryEnv, const instr_time *planduration,
 			   const BufferUsage *bufusage,
-			   const MemoryContextCounters *mem_counters)
+			   const MemoryContextCounters *mem_counters,
+			   const Size *mem_consumed)
 {
 	DestReceiver *dest;
 	QueryDesc  *queryDesc;
@@ -769,6 +773,10 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 		double		plantime = INSTR_TIME_GET_DOUBLE(*planduration);
 
 		ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
+
+		if (mem_consumed)
+			ExplainPropertyUInteger("Planning Memory", "bytes",
+									(uint64) *mem_consumed, es);
 	}
 
 	/* Print info about runtime of triggers */
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index a93f970a29..e5a49e825e 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -582,6 +582,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
 	MemoryContextCounters mem_counters;
 	MemoryContext planner_ctx = NULL;
 	MemoryContext saved_ctx = NULL;
+	Size		mem_consumed;
 
 	if (es->memory)
 	{
@@ -596,6 +597,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
 	if (es->buffers)
 		bufusage_start = pgBufferUsage;
 	INSTR_TIME_SET_CURRENT(planstart);
+	mem_consumed = MemoryContextMemUsed(CurrentMemoryContext);
 
 	/* Look it up in the hash table */
 	entry = FetchPreparedStatement(execstmt->name, true);
@@ -632,6 +634,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
 
 	INSTR_TIME_SET_CURRENT(planduration);
 	INSTR_TIME_SUBTRACT(planduration, planstart);
+	mem_consumed = MemoryContextMemUsed(CurrentMemoryContext) - mem_consumed;
 
 	if (es->memory)
 	{
@@ -656,7 +659,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
 		if (pstmt->commandType != CMD_UTILITY)
 			ExplainOnePlan(pstmt, into, es, query_string, paramLI, pstate->p_queryEnv,
 						   &planduration, (es->buffers ? &bufusage : NULL),
-						   es->memory ? &mem_counters : NULL);
+						   es->memory ? &mem_counters : NULL, &mem_consumed);
 		else
 			ExplainOneUtility(pstmt->utilityStmt, into, es, pstate, paramLI);
 
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 70d33226cb..e0a119a886 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -865,6 +865,25 @@ MemoryContextStatsDetail(MemoryContext context,
 	}
 }
 
+/*
+ * Compute the memory used in the given context and its children.
+ *
+ * XXX: Instead of this separate function we could modify
+ * MemoryContextStatsDetail() to report used memory and disable printing the
+ * detailed stats.
+ */
+extern Size
+MemoryContextMemUsed(MemoryContext context)
+{
+	MemoryContextCounters grand_totals;
+
+	memset(&grand_totals, 0, sizeof(grand_totals));
+
+	MemoryContextStatsInternal(context, 0, false, 100, &grand_totals, false);
+
+	return grand_totals.totalspace - grand_totals.freespace;
+}
+
 /*
  * MemoryContextStatsInternal
  *		One recursion level for MemoryContextStats
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index aa5872bc15..4fb267afef 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -108,7 +108,8 @@ extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into,
 						   ParamListInfo params, QueryEnvironment *queryEnv,
 						   const instr_time *planduration,
 						   const BufferUsage *bufusage,
-						   const MemoryContextCounters *mem_counters);
+						   const MemoryContextCounters *mem_counters,
+						   const Size *mem_consumed);
 
 extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
 extern void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc);
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index bf93433b78..9afb0bfb2a 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -92,6 +92,7 @@ extern void MemoryContextStatsDetail(MemoryContext context,
 									 bool print_to_stderr);
 extern void MemoryContextAllowInCriticalSection(MemoryContext context,
 												bool allow);
+extern Size MemoryContextMemUsed(MemoryContext context);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 extern void MemoryContextCheck(MemoryContext context);
-- 
2.45.2.windows.1

remove-some-assertions.txttext/plain; charset=US-ASCII; name=remove-some-assertions.txtDownload
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 86057538c5..9bbe11a9de 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -4014,18 +4014,6 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
 	/* bitwise-AND to leave only the ones for this EquivalenceClass */
 	return bms_int_members(rel_esis, ec->ec_source_indexes);
 }
@@ -4065,18 +4053,6 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		}
 	}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
 	return esis;
 }
 
@@ -4103,18 +4079,6 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
 	/* bitwise-AND to leave only the ones for this EquivalenceClass */
 	return bms_int_members(rel_edis, ec->ec_derive_indexes);
 }
@@ -4154,18 +4118,6 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		}
 	}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
 	return edis;
 }
 
#95Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Yuya Watari (#94)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Yuya,

On 2024-Dec-11, Yuya Watari wrote:

On Tue, Dec 3, 2024 at 7:38 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

I don't think planning time in assert-enabled builds is something we
should worry about, at all. Planning time in production builds is the
important one.

Thank you for your reply. Making debug builds too slow is not good for
developers,

I'm repeating myself, but I disagree that this is something we should
spend _any_ time on. Developers running assertion-enabled builds do not
care if a complicated query with one thousand partitions is planned in
500 ms instead of 300 ms. Heck, I bet nobody cares if it took 2000 ms
either, because, you know what? The developers don't have a thousand
partitions to begin with; if they do, it's precisely because they want
to measure this kind of effect. This is not going to bother anyone
ever, unless you stick a hundred of these queries in the regression
tests. In regression tests you're going to have, say, 64 partitions at
most, because having more than that doesn't test anything additional;
having that go from 40 ms to 60 ms (or whatever) isn't going to bother
anyone.

If anything, you can add a note to remove the USE_ASSERTIONS blocks once
we get past the beta process; by then any bugs will have been noticed
and the asserts will be of less value.

I would like to see this patch series get committed, and this concern
about planning time in development builds under conditions that are
unrealistic for testing is slowing the process down. (The process is
slow enough. This patch has already missed two releases.) Please stop.

Memory usage and planning time in production builds is important. You
can better spend your energy there.

Thanks

--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"La gente vulgar sólo piensa en pasar el tiempo;
el que tiene talento, en aprovecharlo"

#96Yuya Watari
watari.yuya@gmail.com
In reply to: Alvaro Herrera (#95)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Alvaro,

Thank you for your reply, and I'm sorry if my previous emails caused
confusion or made it seem like I was ignoring more important issues.

On Thu, Dec 12, 2024 at 9:09 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

I'm repeating myself, but I disagree that this is something we should
spend _any_ time on. Developers running assertion-enabled builds do not
care if a complicated query with one thousand partitions is planned in
500 ms instead of 300 ms. Heck, I bet nobody cares if it took 2000 ms
either, because, you know what? The developers don't have a thousand
partitions to begin with; if they do, it's precisely because they want
to measure this kind of effect. This is not going to bother anyone
ever, unless you stick a hundred of these queries in the regression
tests. In regression tests you're going to have, say, 64 partitions at
most, because having more than that doesn't test anything additional;
having that go from 40 ms to 60 ms (or whatever) isn't going to bother
anyone.

I agree that focusing too much on assert-enabled builds is not
productive at this point. In my last email, I shared benchmark results
for debug builds, but I understand your point that even a few seconds
of regression is not practically important for debug builds.

For context, there have been reports in the past of minute-order
regressions in assert-enabled builds (100 seconds [1]/messages/by-id/d8db5b4e-e358-2567-8c56-a85d2d8013df@postgrespro.ru and 50 seconds
[2]: /messages/by-id/CAExHW5uVZ3E5RT9cXHaxQ_DEK7tasaMN=D6rPHcao5gcXanY5w@mail.gmail.com
discussion on debug builds right now, but to clarify why we have been
concerned about them in the past. I should have shared this background
and done appropriate benchmarks (not millisecond regressions, but
minutes). My sincere apologies. Once we have addressed the primary
goals (release build performance and memory usage), I will revisit
these regressions.

If anything, you can add a note to remove the USE_ASSERTIONS blocks once
we get past the beta process; by then any bugs will have been noticed
and the asserts will be of less value.

Thank you for your advice. I will consider removing these assertions
after the beta process or using OPTIMIZER_DEBUG, which is Ashutosh's
idea.

I would like to see this patch series get committed, and this concern
about planning time in development builds under conditions that are
unrealistic for testing is slowing the process down. (The process is
slow enough. This patch has already missed two releases.) Please stop.

I will speed up the process for committing this patch series.

Memory usage and planning time in production builds is important. You
can better spend your energy there.

As you said, we have another big problem, which is memory usage. I
will focus on the memory usage problem first, as you suggested. After
fixing those problems, we can revisit the assert-enabled build
regressions as a final step if necessary. What do you think about this
approach?

[1]: /messages/by-id/d8db5b4e-e358-2567-8c56-a85d2d8013df@postgrespro.ru
[2]: /messages/by-id/CAExHW5uVZ3E5RT9cXHaxQ_DEK7tasaMN=D6rPHcao5gcXanY5w@mail.gmail.com

--
Best regards,
Yuya Watari

#97Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Yuya Watari (#96)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On 2024-Dec-13, Yuya Watari wrote:

Thank you for your reply, and I'm sorry if my previous emails caused
confusion or made it seem like I was ignoring more important issues.

Not to worry!

Memory usage and planning time in production builds [are] important.
You can better spend your energy there.

As you said, we have another big problem, which is memory usage. I
will focus on the memory usage problem first, as you suggested.

That's great, thanks.

BTW I forgot to mention it yesterday, but I was surprised that you
attached Ashutosh's old patch for planner memory usage reporting.
This feature is already in EXPLAIN (MEMORY), so you don't need any patch
to measure memory consumption ... or does your patch add some detail
that isn't already in the code?

After fixing those problems, we can revisit the assert-enabled build
regressions as a final step if necessary. What do you think about this
approach?

Sounds good.

--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"I can't go to a restaurant and order food because I keep looking at the
fonts on the menu. Five minutes later I realize that it's also talking
about food" (Donald Knuth)

#98Yuya Watari
watari.yuya@gmail.com
In reply to: Alvaro Herrera (#97)
9 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Alvaro and all,

Thank you for the quick reply and I apologize for the delay in responding.

On Fri, Dec 13, 2024 at 7:53 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

BTW I forgot to mention it yesterday, but I was surprised that you
attached Ashutosh's old patch for planner memory usage reporting.
This feature is already in EXPLAIN (MEMORY), so you don't need any patch
to measure memory consumption ... or does your patch add some detail
that isn't already in the code?

Thank you for pointing this out. I did not add anything beyond the
rebase. I apologize for not noticing this. I have now re-run the same
experiments as in [1]/messages/by-id/CAJ2pMkZMKwdjYRUGA0=za4SUrgpBJ5_JDYuVeG=Esbv1dKSouQ@mail.gmail.com using EXPLAIN (MEMORY).

As you said, we have another big problem, which is memory usage. I
will focus on the memory usage problem first, as you suggested.

That's great, thanks.

In v29-0008, which is attached to this email, I introduced a new
approach to reduce memory usage during planning. Patch 0002 added
Bitmapset indexes over RestrictInfos. Previously, when retrieving
RestrictInfos, we had to perform intersections or unions of these
Bitmapset indexes, resulting in frequent allocations and copies of
temporary Bitmapsets. This was a significant source of memory
consumption.

The v29-0008 patch adds an iterator mechanism that emulates
bms_next_member(), bms_intersect() and bms_union() operations without
allocating additional Bitmapsets. Instead of constructing new
temporary Bitmapsets for each operation, the iterator performs these
operations on the fly. This approach eliminates the need for temporary
Bitmapsets and reduces memory usage.

I have attached updated experimental results below (tables and figure)
showing the effect of this iterator mechanism on memory usage and
planning time. In summary, the main results are as follows:

* Memory Usage: The v29-0008 change significantly reduces memory
consumption, especially in query B (44.4% reduction).
* Planning Time: While we still see a significant overall improvement
in planning time compared to master, there is some regression observed
in cases with a very large number of tables compared to v28. Notably,
this regression does not occur in smaller size cases such as
'installcheck'.

Table 1: Memory usage (MB)
(n: the number of partitions of each table; PWJ: partition-wise join)
-----------------------------------------------------
Query | n | PWJ | Master | v28 | v29
-----------------------------------------------------
A | 1024 | OFF | 47.818 | 69.000 | 59.156
A | 1024 | ON | 119.969 | 141.150 | 131.307
B | 256 | OFF | 89.429 | 196.217 | 109.088
B | 256 | ON | 4945.597 | 5052.386 | 4965.257
-----------------------------------------------------

---------------------------------------------------------------
Query | n | PWJ | v28 - v29 | 1 - v29 / v28 | v29 - Master
---------------------------------------------------------------
A | 1024 | OFF | 9.844 | 14.3% | 11.338
A | 1024 | ON | 9.844 | 7.0% | 11.338
B | 256 | OFF | 87.129 | 44.4% | 19.659
B | 256 | ON | 87.129 | 1.7% | 19.660
---------------------------------------------------------------

Table 2: Total planning time for installcheck (seconds)
------------------------------------------
Version | Mean | Median | Stddev
------------------------------------------
Master | 0.898575 | 0.897965 | 0.005739
v28 | 0.909598 | 0.906713 | 0.010797
v29 | 0.907552 | 0.906640 | 0.008023
------------------------------------------

Table 3: Speedup for installcheck (higher is better)
--------------------------
Version | Mean | Median
--------------------------
v28 | 98.8% | 99.0%
v29 | 99.0% | 99.0%
--------------------------

Table 4: Planning time for query A (ms)
----------------------------------
n | Master | v28 | v29
----------------------------------
1 | 0.243 | 0.247 | 0.246
2 | 0.263 | 0.269 | 0.269
4 | 0.327 | 0.349 | 0.338
8 | 0.433 | 0.435 | 0.438
16 | 0.617 | 0.615 | 0.608
32 | 1.061 | 1.000 | 0.982
64 | 2.083 | 1.955 | 1.938
128 | 5.774 | 4.225 | 4.209
256 | 17.521 | 10.446 | 10.682
384 | 32.913 | 16.196 | 16.669
512 | 57.379 | 22.274 | 23.392
640 | 92.402 | 28.751 | 30.526
768 | 148.043 | 34.923 | 38.501
896 | 257.313 | 50.124 | 54.165
1024 | 335.586 | 49.742 | 56.389
----------------------------------

Table 5: Speedup of query A (higher is better)
------------------------
n | v28 | v29
------------------------
1 | 98.3% | 98.9%
2 | 98.0% | 98.0%
4 | 93.6% | 96.6%
8 | 99.5% | 98.9%
16 | 100.3% | 101.4%
32 | 106.2% | 108.0%
64 | 106.6% | 107.5%
128 | 136.7% | 137.2%
256 | 167.7% | 164.0%
384 | 203.2% | 197.5%
512 | 257.6% | 245.3%
640 | 321.4% | 302.7%
768 | 423.9% | 384.5%
896 | 513.3% | 475.1%
1024 | 674.6% | 595.1%
------------------------

Table 6: Planning time for query B (ms)
-----------------------------------
n | Master | v28 | v29
-----------------------------------
1 | 11.675 | 11.755 | 11.651
2 | 11.210 | 11.282 | 11.169
4 | 11.745 | 11.677 | 11.556
8 | 12.896 | 12.545 | 12.324
16 | 15.455 | 14.112 | 14.024
32 | 21.642 | 17.531 | 17.585
64 | 44.220 | 26.481 | 26.910
128 | 129.947 | 47.559 | 51.107
256 | 772.431 | 105.115 | 131.829
-----------------------------------

Table 7: Speedup of query B (higher is better)
-----------------------
n | v28 | v29
-----------------------
1 | 99.3% | 100.2%
2 | 99.4% | 100.4%
4 | 100.6% | 101.6%
8 | 102.8% | 104.6%
16 | 109.5% | 110.2%
32 | 123.5% | 123.1%
64 | 167.0% | 164.3%
128 | 273.2% | 254.3%
256 | 734.8% | 585.9%
-----------------------

As shown in the above tables and the attached figure, the iterator
mechanism reduces memory usage (especially for query B, where we
observed a 44.4% reduction). Partition-wise join has no impact on
memory usage in either query A or B.

For planning time, v29 maintains the improvements seen in previous
versions, although there is some regression at large sizes (for
example, about 25.4% slower or 26.7ms more in query B with 256
partitions). For small sizes (a few partitions in the queries A and B,
and 'installcheck'), there is not much difference in planning time
between v28 and v29.

Overall, v29 demonstrates a better balance between planning time and
memory usage. There may still be room for further optimization of the
iterator mechanism, but I believe this is a good step towards
addressing previous concerns. I would appreciate any feedback or
suggestions.

[1]: /messages/by-id/CAJ2pMkZMKwdjYRUGA0=za4SUrgpBJ5_JDYuVeG=Esbv1dKSouQ@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

v29-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v29-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 750d1d66aaef2afab645c45e2a8b52732ff3c149 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v29 1/8] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +-
 src/backend/optimizer/path/equivclass.c | 464 ++++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c   |  47 ++-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  89 +++++
 src/include/nodes/pathnodes.h           |  61 ++++
 src/include/optimizer/pathnode.h        |  18 +
 src/include/optimizer/paths.h           |  12 +-
 src/tools/pgindent/typedefs.list        |   1 +
 11 files changed, 698 insertions(+), 114 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c0810fbd7c8..e68c116164c 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7828,13 +7828,35 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	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);
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * If we need to see child EquivalenceMembers, we access them via
+	 * EquivalenceChildMemberIterator during the iteration.
+	 */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/*
+		 * If child EquivalenceMembers may match the request, we add and
+		 * iterate over them by calling iterate_child_rel_equivalences().
+		 */
+		if (unlikely(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
@@ -7846,6 +7868,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7899,9 +7922,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index fae137dd825..25ce4dd2242 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -68,6 +72,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -373,7 +382,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -390,7 +399,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -422,9 +431,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +519,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +539,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +561,29 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -598,6 +632,17 @@ 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.
@@ -616,7 +661,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,16 +677,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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.
+			 * 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().
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -653,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -690,7 +749,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -760,19 +819,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	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;
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * If we need to see child EquivalenceMembers, we access them via
+	 * EquivalenceChildMemberIterator during the iteration.
+	 */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -782,6 +857,14 @@ find_ec_member_matching_expr(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 (unlikely(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.
 		 */
@@ -799,6 +882,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +925,9 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -854,9 +940,23 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_WINDOWFUNCS |
 							   PVC_INCLUDE_PLACEHOLDERS);
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * 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);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -867,6 +967,14 @@ 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 (unlikely(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.
 		 */
@@ -900,6 +1008,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -938,7 +1047,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1563,7 +1672,19 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	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
@@ -1573,10 +1694,20 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/*
+		 * If child EquivalenceMembers may match the request, we add and
+		 * iterate over them by calling iterate_child_rel_equivalences().
+		 */
+		if (unlikely(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
@@ -1593,6 +1724,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1610,6 +1742,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1684,6 +1817,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1691,7 +1825,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2404,6 +2538,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2525,8 +2660,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2597,8 +2732,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2695,6 +2830,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2707,7 +2843,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,15 +2855,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2741,8 +2870,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2758,6 +2887,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2787,9 +2917,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
@@ -2818,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2839,7 +2981,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2852,15 +2993,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2869,8 +3004,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2885,6 +3020,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2915,9 +3051,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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
+				 * add_transformed_child_version() 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);
 			}
 		}
 	}
@@ -2986,6 +3134,161 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * iterate_child_rel_equivalences
+ *	  Add transformed EquivalenceMembers referencing child rels in the given
+ *	  child_relids to the iterator.
+ *
+ * The transformation is done in add_transformed_child_version().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	int			i;
+	Relids		matching_relids;
+
+	/* 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. This information is stored in indexes named em_child_relids
+	 * and em_child_joinrel_relids.
+	 *
+	 * This function iterates over the child EquivalenceMembers that reference
+	 * the given 'child_relids'. To do this, we first intersect 'child_relids'
+	 * with these indexes. The result contains Relids of RelOptInfos that have
+	 * child EquivalenceMembers we want to retrieve. Then we get the child
+	 * members from the RelOptInfos using add_transformed_child_version().
+	 *
+	 * We need to do these steps for each of the two indexes.
+	 */
+
+	/*
+	 * First, we translate simple rels.
+	 */
+	matching_relids = bms_intersect(parent_em->em_child_relids,
+									child_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child rel */
+		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);
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	i = -1;
+	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	{
+		/* Add transformed child version for this child join rel */
+		RelOptInfo *child_joinrel =
+			list_nth_node(RelOptInfo, root->join_rel_list, i);
+
+		Assert(child_joinrel != NULL);
+		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3019,7 +3322,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids,
+				top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -3028,6 +3332,16 @@ 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);
@@ -3040,6 +3354,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3060,16 +3375,25 @@ 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.
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3084,8 +3408,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3303,8 +3627,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5d102a0d371..4a42752486e 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3726,12 +3726,23 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
@@ -3743,7 +3754,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3762,16 +3774,36 @@ match_pathkeys_to_index(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.
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
+			/*
+			 * If child EquivalenceMembers may match the request, we add and
+			 * iterate over them by calling iterate_child_rel_equivalences().
+			 */
+			if (unlikely(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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (!member->em_is_child &&
+				!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
@@ -3800,6 +3832,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 03c2ac36e05..b8f9c5f0e93 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 178c572b021..b5f8d5fad0a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1285,7 +1289,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1329,7 +1333,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1470,7 +1474,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1501,7 +1505,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1981,7 +1985,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2195,7 +2199,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2219,7 +2223,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2288,7 +2292,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4550,7 +4554,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4559,7 +4564,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4584,7 +4590,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6234,7 +6240,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6301,7 +6307,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6329,7 +6335,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6345,7 +6351,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6416,7 +6422,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6425,7 +6432,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6451,8 +6458,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6461,7 +6469,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6820,7 +6828,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6883,7 +6892,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index c5b906a9d43..3c2198ea5bd 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f96573eb5d6..ce494364173 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -123,11 +123,14 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +151,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +199,25 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +271,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +667,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +785,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +973,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1536,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1530,6 +1577,48 @@ 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 == 0)
+			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 0759e00e96d..d351bc5fa85 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -248,6 +248,14 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -957,6 +965,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1376,6 +1395,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1448,10 +1473,46 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1035e6560c1..5e79cf1f63b 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -332,6 +332,24 @@ 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) \
+	(likely((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 54869d44013..50812e3a5d8 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -179,6 +180,15 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index ce33e55bf1d..9673feef969 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -684,6 +684,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
 EquivalenceMember
 ErrorContextCallback
-- 
2.45.2.windows.1

v29-0002-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v29-0002-Introduce-indexes-for-RestrictInfo.patchDownload
From 559b292ebb2792c71551aca4d088df73dbfb6651 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v29 2/8] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   6 +-
 src/backend/nodes/readfuncs.c             |   2 +
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 516 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/inherit.c      |   7 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/parsenodes.h            |   6 +
 src/include/nodes/pathnodes.h             |  41 +-
 src/include/optimizer/paths.h             |  21 +-
 13 files changed, 593 insertions(+), 69 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9827cf16be4..bd6a79b57a7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -573,6 +573,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index be5f19dd7f6..70864fc1b29 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,6 +434,8 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index c36687aa4df..1ec10212eb8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 25ce4dd2242..474231499e6 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -319,7 +323,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -330,6 +333,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -350,8 +355,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -363,10 +370,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -377,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -394,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -411,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -420,8 +430,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -443,6 +453,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -584,6 +596,167 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_del_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_del_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  rte->eclass_source_indexes));
+			rte->eclass_source_indexes =
+				bms_add_member(rte->eclass_source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  rte->eclass_derive_indexes));
+			rte->eclass_derive_indexes =
+				bms_add_member(rte->eclass_derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 rte->eclass_source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 rte->eclass_derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -729,8 +902,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1134,7 +1307,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1227,6 +1400,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1236,9 +1410,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1296,9 +1470,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1308,7 +1482,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1374,7 +1549,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1431,11 +1606,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1476,11 +1652,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1870,12 +2046,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1887,12 +2067,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1954,10 +2134,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1968,9 +2149,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1981,9 +2165,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2056,7 +2244,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2782,16 +2970,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3517,7 +3708,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3605,7 +3796,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3762,3 +3953,240 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely eclass_source_indexes and
+ *		eclass_derive_indexes. If you modify these indexes, you should check
+ *		them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * eclass_source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * eclass_derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 5bc16c4bfc7..4449b8a22ee 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -36,9 +36,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -445,7 +446,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -474,7 +475,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -547,19 +548,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -584,14 +594,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -607,9 +617,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -636,11 +648,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -648,7 +661,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a0a2de7ee4b..32feb4c86ff 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -661,6 +661,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 3fa4d78c3e0..e68ff37e042 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1153,6 +1153,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 45e8b74f944..db88047f8b7 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -495,6 +495,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5bd..b5d3984219e 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,6 +492,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index ca3e764c201..917d6b199cf 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,9 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -394,6 +397,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0f9462493e3..d28e2034d09 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,6 +1247,12 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index d351bc5fa85..47f213d4f38 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -321,6 +321,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1401,6 +1407,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1419,8 +1443,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -2661,7 +2687,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2779,6 +2810,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 50812e3a5d8..a24a5801c55 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -165,7 +167,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +207,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v29-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchapplication/octet-stream; name=v29-0003-Move-EquivalenceClass-indexes-to-PlannerInfo.patchDownload
From 80337bf565cba807a58fc6dc959e93dd94c96f96 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 19 Jan 2024 11:43:29 +0900
Subject: [PATCH v29 3/8] Move EquivalenceClass indexes to PlannerInfo

In the previous commit, the indexes, namely ec_[source|derive]_indexes,
were in RestrictInfo. This was a workaround because RelOptInfo was the
best place but some RelOptInfos can be NULL and we cannot store indexes
for them.

This commit introduces a new struct, EquivalenceClassIndexes. This
struct exists in PlannerInfo and holds our indexes. This change prevents
a serialization problem with RestirctInfo in the previous commit.
---
 src/backend/nodes/outfuncs.c            |  2 -
 src/backend/nodes/readfuncs.c           |  2 -
 src/backend/optimizer/path/equivclass.c | 83 ++++++++++++-------------
 src/backend/optimizer/util/inherit.c    |  7 ---
 src/backend/optimizer/util/relnode.c    |  6 ++
 src/include/nodes/parsenodes.h          |  6 --
 src/include/nodes/pathnodes.h           | 26 ++++++++
 src/tools/pgindent/typedefs.list        |  1 +
 8 files changed, 74 insertions(+), 59 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bd6a79b57a7..8a635a2a12b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -573,8 +573,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
-	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
-	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 70864fc1b29..be5f19dd7f6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,8 +434,6 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
-	READ_BITMAPSET_FIELD(eclass_source_indexes);
-	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 474231499e6..96869ea80e8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -613,10 +613,10 @@ add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
-													source_idx);
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
 	}
 }
 
@@ -637,10 +637,10 @@ add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
 	i = -1;
 	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
-													derive_idx);
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
 	}
 }
 
@@ -679,22 +679,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(removing_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_del_member(rte->eclass_source_indexes,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_del_member(rte->eclass_derive_indexes,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -708,22 +708,22 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(adding_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_sources_index,
-								  rte->eclass_source_indexes));
-			rte->eclass_source_indexes =
-				bms_add_member(rte->eclass_source_indexes,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
 							   rinfo->eq_sources_index);
 		}
 		if (rinfo->eq_derives_index != -1)
 		{
 			Assert(!bms_is_member(rinfo->eq_derives_index,
-								  rte->eclass_derive_indexes));
-			rte->eclass_derive_indexes =
-				bms_add_member(rte->eclass_derive_indexes,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
 							   rinfo->eq_derives_index);
 		}
 	}
@@ -738,14 +738,14 @@ update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
 	i = -1;
 	while ((i = bms_next_member(common_relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		if (rinfo->eq_sources_index != -1)
 			Assert(bms_is_member(rinfo->eq_sources_index,
-								 rte->eclass_source_indexes));
+								 index->source_indexes));
 		if (rinfo->eq_derives_index != -1)
 			Assert(bms_is_member(rinfo->eq_derives_index,
-								 rte->eclass_derive_indexes));
+								 index->derive_indexes));
 	}
 	bms_free(common_relids);
 #endif
@@ -3970,9 +3970,9 @@ get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4008,7 +4008,7 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4017,12 +4017,12 @@ get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		esis = bms_intersect(ec->ec_source_indexes,
-							 rte->eclass_source_indexes);
+							 index->source_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			esis = bms_int_members(esis, rte->eclass_source_indexes);
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
 		}
 	}
 
@@ -4059,9 +4059,9 @@ get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
 
 	while ((i = bms_next_member(relids, i)) >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
-		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
 	}
 
 #ifdef USE_ASSERT_CHECKING
@@ -4097,7 +4097,7 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 
 	if (i >= 0)
 	{
-		RangeTblEntry *rte = root->simple_rte_array[i];
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
 
 		/*
 		 * bms_intersect to the first relation to try to keep the resulting
@@ -4106,12 +4106,12 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 		 * more words than the other.
 		 */
 		edis = bms_intersect(ec->ec_derive_indexes,
-							 rte->eclass_derive_indexes);
+							 index->derive_indexes);
 
 		while ((i = bms_next_member(relids, i)) >= 0)
 		{
-			rte = root->simple_rte_array[i];
-			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
 		}
 	}
 
@@ -4134,9 +4134,8 @@ get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
 /*
  * verify_eclass_indexes
  *		Verify that there are no missing references between RestrictInfos and
- *		EquivalenceMember's indexes, namely eclass_source_indexes and
- *		eclass_derive_indexes. If you modify these indexes, you should check
- *		them with this function.
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
  */
 void
 verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
@@ -4145,7 +4144,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 
 	/*
 	 * All RestrictInfos in root->eq_sources must have references to
-	 * eclass_source_indexes.
+	 * source_indexes.
 	 */
 	foreach(lc, root->eq_sources)
 	{
@@ -4162,13 +4161,13 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_source_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
 		}
 	}
 
 	/*
 	 * All RestrictInfos in root->eq_derives must have references to
-	 * eclass_derive_indexes.
+	 * derive_indexes.
 	 */
 	foreach(lc, root->eq_derives)
 	{
@@ -4185,7 +4184,7 @@ verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
 		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
 		{
 			/* must have a reference */
-			Assert(bms_is_member(index, root->simple_rte_array[k]->eclass_derive_indexes));
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
 		}
 	}
 }
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index b5d3984219e..3c2198ea5bd 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -492,13 +492,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
-	/*
-	 * We do not want to inherit the EquivalenceMember indexes of the parent
-	 * to its child
-	 */
-	childrte->eclass_source_indexes = NULL;
-	childrte->eclass_derive_indexes = NULL;
-
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ce494364173..ca1d7e91bff 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -218,6 +221,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->top_parent_relid_array = NULL;
 	}
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d28e2034d09..0f9462493e3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,12 +1247,6 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
-	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
-										 * list for RestrictInfos that mention
-										 * this relation */
-	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
-										 * list for RestrictInfos that mention
-										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 47f213d4f38..09cfd626a6a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -192,6 +192,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -248,6 +250,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
@@ -1539,6 +1548,23 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
+ * struct exists for each relation and holds the corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9673feef969..a1f308e2ac4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -686,6 +686,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v29-0004-Rename-add_eq_member-to-add_parent_eq_member.patchapplication/octet-stream; name=v29-0004-Rename-add_eq_member-to-add_parent_eq_member.patchDownload
From 8c39e9822bc090c335ba4d1d29ebea23470bec29 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Wed, 21 Aug 2024 13:54:59 +0900
Subject: [PATCH v29 4/8] Rename add_eq_member() to add_parent_eq_member()

After our changes, add_eq_member() no longer creates and adds child
EquivalenceMembers. This commit renames it to add_parent_eq_member() to
clarify that it only creates parent members, and that we need to use
make_eq_member() to handle child EquivalenceMembers.
---
 src/backend/optimizer/path/equivclass.c | 54 ++++++++++++-------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 96869ea80e8..e30d931f19c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,10 +38,10 @@ static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
 										 JoinDomain *jdomain,
 										 EquivalenceMember *parent,
 										 Oid datatype);
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
@@ -389,8 +389,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -407,8 +407,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -440,10 +440,10 @@ 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,
-							jdomain, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -578,7 +578,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 }
 
 /*
- * add_eq_member - build a new parent EquivalenceMember and add it to an EC
+ * 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
@@ -586,8 +586,8 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * add_child_join_rel_equivalences() to see how to add child members.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, Oid datatype)
+add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+					 JoinDomain *jdomain, Oid datatype)
 {
 	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
 										   NULL, datatype);
@@ -921,14 +921,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -3305,12 +3305,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

figure.pngimage/png; name=figure.pngDownload
v29-0005-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v29-0005-Resolve-conflict-with-commit-66c0185.patchDownload
From a4e61b85297506ae3b94a92bf5ace5cdd067c4ef Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v29 5/8] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 51 ++++++++++++++++++++++---
 src/backend/optimizer/util/inherit.c    |  8 +++-
 src/backend/optimizer/util/relnode.c    | 10 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e30d931f19c..022f20020af 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3288,7 +3288,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3305,12 +3307,49 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		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
+		 * add_transformed_child_version() 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
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3c2198ea5bd..7b2390687e0 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ca1d7e91bff..fcd57c1f3e9 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
@@ -1608,7 +1612,7 @@ find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
 	{
 		int			top_parent_relid = (int) top_parent_relid_array[i];
 
-		if (top_parent_relid == 0)
+		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;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 09cfd626a6a..7009cb0dd89 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -260,7 +260,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v29-0006-Don-t-use-likely-in-find_relids_top_parents.patchapplication/octet-stream; name=v29-0006-Don-t-use-likely-in-find_relids_top_parents.patchDownload
From 367a57f208413f2df08824610480d6325c20b08b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 29 Nov 2024 14:49:42 +0900
Subject: [PATCH v29 6/8] Don't use likely in find_relids_top_parents

---
 src/include/optimizer/pathnode.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 5e79cf1f63b..cf21ba29fe9 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -346,7 +346,7 @@ extern Relids find_childrel_parents(PlannerInfo *root, RelOptInfo *rel);
  * root->top_parent_relid_array is NULL.
  */
 #define find_relids_top_parents(root, relids) \
-	(likely((root)->top_parent_relid_array == NULL) \
+	((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);
 
-- 
2.45.2.windows.1

v29-0007-Don-t-use-unlikely-top_parent-NULL.patchapplication/octet-stream; name=v29-0007-Don-t-use-unlikely-top_parent-NULL.patchDownload
From 13e4f61743b99809ecbbcf3454cd6d11d90a4254 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 29 Nov 2024 14:49:43 +0900
Subject: [PATCH v29 7/8] Don't use unlikely(top_parent != NULL)

---
 contrib/postgres_fdw/postgres_fdw.c     |  2 +-
 src/backend/optimizer/path/equivclass.c | 10 +++++-----
 src/backend/optimizer/path/indxpath.c   |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e68c116164c..030fe741030 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7854,7 +7854,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_rel_relids != NULL) && !em->em_is_child &&
+		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);
 
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 022f20020af..86057538c5c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -861,7 +861,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 			 * If child EquivalenceMembers may match the request, we add and
 			 * iterate over them by calling iterate_child_rel_equivalences().
 			 */
-			if (unlikely(top_parent_rel != NULL) && !cur_em->em_is_child &&
+			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);
 
@@ -1034,7 +1034,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+		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);
 
@@ -1144,7 +1144,7 @@ find_computable_ec_member(PlannerInfo *root,
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_relids != NULL) && !em->em_is_child &&
+		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);
 
@@ -1881,7 +1881,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		 * If child EquivalenceMembers may match the request, we add and
 		 * iterate over them by calling iterate_child_rel_equivalences().
 		 */
-		if (unlikely(top_parent_join_relids != NULL) && !cur_em->em_is_child &&
+		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);
 
@@ -3616,7 +3616,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			 * If child EquivalenceMembers may match the request, we add and
 			 * iterate over them by calling iterate_child_rel_equivalences().
 			 */
-			if (unlikely(top_parent_rel_relids != NULL) && !cur_em->em_is_child &&
+			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) &&
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4a42752486e..9b7d01a85d8 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3787,7 +3787,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 			 * If child EquivalenceMembers may match the request, we add and
 			 * iterate over them by calling iterate_child_rel_equivalences().
 			 */
-			if (unlikely(top_parent_index_relids != NULL) && !member->em_is_child &&
+			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);
-- 
2.45.2.windows.1

v29-0008-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v29-0008-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From 3e6392538153ffd46743bbedaf7f763afc13a2a6 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v29 8/8] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporal Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 313 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  18 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 198 insertions(+), 175 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index cd05c642b04..67330467b6c 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_next(). If you
+ * change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 86057538c5c..a890cc3507d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2046,16 +2046,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec, nominal_join_relids, true,
+								false);
+	while ((restrictinfo = eclass_rinfo_iterator_next(&iter)) != 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) &&
@@ -2063,6 +2061,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2134,11 +2133,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2149,12 +2147,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								true, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2164,14 +2161,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								false, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2181,6 +2177,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3994,179 +3991,167 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator
+ *	  Setup a RestrictInfoIterator 'iter' so that it can iterate over
+ *	  RestrictInfos. We iterate through root->eq_sources if 'is_source' is
+ *	  true, or root->eq_derives otherwise. The members must be from 'ec' and
+ *	  satisfy "bms_is_subset(relids, rinfo->clause_relids)" if 'is_strict' is
+ *	  true, or "bms_overlap(relids, rinfo->clause_relids)" otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
+							EquivalenceClass *ec, Relids relids,
+							bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator(). NULL is returned if there are no
+ *	  members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
 
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || (iter->last_word & mask) == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				iter->last_word = word;
+				mask = (~(bitmapword) 0);
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' must have a new member. We get it.
+	 */
+	w = iter->last_word & mask;
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 7009cb0dd89..3b4d4c9962f 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1548,6 +1548,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index a24a5801c55..e6aa4c715d9 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -207,18 +207,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+extern void setup_eclass_rinfo_iterator(RestrictInfoIterator *iter,
+										PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+										Relids relids,
+										bool is_source,
+										bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index a1f308e2ac4..0f4e0c48f4a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2469,6 +2469,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

#99Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#98)
9 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello all,

On Fri, Dec 20, 2024 at 2:26 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Overall, v29 demonstrates a better balance between planning time and
memory usage. There may still be room for further optimization of the
iterator mechanism, but I believe this is a good step towards
addressing previous concerns. I would appreciate any feedback or
suggestions.

I was looking at the v29-0001 patch and noticed that it lacks indexes
for the child joinrels generated by add_child_join_rel_equivalences().
To address this, I have introduced an inverted index mechanism to
speed up lookups for these child joinrels. This email summarizes the
changes, experimental results, and comparisons to both older patches
(especially v19) and the master.

1. Inverted index approach

In my approach, child EquivalenceMembers are stored in RelOptInfo
during add_child_join_rel_equivalences(). The inverted indexes in v30
maintain a mapping from Relids to their corresponding RelOptInfos.
When retrieving child members, we take the union of these indexes to
find all child EquivalenceMembers whose em_relids overlap the given
Relids. For more details, please see the comments in
iterate_child_rel_equivalences().

2. Merging small changes

Many small changes had accumulated as individual commits in previous
versions. For easier review, I have merged them into fewer commits in
v30. A diff between v29 and v30 is attached for quick reference.

3. Experimental setup

In this email, I ran additional experiments using a new query called
Query C (attached). Query C highlights the performance issues found in
previous versions, especially v29. I also tested v19 (rebased) [1]/messages/by-id/CAJ2pMkbsP4f4SvUx+GguQ1BaA8oVo4BfcLvy6--c3QqQcB8PAQ@mail.gmail.com,
which was the last version before my new approach was introduced. Note
that the rebased v19 does NOT pass regression tests. I may have missed
something, but I have not investigated the issue in detail.

In the experiments, I tested three queries, A and B (from [2]/messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com), and
the new query C. The patch versions tested were:
* Master
* v19 (rebased, but fails regression tests)
* v29
* v30
* v30 w/o 0004 (to evaluate the effect of the iterator mechanism)

4. Memory Usage

Using EXPLAIN (MEMORY), I measured the memory usage. The results are
shown in Table 1 and the attached figure. Here, "n" is the number of
partitions per table, and "PWJ" stands for partition-wise join.

Table 1: Memory usage (MB)
-------------------------------------------------------------------------------
Query | n | PWJ | Master | v19 | v29 | v30 | v30 w/o 0004
-------------------------------------------------------------------------------
A | 1024 | OFF | 47.821 | 79.281 | 59.159 | 59.183 | 69.027
A | 1024 | ON | 123.347 | 154.807 | 134.685 | 134.708 | 144.553
B | 256 | OFF | 90.409 | 204.711 | 110.068 | 110.084 | 197.214
B | 256 | ON | 5198.579 | 5312.881 | 5218.238 | 5218.254 | 5305.383
C | 1024 | OFF | 36.854 | 44.356 | 37.999 | 38.022 | 38.022
C | 1024 | ON | 85.574 | 100.843 | 215.571 | 88.932 | 88.932
-------------------------------------------------------------------------------

Summary:
* v19 used more memory than the other versions (including v30 w/o
0004, where the iterator mechanism was removed, and excluding query C
in v29).
* v29 used excessive memory for query C when PWJ was enabled, but v30
reduced it significantly.
* v30 used less memory than v19.

5. Planning Time (Queries A, B, and C)

Tables 2, 4, and 6 show the absolute planning times, and tables 3, 5,
and 7 show the corresponding speedups (higher is better). Below is a
brief summary:
* v19 and v30 have nearly identical planning times for small and large sizes.
* v29 introduced a major regression in query C, which was fixed in v30.
* v30 showed some regression for large sizes in query B, but this was
not seen in v30 w/o 0004, indicating that this regression was due to
the iterator mechanism introduced in v29.

Table 2: Planning time for query A (ms)
----------------------------------------------------------
n | Master | v19 | v29 | v30 | v30 w/o 0004
----------------------------------------------------------
1 | 0.241 | 0.245 | 0.246 | 0.246 | 0.247
2 | 0.261 | 0.264 | 0.264 | 0.264 | 0.266
4 | 0.320 | 0.343 | 0.330 | 0.337 | 0.330
8 | 0.430 | 0.432 | 0.433 | 0.434 | 0.437
16 | 0.612 | 0.606 | 0.611 | 0.611 | 0.615
32 | 1.070 | 1.001 | 1.009 | 1.014 | 1.029
64 | 2.278 | 1.952 | 2.176 | 2.189 | 2.190
128 | 6.129 | 4.579 | 4.633 | 4.562 | 4.216
256 | 17.485 | 10.432 | 10.636 | 10.750 | 10.534
384 | 32.584 | 15.916 | 16.495 | 16.569 | 15.993
512 | 54.771 | 21.923 | 23.066 | 23.065 | 22.008
640 | 88.273 | 28.381 | 30.097 | 30.127 | 28.136
768 | 136.878 | 34.678 | 37.818 | 37.931 | 34.573
896 | 216.365 | 50.652 | 53.296 | 53.031 | 49.368
1024 | 293.751 | 49.036 | 55.981 | 55.623 | 48.279
----------------------------------------------------------

Table 3: Speedup of query A (higher is better)
------------------------------------------------
n | v19 | v29 | v30 | v30 w/o 0004
------------------------------------------------
1 | 98.3% | 97.7% | 97.8% | 97.6%
2 | 98.9% | 98.8% | 99.1% | 98.1%
4 | 93.5% | 96.9% | 95.2% | 97.2%
8 | 99.4% | 99.2% | 99.0% | 98.3%
16 | 101.1% | 100.2% | 100.2% | 99.6%
32 | 106.9% | 106.1% | 105.5% | 104.0%
64 | 116.7% | 104.7% | 104.1% | 104.0%
128 | 133.8% | 132.3% | 134.3% | 145.4%
256 | 167.6% | 164.4% | 162.7% | 166.0%
384 | 204.7% | 197.5% | 196.7% | 203.7%
512 | 249.8% | 237.4% | 237.5% | 248.9%
640 | 311.0% | 293.3% | 293.0% | 313.7%
768 | 394.7% | 361.9% | 360.9% | 395.9%
896 | 427.2% | 406.0% | 408.0% | 438.3%
1024 | 599.1% | 524.7% | 528.1% | 608.5%
------------------------------------------------

Table 4: Planning time for query B (ms)
-----------------------------------------------------------
n | Master | v19 | v29 | v30 | v30 w/o 0004
-----------------------------------------------------------
1 | 11.918 | 12.405 | 12.020 | 11.870 | 12.020
2 | 11.413 | 11.864 | 11.524 | 11.369 | 11.575
4 | 11.896 | 12.225 | 11.895 | 11.787 | 11.966
8 | 13.201 | 13.086 | 12.830 | 12.658 | 12.888
16 | 15.917 | 14.742 | 14.490 | 14.398 | 14.569
32 | 21.838 | 17.842 | 17.776 | 17.658 | 17.793
64 | 44.337 | 26.242 | 27.055 | 26.910 | 26.508
128 | 126.472 | 46.073 | 50.969 | 51.004 | 47.114
256 | 631.093 | 98.469 | 128.827 | 129.046 | 101.041
-----------------------------------------------------------

Table 5: Speedup of query B (higher is better)
-----------------------------------------------
n | v19 | v29 | v30 | v30 w/o 0004
-----------------------------------------------
1 | 96.1% | 99.1% | 100.4% | 99.1%
2 | 96.2% | 99.0% | 100.4% | 98.6%
4 | 97.3% | 100.0% | 100.9% | 99.4%
8 | 100.9% | 102.9% | 104.3% | 102.4%
16 | 108.0% | 109.8% | 110.5% | 109.3%
32 | 122.4% | 122.9% | 123.7% | 122.7%
64 | 169.0% | 163.9% | 164.8% | 167.3%
128 | 274.5% | 248.1% | 248.0% | 268.4%
256 | 640.9% | 489.9% | 489.0% | 624.6%
-----------------------------------------------

Table 6: Planning time for query C (ms)
-------------------------------------------------------------
n | Master | v19 | v29 | v30 | v30 w/o 0004
-------------------------------------------------------------
1 | 0.262 | 0.266 | 0.263 | 0.262 | 0.263
2 | 0.380 | 0.383 | 0.379 | 0.379 | 0.380
4 | 0.526 | 0.534 | 0.529 | 0.528 | 0.526
8 | 0.841 | 0.851 | 0.844 | 0.833 | 0.833
16 | 1.593 | 1.599 | 1.581 | 1.564 | 1.573
32 | 3.393 | 3.359 | 3.392 | 3.306 | 3.293
64 | 6.795 | 6.584 | 6.835 | 6.950 | 6.977
128 | 15.439 | 14.461 | 15.619 | 14.143 | 14.231
256 | 35.247 | 31.036 | 35.643 | 30.011 | 30.422
512 | 85.460 | 66.008 | 91.261 | 64.484 | 64.724
1024 | 331.060 | 151.319 | 338.119 | 147.063 | 146.964
-------------------------------------------------------------

Table 7: Speedup of query C (higher is better)
------------------------------------------------
n | v19 | v29 | v30 | v30 w/o 0004
------------------------------------------------
1 | 98.6% | 99.7% | 100.0% | 99.7%
2 | 99.1% | 100.2% | 100.2% | 99.8%
4 | 98.6% | 99.6% | 99.6% | 100.0%
8 | 98.8% | 99.6% | 100.9% | 100.9%
16 | 99.6% | 100.7% | 101.8% | 101.2%
32 | 101.0% | 100.0% | 102.6% | 103.0%
64 | 103.2% | 99.4% | 97.8% | 97.4%
128 | 106.8% | 98.8% | 109.2% | 108.5%
256 | 113.6% | 98.9% | 117.4% | 115.9%
512 | 129.5% | 93.6% | 132.5% | 132.0%
1024 | 218.8% | 97.9% | 225.1% | 225.3%
------------------------------------------------

6. Conclusions

For planning time, v30 performs as well as the older version, v19. For
memory usage, v30 still consumes some memory, but much less than v19.
v19 consumes more memory than v30 w/o 0004, where the memory reduction
mechanism is not present.

Overall, v30 offers a balanced approach to both planning time and
memory usage. I would greatly appreciate any feedback, reviews, or
further suggestions.

[1]: /messages/by-id/CAJ2pMkbsP4f4SvUx+GguQ1BaA8oVo4BfcLvy6--c3QqQcB8PAQ@mail.gmail.com
[2]: /messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

diff-v29-v30.txttext/plain; charset=US-ASCII; name=diff-v29-v30.txtDownload
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 51b16d4f168..0395cee9a7c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3114,7 +3114,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/*
 				 * We save the knowledge that 'child_em' can be translated
 				 * using 'child_rel'. This knowledge is useful for
-				 * add_transformed_child_version() to find child members from
+				 * 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,
@@ -3209,6 +3209,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				Expr	   *child_expr;
 				Relids		new_relids;
 				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -3248,12 +3249,39 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/*
 				 * We save the knowledge that 'child_em' can be translated
 				 * using 'child_joinrel'. This knowledge is useful for
-				 * add_transformed_child_version() to find child members from
+				 * 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.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * needs EquivalenceMembers whose em_relids has child
+					 * relations, skipping the update of this inverted index
+					 * allows for faster iteration.
+					 */
+					if (root->top_parent_relid_array[j] == -1)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -3316,7 +3344,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		/*
 		 * We save the knowledge that 'child_em' can be translated using
 		 * 'child_rel'. This knowledge is useful for
-		 * add_transformed_child_version() to find child members from the
+		 * iterate_child_rel_equivalences() to find child members from the
 		 * given Relids.
 		 */
 		parent_em->em_child_relids =
@@ -3449,8 +3477,10 @@ add_transformed_child_version(EquivalenceChildMemberIterator *it,
 
 /*
  * iterate_child_rel_equivalences
- *	  Add transformed EquivalenceMembers referencing child rels in the given
- *	  child_relids to the iterator.
+ *	  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().
  */
@@ -3461,9 +3491,6 @@ iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
 							   EquivalenceMember *parent_em,
 							   Relids child_relids)
 {
-	int			i;
-	Relids		matching_relids;
-
 	/* The given EquivalenceMember should be parent */
 	Assert(!parent_em->em_is_child);
 
@@ -3473,46 +3500,111 @@ iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
 	 * 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. This information is stored in indexes named em_child_relids
-	 * and em_child_joinrel_relids.
+	 * 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.
 	 *
-	 * This function iterates over the child EquivalenceMembers that reference
-	 * the given 'child_relids'. To do this, we first intersect 'child_relids'
-	 * with these indexes. The result contains Relids of RelOptInfos that have
-	 * child EquivalenceMembers we want to retrieve. Then we get the child
-	 * members from the RelOptInfos using add_transformed_child_version().
+	 * 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 do these steps for each of the two indexes.
+	 * We need to perform these steps for each of the two types of relations.
 	 */
 
 	/*
 	 * First, we translate simple rels.
 	 */
-	matching_relids = bms_intersect(parent_em->em_child_relids,
-									child_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_relids, i)) >= 0)
+	if (!bms_is_empty(parent_em->em_child_relids))
 	{
-		/* Add transformed child version for this child rel */
-		RelOptInfo *child_rel = root->simple_rel_array[i];
+		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);
 
-		Assert(child_rel != NULL);
-		add_transformed_child_version(it, root, ec, parent_em, child_rel);
+		/* 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);
 	}
-	bms_free(matching_relids);
 
 	/*
 	 * Next, we try to translate join rels.
 	 */
-	i = -1;
-	while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0)
+	if (!bms_is_empty(parent_em->em_child_joinrel_relids))
 	{
-		/* Add transformed child version for this child join rel */
-		RelOptInfo *child_joinrel =
-			list_nth_node(RelOptInfo, root->join_rel_list, i);
+		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);
 
-		Assert(child_joinrel != NULL);
-		add_transformed_child_version(it, root, ec, parent_em, child_joinrel);
+		/* 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);
 	}
 }
 
@@ -4040,11 +4132,12 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 	++(iter->index);
 	bitnum = iter->index % BITS_PER_BITMAPWORD;
 	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
 	/*
 	 * Do we need to consume a new word?
 	 */
-	if (bitnum == 0 || (iter->last_word & mask) == 0)
+	if (bitnum == 0 || w == 0)
 	{
 		/*
 		 * Yes, we need a new word.
@@ -4114,8 +4207,7 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 			if (word != 0)
 			{
 				/* Yes, we find new ones. */
-				iter->last_word = word;
-				mask = (~(bitmapword) 0);
+				w = iter->last_word = word;
 				break;
 			}
 			/* No, we need to consume more word */
@@ -4123,9 +4215,8 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 	}
 
 	/*
-	 * Here, 'iter->last_word' must have a new member. We get it.
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
 	 */
-	w = iter->last_word & mask;
 	Assert(w != 0);
 	iter->index =
 		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 35af63bb399..cef24c10459 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1590,11 +1590,15 @@ typedef struct
  * EquivalenceClassIndexes
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
- * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This
- * struct exists for each relation and holds the corresponding indexes.
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
 	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
 								 * for RestrictInfos that mention this
 								 * relation */
figure.pngimage/png; name=figure.pngDownload
v30-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v30-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 4e6d52e0ecfea1e9712639735e144e9c9076b83c Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v30 1/4] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  36 +-
 src/backend/optimizer/path/equivclass.c | 594 ++++++++++++++++++++----
 src/backend/optimizer/path/indxpath.c   |  47 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 +--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  95 ++++
 src/include/nodes/pathnodes.h           |  85 ++++
 src/include/optimizer/pathnode.h        |  18 +
 src/include/optimizer/paths.h           |  12 +-
 src/tools/pgindent/typedefs.list        |   2 +
 11 files changed, 840 insertions(+), 133 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b92e2a0fc9f..7444622a527 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7828,13 +7828,35 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	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);
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * If we need to see child EquivalenceMembers, we access them via
+	 * EquivalenceChildMemberIterator during the iteration.
+	 */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+		/*
+		 * 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
@@ -7846,6 +7868,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7899,9 +7922,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7cafaca33c5..43711eb303a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,11 +33,15 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										EquivalenceMember *parent,
-										Oid datatype);
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -68,6 +72,11 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 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);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -372,8 +381,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -389,8 +398,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -421,10 +430,10 @@ 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,
-							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +519,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +539,8 @@ add_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;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +561,29 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * 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.
+ */
+static EquivalenceMember *
+add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+					 JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -598,6 +632,17 @@ 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.
@@ -616,7 +661,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,16 +677,28 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		/*
+		 * If we need to see child EquivalenceMembers, we access them via
+		 * EquivalenceChildMemberIterator during the iteration.
+		 */
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * 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.
+			 * 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().
 			 */
-			if (cur_em->em_is_child &&
-				!bms_equal(cur_em->em_relids, rel))
-				continue;
+			Assert(!cur_em->em_is_child || bms_equal(cur_em->em_relids, rel));
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -653,6 +711,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -689,14 +748,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -760,19 +819,35 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	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;
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * If we need to see child EquivalenceMembers, we access them via
+	 * EquivalenceChildMemberIterator during the iteration.
+	 */
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -782,6 +857,14 @@ find_ec_member_matching_expr(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.
 		 */
@@ -799,6 +882,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +925,9 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	Relids		top_parent_relids;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -854,9 +940,23 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_WINDOWFUNCS |
 							   PVC_INCLUDE_PLACEHOLDERS);
 
-	foreach(lc, ec->ec_members)
+	/*
+	 * 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);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -867,6 +967,14 @@ 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.
 		 */
@@ -900,6 +1008,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -938,7 +1047,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1563,7 +1672,19 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	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
@@ -1573,10 +1694,20 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, ec);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+		/*
+		 * 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
@@ -1593,6 +1724,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1610,6 +1742,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1684,6 +1817,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1691,7 +1825,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2404,6 +2538,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2525,8 +2660,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2597,8 +2732,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2695,6 +2830,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2707,7 +2843,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,15 +2855,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2741,8 +2870,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2758,6 +2887,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2787,9 +2917,20 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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);
@@ -2818,6 +2959,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2839,7 +2981,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2852,15 +2993,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2869,8 +3004,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2885,6 +3020,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2915,9 +3052,48 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				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.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * needs EquivalenceMembers whose em_relids has child
+					 * relations, skipping the update of this inverted index
+					 * allows for faster iteration.
+					 */
+					if (root->top_parent_relid_array[j] == 0)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2966,12 +3142,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -2986,6 +3162,225 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   EquivalenceClass *ec)
+{
+	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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * iterate_child_rel_equivalences().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  EquivalenceMember *parent_em,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_parent != parent_em)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * 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().
+ */
+void
+iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it,
+							   PlannerInfo *root,
+							   EquivalenceClass *ec,
+							   EquivalenceMember *parent_em,
+							   Relids child_relids)
+{
+	/* 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);
+	}
+
+	/*
+	 * Next, we try to translate join rels.
+	 */
+	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);
+	}
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3019,7 +3414,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 {
 	List	   *result = NIL;
 	bool		is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-	Relids		parent_relids;
+	Relids		parent_relids,
+				top_parent_rel_relids;
 	int			i;
 
 	/* Should be OK to rely on eclass_indexes */
@@ -3028,6 +3424,16 @@ 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);
@@ -3040,6 +3446,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3060,16 +3467,25 @@ 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.
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, cur_ec);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
+			/*
+			 * 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;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3084,8 +3500,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3303,8 +3719,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5f428e835b0..c30f0cae7d2 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3726,12 +3726,23 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+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;
 
@@ -3743,7 +3754,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3762,16 +3774,36 @@ match_pathkeys_to_index(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.
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_child_member_iterator(&it, pathkey->pk_eclass);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			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 (!bms_equal(member->em_relids, index->rel->relids))
+			if (!member->em_is_child &&
+				!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
@@ -3800,6 +3832,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1caad5f3a61..b81791f8fec 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1285,7 +1289,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1329,7 +1333,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1470,7 +1474,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1501,7 +1505,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1981,7 +1985,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2195,7 +2199,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2219,7 +2223,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2288,7 +2292,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4554,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4563,7 +4568,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4588,7 +4594,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6238,7 +6244,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6305,7 +6311,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6333,7 +6339,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6349,7 +6355,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6420,7 +6426,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6429,7 +6436,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6455,8 +6462,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6465,7 +6473,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6824,7 +6832,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6887,7 +6896,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 17e51cd75d7..734d54a7305 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..0ee1476b471 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,15 +119,21 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +154,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +202,28 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +277,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +673,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +791,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +979,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1542,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
@@ -1530,6 +1583,48 @@ 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 == 0)
+			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 54ee17697e5..dfcbd613156 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -192,6 +192,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -248,6 +250,21 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -957,6 +974,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1376,6 +1404,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1448,10 +1482,61 @@ 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);
 } 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.
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceChildMemberIterator	it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_child_member_iterator(&it, ec);
+ * 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);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 719be3897f6..b6208928bf9 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -331,6 +331,24 @@ 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 46955d128f0..5563932b1a5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -179,6 +180,15 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   EquivalenceClass *ec);
+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,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e1c4f913f84..0cca9be1b95 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -685,7 +685,9 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v30-0002-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v30-0002-Resolve-conflict-with-commit-66c0185.patchDownload
From 7270edf4d12379d717d1674d49181eed4a8a7dc6 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v30 2/4] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 53 +++++++++++++++++++++----
 src/backend/optimizer/util/inherit.c    |  8 +++-
 src/backend/optimizer/util/relnode.c    | 10 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 43711eb303a..5f81dcaf87d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3088,7 +3088,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					 * relations, skipping the update of this inverted index
 					 * allows for faster iteration.
 					 */
-					if (root->top_parent_relid_array[j] == 0)
+					if (root->top_parent_relid_array[j] == -1)
 						continue;
 					indexes->joinrel_indexes =
 						bms_add_member(indexes->joinrel_indexes,
@@ -3125,7 +3125,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3142,12 +3144,49 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		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
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 734d54a7305..08cd39a6c85 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0ee1476b471..48517ddaf86 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
@@ -1608,7 +1612,7 @@ find_relids_top_parents_slow(PlannerInfo *root, Relids relids)
 	{
 		int			top_parent_relid = (int) top_parent_relid_array[i];
 
-		if (top_parent_relid == 0)
+		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;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index dfcbd613156..62c4f7e214c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -260,7 +260,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v30-0003-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v30-0003-Introduce-indexes-for-RestrictInfo.patchDownload
From 61d00ee71b193ef5b9cfb1b67a25cc9b30f69e62 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v30 3/4] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 515 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/pathnodes.h             |  51 ++-
 src/include/optimizer/paths.h             |  21 +-
 10 files changed, 583 insertions(+), 71 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ec004ed9493..bac2252eb91 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 5f81dcaf87d..81b23b948b0 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
 											   Expr *expr, Relids relids,
 											   JoinDomain *jdomain,
 											   Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -319,7 +323,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -330,6 +333,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -350,8 +355,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -363,10 +370,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -377,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_parent_eq_member(ec1, item2, item2_relids,
 								   jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -394,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_parent_eq_member(ec2, item1, item1_relids,
 								   jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -411,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -420,8 +430,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -443,6 +453,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -584,6 +596,167 @@ add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -729,8 +902,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1134,7 +1307,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1227,6 +1400,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1236,9 +1410,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1296,9 +1470,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1308,7 +1482,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1374,7 +1549,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1431,11 +1606,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1476,11 +1652,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1870,12 +2046,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1887,12 +2067,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1954,10 +2134,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1968,9 +2149,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1981,9 +2165,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2056,7 +2244,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2782,16 +2970,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3648,7 +3839,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3736,7 +3927,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3893,3 +4084,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b33fc671775..e47952a5533 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -36,9 +36,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -445,7 +446,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -474,7 +475,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -547,19 +548,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -584,14 +594,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -607,9 +617,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -636,11 +648,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -648,7 +661,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e92e108b6b6..fce0f7c7fb1 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -661,6 +661,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 82775a3dd51..641146ec4a1 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1153,6 +1153,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index cece3a5be75..f2355cbcdbb 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -495,6 +495,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..980ab4d8bed 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,9 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -394,6 +397,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 62c4f7e214c..f3c392e9185 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -330,6 +330,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1410,6 +1416,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1428,8 +1452,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1527,14 +1553,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2684,7 +2716,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2802,6 +2839,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 5563932b1a5..ea2b7cf840b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -165,7 +167,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +207,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v30-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v30-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From f1b44a3f380bb63b0167ea3764ccb74be0c44d0b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v30 4/4] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  18 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 197 insertions(+), 175 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..21c2593e959 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_next(). If you
+ * change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 81b23b948b0..0395cee9a7c 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2046,16 +2046,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec, nominal_join_relids, true,
+								false);
+	while ((restrictinfo = eclass_rinfo_iterator_next(&iter)) != 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) &&
@@ -2063,6 +2061,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2134,11 +2133,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2149,12 +2147,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								true, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2164,14 +2161,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								false, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2181,6 +2177,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -4086,179 +4083,166 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator
+ *	  Setup a RestrictInfoIterator 'iter' so that it can iterate over
+ *	  RestrictInfos. We iterate through root->eq_sources if 'is_source' is
+ *	  true, or root->eq_derives otherwise. The members must be from 'ec' and
+ *	  satisfy "bms_is_subset(relids, rinfo->clause_relids)" if 'is_strict' is
+ *	  true, or "bms_overlap(relids, rinfo->clause_relids)" otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
+							EquivalenceClass *ec, Relids relids,
+							bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator(). NULL is returned if there are no
+ *	  members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
 
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
+
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index f3c392e9185..cef24c10459 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1548,6 +1548,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ea2b7cf840b..a91fb41335a 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -207,18 +207,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+extern void setup_eclass_rinfo_iterator(RestrictInfoIterator *iter,
+										PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+										Relids relids,
+										bool is_source,
+										bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 0cca9be1b95..c7f5a3558c8 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2473,6 +2473,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

v19-rebased.txttext/plain; charset=US-ASCII; name=v19-rebased.txtDownload
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b92e2a0fc9f..4d563c58bdc 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7770,6 +7770,10 @@ conversion_error_callback(void *arg)
 				varno = var->varno;
 				colno = var->varattno;
 			}
+			/*
+			 * XXX why don't we break here? Surely there can't be another
+			 * equal EquivalenceMember?
+			 */
 		}
 
 		if (varno > 0)
@@ -7828,26 +7832,29 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *em;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
 
-	foreach(lc, ec->ec_members)
-	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+	setup_eclass_member_iterator(&iter, root, ec, rel->relids, true, false);
 
+	while ((em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
 		 */
+		Assert(!bms_is_empty(em->em_relids));
+
 		if (bms_is_subset(em->em_relids, rel->relids) &&
-			!bms_is_empty(em->em_relids) &&
 			bms_is_empty(bms_intersect(em->em_relids, fpinfo->hidden_subquery_rels)) &&
 			is_foreign_expr(root, rel, em->em_expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -7874,7 +7881,9 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc1);
 		Index		sgref = get_pathtarget_sortgroupref(target, i);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *em;
+		Relids		expr_relids;
 
 		/* Ignore non-sort expressions */
 		if (sgref == 0 ||
@@ -7889,19 +7898,20 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
+		expr_relids = pull_varnos(root, (Node *) expr);
+		setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids,
+											false, false);
+
 		/* Locate an EquivalenceClass member matching this expr, if any */
-		foreach(lc2, ec->ec_members)
+		while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Expr	   *em_expr;
 
-			/* Don't match constants */
-			if (em->em_is_const)
-				continue;
+			/* don't expect constants */
+			Assert(!em->em_is_const);
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* don't expect child members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
@@ -7913,10 +7923,15 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 
 			/* Check that expression (including relabels!) is shippable */
 			if (is_foreign_expr(root, rel, em->em_expr))
+			{
+				bms_free(expr_relids);
+				eclass_member_iterator_dispose(&iter);
 				return em;
+			}
 		}
-
 		i++;
+		bms_free(expr_relids);
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	return NULL;
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index 1597b8e8127..d093871427d 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1684,6 +1684,22 @@ list_sort(List *list, list_sort_comparator cmp)
 		qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp);
 }
 
+/*
+ * list_sort comparator for sorting a list into ascending ptr order.
+ */
+int
+list_ptr_cmp(const ListCell *p1, const ListCell *p2)
+{
+	void	   *v1 = lfirst(p1);
+	void	   *v2 = lfirst(p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
 /*
  * list_sort comparator for sorting a list into ascending int order.
  */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..94ce706c6c9 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,11 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_member_indexes);
+	WRITE_BITMAPSET_FIELD(ec_nonchild_indexes);
+	WRITE_BITMAPSET_FIELD(ec_norel_indexes);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
@@ -573,6 +576,9 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_BOOL_FIELD(lateral);
 	WRITE_BOOL_FIELD(inFromCl);
 	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BITMAPSET_FIELD(eclass_member_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_source_indexes);
+	WRITE_BITMAPSET_FIELD(eclass_derive_indexes);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 64d3a09f765..78960e4f475 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -434,6 +434,9 @@ _readRangeTblEntry(void)
 	READ_BOOL_FIELD(lateral);
 	READ_BOOL_FIELD(inFromCl);
 	READ_NODE_FIELD(securityQuals);
+	READ_BITMAPSET_FIELD(eclass_member_indexes);
+	READ_BITMAPSET_FIELD(eclass_source_indexes);
+	READ_BITMAPSET_FIELD(eclass_derive_indexes);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ec004ed9493..bac2252eb91 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7cafaca33c5..ec4669c23bf 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,8 +32,12 @@
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
-
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static EquivalenceMember *add_eq_member(PlannerInfo *root,
+										EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										EquivalenceMember *parent,
@@ -310,7 +314,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -321,6 +324,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -341,8 +346,16 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_member_indexes = bms_add_members(ec1->ec_member_indexes,
+												 ec2->ec_member_indexes);
+		ec1->ec_nonchild_indexes = bms_add_members(ec1->ec_nonchild_indexes,
+												   ec2->ec_nonchild_indexes);
+		ec1->ec_norel_indexes = bms_add_members(ec1->ec_norel_indexes,
+												ec2->ec_norel_indexes);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -354,10 +367,12 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_member_indexes = NULL;
+		ec2->ec_nonchild_indexes = NULL;
+		ec2->ec_norel_indexes = NULL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -368,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
+		em2 = add_eq_member(root, ec1, item2, item2_relids,
 							jdomain, NULL, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -385,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
+		em1 = add_eq_member(root, ec2, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -402,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -411,8 +430,11 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_member_indexes = NULL;
+		ec->ec_nonchild_indexes = NULL;
+		ec->ec_norel_indexes = NULL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -421,9 +443,9 @@ 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,
+		em1 = add_eq_member(root, ec, item1, item1_relids,
 							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
+		em2 = add_eq_member(root, ec, item2, item2_relids,
 							jdomain, NULL, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
@@ -434,6 +456,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -509,16 +533,64 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 	return expr;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_source_indexes = bms_add_member(rte->eclass_source_indexes,
+													source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_derive_indexes = bms_add_member(rte->eclass_derive_indexes,
+													derive_idx);
+	}
+}
+
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+add_eq_member(PlannerInfo *root, EquivalenceClass *ec, Expr *expr, Relids relids,
 			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
+	Relids		expr_relids;
+	int			em_index = list_length(root->eq_members);
+	int			i;
 
 	em->em_expr = expr;
+	em->em_index = em_index;
 	em->em_relids = relids;
 	em->em_is_const = false;
 	em->em_is_child = (parent != NULL);
@@ -526,6 +598,23 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
 
+	/*
+	 * We must determine the exact set of relids in the expr for child
+	 * EquivalenceMembers as what is given to us in 'relids' may differ from
+	 * the relids mentioned in the expression.  See add_child_rel_equivalences
+	 */
+	if (parent != NULL)
+		expr_relids = pull_varnos(root, (Node *) expr);
+	else
+	{
+		expr_relids = relids;
+		/* We expect the relids to match for non-child members */
+		Assert(bms_equal(pull_varnos(root, (Node *) expr), relids));
+	}
+
+	/* record the actual relids from 'expr' */
+	em->em_norel_expr = bms_is_empty(expr_relids);
+
 	if (bms_is_empty(relids))
 	{
 		/*
@@ -544,9 +633,32 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	else if (!parent)			/* child members don't add to ec_relids */
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_nonchild_indexes = bms_add_member(ec->ec_nonchild_indexes, em_index);
 	}
+
+	/* add the new member to the list */
 	ec->ec_members = lappend(ec->ec_members, em);
 
+	/* and add it to the index and PlannerInfo's list */
+	ec->ec_member_indexes = bms_add_member(ec->ec_member_indexes, em_index);
+	root->eq_members = lappend(root->eq_members, em);
+
+	/* record exprs with no relids */
+	if (bms_is_empty(expr_relids))
+		ec->ec_norel_indexes = bms_add_member(ec->ec_norel_indexes, em_index);
+
+	if (parent != NULL)
+		expr_relids = bms_add_members(expr_relids, relids);
+
+	i = -1;
+	while ((i = bms_next_member(expr_relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rte->eclass_member_indexes =
+			bms_add_member(rte->eclass_member_indexes, em_index);
+	}
+
 	return em;
 }
 
@@ -603,6 +715,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 * Ensure the expression exposes the correct type and collation.
 	 */
 	expr = canonicalize_ec_expression(expr, opcintype, collation);
+	expr_relids = pull_varnos(root, (Node *) expr);
 
 	/*
 	 * Since SortGroupClause nodes are top-level expressions (GROUP BY, ORDER
@@ -616,7 +729,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +745,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, expr_relids,
+											true, true);
 
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +768,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -670,8 +787,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_member_indexes = NULL;
+	newec->ec_nonchild_indexes = NULL;
+	newec->ec_norel_indexes = NULL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -684,12 +804,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	if (newec->ec_has_volatile && sortref == 0) /* should not happen */
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-	/*
-	 * Get the precise set of relids appearing in the expression.
-	 */
-	expr_relids = pull_varnos(root, (Node *) expr);
-
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+	newem = add_eq_member(root, newec, copyObject(expr), expr_relids,
 						  jdomain, NULL, opcintype);
 
 	/*
@@ -721,7 +836,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		int			ec_index = list_length(root->eq_classes) - 1;
 		int			i = -1;
 
-		while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(newec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -731,7 +846,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
 			if (rel == NULL)	/* must be an outer join */
 			{
-				Assert(bms_is_member(i, root->outer_join_rels));
+				Assert(i == 0 || bms_is_member(i, root->outer_join_rels));
 				continue;
 			}
 
@@ -760,19 +875,25 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root,
+							 EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator iter;
+	Relids		expr_relids;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	expr_relids = pull_varnos(root, (Node *) expr);
+	setup_eclass_member_strict_iterator(&iter, root, ec, expr_relids, true,
+										true);
+
+	while ((em = eclass_member_iterator_strict_next(&iter)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -797,10 +918,13 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, expr))
-			return em;
+			break;
 	}
 
-	return NULL;
+	bms_free(expr_relids);
+	eclass_member_iterator_dispose(&iter);
+
+	return em;
 }
 
 /*
@@ -938,7 +1062,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1025,7 +1149,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1082,7 +1206,7 @@ generate_base_implied_equalities(PlannerInfo *root)
 		 * this is a cheap version of has_relevant_eclass_joinclause().
 		 */
 		i = -1;
-		while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+		while ((i = bms_next_member(ec->ec_relids, i)) >= 0)
 		{
 			RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -1118,6 +1242,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1127,9 +1252,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1141,9 +1266,11 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * machinery might be able to exclude relations on the basis of generated
 	 * "var = const" equalities, but "var = param" won't work for that.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_norel_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 		if (cur_em->em_is_const)
 		{
@@ -1187,9 +1314,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1199,7 +1326,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1212,7 +1340,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 										  EquivalenceClass *ec)
 {
 	EquivalenceMember **prev_ems;
-	ListCell   *lc;
+	Bitmapset  *matching_ems;
+	int			i;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -1225,9 +1354,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
-	foreach(lc, ec->ec_members)
+	matching_ems = bms_difference(ec->ec_member_indexes, ec->ec_norel_indexes);
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		int			relid;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
@@ -1265,7 +1397,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1289,11 +1421,15 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	 * for this eclass.  Perhaps this could be improved by doing some
 	 * pre-analysis of which members we prefer to join, but it's no worse than
 	 * what happened in the pre-8.3 code.  (Note: rebuild_eclass_attr_needed
-	 * needs to match this code.)
+	 * needs to match this code.)  We're able to make use of
+	 * matching_ems from above.  We're not going to find Vars in
+	 * em_const_indexes.
 	 */
-	foreach(lc, ec->ec_members)
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 		List	   *vars = pull_var_clause((Node *) cur_em->em_expr,
 										   PVC_RECURSE_AGGREGATES |
 										   PVC_RECURSE_WINDOWFUNCS |
@@ -1302,6 +1438,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
 	}
+	bms_free(matching_ems);
 }
 
 /*
@@ -1322,11 +1459,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1367,11 +1505,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1559,6 +1697,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 										Relids outer_relids,
 										Relids inner_relids)
 {
+	EquivalenceMemberIterator iter;
+	EquivalenceMember *cur_em;
 	List	   *result = NIL;
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
@@ -1574,10 +1714,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.
 	 */
-	foreach(lc1, ec->ec_members)
-	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+	setup_eclass_member_iterator(&iter, root, ec, join_relids, true, false);
 
+	while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1734,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 			new_members = lappend(new_members, cur_em);
 	}
 
+	eclass_member_iterator_dispose(&iter);
+
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
 	 * member to any one inner member, but we have to find a datatype
@@ -1691,7 +1833,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -1736,12 +1878,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1753,12 +1899,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1820,10 +1966,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1834,9 +1981,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1847,9 +1997,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1922,7 +2076,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2132,7 +2286,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids;
+	Relids		outer_relids,
+				inner_relids;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2146,6 +2301,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_leftop(rinfo->clause);
 		innervar = (Expr *) get_rightop(rinfo->clause);
 		inner_datatype = right_type;
+		outer_relids = rinfo->left_relids;
 		inner_relids = rinfo->right_relids;
 	}
 	else
@@ -2153,6 +2309,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		outervar = (Expr *) get_rightop(rinfo->clause);
 		innervar = (Expr *) get_leftop(rinfo->clause);
 		inner_datatype = left_type;
+		outer_relids = rinfo->right_relids;
 		inner_relids = rinfo->left_relids;
 	}
 
@@ -2160,8 +2317,10 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 		bool		match;
-		ListCell   *lc2;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2176,10 +2335,12 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 			continue;
 		/* Does it contain a match to outervar? */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+		setup_eclass_member_strict_iterator(&iter, root, cur_ec, outer_relids,
+											false, true);
+
+		while ((cur_em = eclass_member_iterator_strict_next(&iter)) != NULL)
+		{
 			Assert(!cur_em->em_is_child);	/* no children yet */
 			if (equal(outervar, cur_em->em_expr))
 			{
@@ -2187,6 +2348,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				break;
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
+
 		if (!match)
 			continue;			/* no match, so ignore this EC */
 
@@ -2196,13 +2359,15 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		 * constant before we can decide to throw away the outer-join clause.
 		 */
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
 
+			cur_em = list_nth_node(EquivalenceMember, root->eq_members, i);
+
 			if (!cur_em->em_is_const)
 				continue;		/* ignore non-const members */
 			eq_op = select_equality_operator(cur_ec,
@@ -2272,11 +2437,12 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
 		EquivalenceMember *coal_em = NULL;
+		Bitmapset  *matching_ems;
 		bool		match;
 		bool		matchleft;
 		bool		matchright;
-		ListCell   *lc2;
 		int			coal_idx = -1;
+		int			i;
 
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
@@ -2303,10 +2469,14 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * 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);
 		match = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			coal_em = (EquivalenceMember *) lfirst(lc2);
+			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))
 			{
@@ -2333,7 +2503,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 
 				if (equal(leftvar, cfirst) && equal(rightvar, csecond))
 				{
-					coal_idx = foreach_current_index(lc2);
+					coal_idx = i;
 					match = true;
 					break;
 				}
@@ -2349,9 +2519,11 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * decide to throw away the outer-join clause.
 		 */
 		matchleft = matchright = false;
-		foreach(lc2, cur_ec->ec_members)
+		i = -1;
+		while ((i = bms_next_member(cur_ec->ec_norel_indexes, i)) >= 0)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *cur_em = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			Oid			eq_op;
 			RestrictInfo *newrinfo;
 			JoinDomain *jdomain;
@@ -2399,11 +2571,28 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		 * we can throw away the full-join clause as redundant.  Moreover, we
 		 * can remove the COALESCE entry from the EC, since the added
 		 * restrictions ensure it will always have the expected value. (We
-		 * don't bother trying to update ec_relids or ec_sources.)
+		 * don't bother trying to update ec_relids or root's eq_sources.)
 		 */
 		if (matchleft && matchright)
 		{
-			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			/* XXX performance of list_delete()?? */
+			cur_ec->ec_members = list_delete(cur_ec->ec_members, coal_em);
+			cur_ec->ec_member_indexes = bms_del_member(cur_ec->ec_member_indexes,
+													   coal_idx);
+			cur_ec->ec_nonchild_indexes = bms_del_member(cur_ec->ec_nonchild_indexes,
+														 coal_idx);
+			/* no need to adjust ec_norel_indexes */
+
+			/* Remove the member from each of the relations */
+			i = -1;
+			while ((i = bms_next_member(coal_em->em_relids, i)) >= 0)
+			{
+				RangeTblEntry *rte = root->simple_rte_array[i];
+
+				rte->eclass_member_indexes =
+					bms_del_member(rte->eclass_member_indexes, coal_idx);
+			}
+
 			return true;
 		}
 
@@ -2498,13 +2687,16 @@ bool
 exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 {
 	ListCell   *lc1;
+	Relids		item1_relids = pull_varnos(root, item1);
+	Relids		item2_relids = pull_varnos(root, item2);
 
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
 		bool		item1member = false;
 		bool		item2member = false;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			i;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
@@ -2521,9 +2713,13 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 			!list_member_oid(ec->ec_opfamilies, opfamily))
 			continue;
 
-		foreach(lc2, ec->ec_members)
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, item1_relids, false, true),
+								get_ecmember_indexes_strict(root, ec, item2_relids, false, true));
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
 			if (em->em_is_child)
 				continue;		/* ignore children here */
@@ -2585,16 +2781,21 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 															 i);
 		EquivalenceMember *item1_em = NULL;
 		EquivalenceMember *item2_em = NULL;
-		ListCell   *lc2;
+		Bitmapset  *matching_ems;
+		int			j;
 
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
 		/* It's okay to consider "broken" ECs here, see exprs_known_equal */
+		matching_ems = bms_join(get_ecmember_indexes_strict(root, ec, rel1->relids, false, false),
+								get_ecmember_indexes_strict(root, ec, rel2->relids, false, false));
 
-		foreach(lc2, ec->ec_members)
+		j = -1;
+		while ((j = bms_next_member(matching_ems, j)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, j);
 			Var		   *var;
 
 			if (em->em_is_child)
@@ -2647,16 +2848,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -2707,7 +2911,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,29 +2926,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
-		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
 
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
+		{
 			/*
 			 * Consider only members that reference and can be computed at
 			 * child's topmost parent rel.  In particular we want to exclude
@@ -2752,13 +2943,16 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * simple Var; and in any case it wouldn't produce a member that
 			 * has any use in creating plans for the child rel.
 			 */
-			if (bms_is_subset(cur_em->em_relids, top_parent_relids) &&
-				!bms_is_empty(cur_em->em_relids))
+			if (bms_is_subset(cur_em->em_relids, top_parent_relids))
 			{
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
 
+				Assert(!cur_em->em_is_const);
+				Assert(!cur_em->em_is_child);
+				Assert(!bms_is_empty(cur_em->em_relids));
+
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
 					/* Simple single-level transformation */
@@ -2787,7 +2981,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
+				(void) add_eq_member(root, cur_ec, child_expr, new_relids,
 									 cur_em->em_jdomain,
 									 cur_em, cur_em->em_datatype);
 
@@ -2795,6 +2989,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
 			}
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 }
 
@@ -2839,7 +3034,8 @@ 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);
-		int			num_members;
+		EquivalenceMemberIterator iter;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,24 +3049,21 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
 		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
+		 * Looping using the EquivalenceMemberIterator means we only loop over
+		 * the members which existed when we set up the iterator, not newly
+		 * added ones.
 		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		setup_eclass_member_iterator(&iter, root, cur_ec, top_parent_relids,
+									 false, false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			Expr	   *child_expr;
+			Relids		new_relids;
 
-			if (cur_em->em_is_const)
-				continue;		/* ignore consts here */
-
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_const);
+			Assert(!cur_em->em_is_child);
+			Assert(bms_overlap(cur_em->em_relids, top_parent_relids));
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2879,47 +3072,40 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
 				continue;
 
-			/* Does this member reference child's topmost parent rel? */
-			if (bms_overlap(cur_em->em_relids, top_parent_relids))
+			if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 			{
-				/* Yes, generate transformed child version */
-				Expr	   *child_expr;
-				Relids		new_relids;
-
-				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
-				{
-					/* Simple single-level transformation */
-					child_expr = (Expr *)
-						adjust_appendrel_attrs(root,
-											   (Node *) cur_em->em_expr,
-											   nappinfos, appinfos);
-				}
-				else
-				{
-					/* Must do multi-level transformation */
-					Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
-					child_expr = (Expr *)
-						adjust_appendrel_attrs_multilevel(root,
-														  (Node *) cur_em->em_expr,
-														  child_joinrel,
-														  child_joinrel->top_parent);
-				}
+				/* Simple single-level transformation */
+				child_expr = (Expr *)
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
+										   nappinfos, appinfos);
+			}
+			else
+			{
+				/* Must do multi-level transformation */
+				Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+				child_expr = (Expr *)
+					adjust_appendrel_attrs_multilevel(root,
+													  (Node *) cur_em->em_expr,
+													  child_joinrel,
+													  child_joinrel->top_parent);
+			}
 
-				/*
-				 * Transform em_relids to match.  Note we do *not* do
-				 * pull_varnos(child_expr) here, as for example the
-				 * transformation might have substituted a constant, but we
-				 * don't want the child member to be marked as constant.
-				 */
-				new_relids = bms_difference(cur_em->em_relids,
-											top_parent_relids);
-				new_relids = bms_add_members(new_relids, child_relids);
+			/*
+			 * Transform em_relids to match.  Note we do *not* do
+			 * pull_varnos(child_expr) here, as for example the
+			 * transformation might have substituted a constant, but we
+			 * don't want the child member to be marked as constant.
+			 */
+			new_relids = bms_difference(cur_em->em_relids,
+										top_parent_relids);
+			new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-			}
+			(void) add_eq_member(root, cur_ec, child_expr, new_relids,
+								 cur_em->em_jdomain,
+								 cur_em, cur_em->em_datatype);
 		}
+		eclass_member_iterator_dispose(&iter);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
@@ -2966,7 +3152,8 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
+		add_eq_member(root,
+					  pk->pk_eclass,
 					  tle->expr,
 					  child_rel->relids,
 					  parent_em->em_jdomain,
@@ -3039,7 +3226,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
-		ListCell   *lc2;
+		EquivalenceMemberIterator iter;
+		int			j;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3061,16 +3249,19 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&iter, root, cur_ec, rel->relids, true,
+									 false);
+
+		while ((cur_em = eclass_member_iterator_next(&iter)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 			cur_em = NULL;
 		}
 
+		eclass_member_iterator_dispose(&iter);
+
 		if (!cur_em)
 			continue;
 
@@ -3078,14 +3269,15 @@ 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)
+		j = -1;
+		while ((j = bms_next_member(cur_ec->ec_nonchild_indexes, j)) >= 0)
 		{
-			EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *other_em = list_nth_node(EquivalenceMember,
+														root->eq_members, j);
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3193,7 +3385,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3268,7 +3460,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 						  RelOptInfo *rel)
 {
 	Relids		relids;
-	ListCell   *lc;
+	int			i;
 
 	Assert(!eclass->ec_merged);
 
@@ -3281,7 +3473,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3299,12 +3491,14 @@ eclass_useful_for_merging(PlannerInfo *root,
 		return false;
 
 	/* To join, we need a member not in the given rel */
-	foreach(lc, eclass->ec_members)
+	i = -1;
+	while ((i = bms_next_member(eclass->ec_nonchild_indexes, i)) >= 0)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+		EquivalenceMember *cur_em =
+		list_nth_node(EquivalenceMember, root->eq_members, i);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* we don't expect child members here */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
@@ -3392,7 +3586,7 @@ get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
 	/* Should be OK to rely on eclass_indexes */
 	Assert(root->ec_merging_done);
 
-	while ((i = bms_next_member(relids, i)) > 0)
+	while ((i = bms_next_member(relids, i)) >= 0)
 	{
 		RelOptInfo *rel = root->simple_rel_array[i];
 
@@ -3438,3 +3632,595 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ecmember_indexes
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_overlap(em->em_relids, relids) as true.  The returned indexes
+ *		may reference EquivalenceMembers with em_relids containing members
+ *		not mentioned in 'relids'.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids,
+					 bool with_children, bool with_norel_members)
+{
+	Bitmapset  *matching_ems;
+	Bitmapset  *rel_ems = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_ems = bms_add_members(rel_ems, rte->eclass_member_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_overlap(em->em_relids, relids));
+	}
+#endif
+
+	/* remove EC members not mentioning the required rels. */
+	if (!with_children)
+		matching_ems = bms_int_members(rel_ems, ec->ec_nonchild_indexes);
+	else
+		matching_ems = bms_int_members(rel_ems, ec->ec_member_indexes);
+
+	/* now add any members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * get_ecmember_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_members for all
+ *		EquivalenceMembers in 'ec' that have
+ *		bms_is_subset(relids, em->em_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ecmember_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							Relids relids, bool with_children,
+							bool with_norel_members)
+{
+	Bitmapset  *matching_ems = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		if (!with_children)
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
+										 ec->ec_nonchild_indexes);
+		else
+			matching_ems = bms_intersect(rte->eclass_member_indexes,
+										 ec->ec_member_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			matching_ems = bms_int_members(matching_ems,
+										   rte->eclass_member_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(matching_ems, i)) >= 0)
+	{
+		EquivalenceMember *em = list_nth_node(EquivalenceMember,
+											  root->eq_members, i);
+
+		Assert(bms_is_subset(relids, em->em_relids));
+	}
+#endif
+
+	/* optionally add members with that are not mentioned by any relation */
+	if (with_norel_members)
+		matching_ems = bms_add_members(matching_ems, ec->ec_norel_indexes);
+
+	return matching_ems;
+}
+
+/*
+ * The threshold for the number of members that must be in an EquivalenceClass
+ * before we switch to searching for EquivalenceMember by using the Bitmapset
+ * indexes stored in EquivalenceClass and RelOptInfo.  We don't want to make
+ * this too low as the manipulation of Bitmapsets slows this down for
+ * EquivalenceClasses with just a few members.  The linear search becomes very
+ * slow when an EquivalenceClass has a large number of members, as can happen
+ * when planning queries to partitioned tables.
+ */
+#define ECMEMBER_INDEX_THRESHOLD 16
+
+/*
+ * setup_eclass_member_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_next to start
+ *		searching for EquivalenceMembers matching the specified parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+							 PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids, bool with_children,
+							 bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = false;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes(root, ec, relids,
+												  with_children,
+												  with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+
+}
+
+/*
+ * setup_eclass_member_strict_iterator
+ *		Setup 'iter' so it's ready for eclass_member_iterator_strict_next to
+ *		start searching for EquivalenceMembers matching the specified
+ *		parameters.
+ *
+ * Once used, the iterator must be disposed of using
+ * eclass_member_iterator_dispose.
+ */
+void
+setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+									PlannerInfo *root, EquivalenceClass *ec,
+									Relids relids, bool with_children,
+									bool with_norel_members)
+{
+	iter->orig_length = list_length(ec->ec_members);
+	iter->use_index = iter->orig_length >= ECMEMBER_INDEX_THRESHOLD;
+	iter->with_children = with_children;
+	iter->with_norel_members = with_norel_members;
+	iter->relids_empty = bms_is_empty(relids);
+#ifdef USE_ASSERT_CHECKING
+	iter->isstrict = true;
+#endif
+	iter->with_relids = relids;
+	iter->root = root;
+	iter->eclass = ec;
+
+#ifdef USE_ASSERT_CHECKING
+	if (1)
+#else
+	if (iter->use_index)
+#endif
+		iter->matching_ems = get_ecmember_indexes_strict(root, ec, relids,
+														 with_children,
+														 with_norel_members);
+	else
+		iter->matching_ems = NULL;
+
+	iter->current_index = -1;
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that an iterator that uses the index and one that does not both
+	 * return the same EquivalenceMembers
+	 */
+	{
+		EquivalenceMemberIterator idx_iter;
+		EquivalenceMemberIterator noidx_iter;
+		EquivalenceMember *em;
+		List	   *list1 = NIL;
+		List	   *list2 = NIL;
+		ListCell   *lc1,
+				   *lc2;
+
+		memcpy(&idx_iter, iter, sizeof(EquivalenceMemberIterator));
+		memcpy(&noidx_iter, iter, sizeof(EquivalenceMemberIterator));
+
+		idx_iter.use_index = true;
+		noidx_iter.use_index = false;
+
+		while ((em = eclass_member_iterator_strict_next(&idx_iter)) != NULL)
+			list1 = lappend(list1, em);
+
+		while ((em = eclass_member_iterator_strict_next(&noidx_iter)) != NULL)
+			list2 = lappend(list2, em);
+
+		list_sort(list1, list_ptr_cmp);
+		list_sort(list2, list_ptr_cmp);
+
+		Assert(list_length(list1) == list_length(list2));
+
+		forboth(lc1, list1, lc2, list2)
+			Assert(lfirst(lc1) == lfirst(lc2));
+	}
+#endif
+}
+
+/*
+ * eclass_member_iterator_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_iterator().  Returns NULL when
+ *		there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_strict_next */
+	Assert(!iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't return members which have no common rels with with_relids
+			 */
+			if (!bms_overlap(em->em_relids, iter->with_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_strict_next
+ *		Fetch the next EquivalenceMember from an EquivalenceMemberIterator
+ *		which was set up by setup_eclass_member_strict_iterator().  Returns
+ *		NULL when there are no more matching EquivalenceMembers.
+ */
+EquivalenceMember *
+eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter)
+{
+	/* Fail if this was used instead of eclass_member_iterator_next */
+	Assert(iter->isstrict);
+
+	if (iter->use_index)
+	{
+		iter->current_index = bms_next_member(iter->matching_ems,
+											  iter->current_index);
+		if (iter->current_index >= 0)
+			return list_nth_node(EquivalenceMember, iter->root->eq_members,
+								 iter->current_index);
+		return NULL;
+	}
+	else
+	{
+		ListCell   *lc;
+
+		for_each_from(lc, iter->eclass->ec_members, iter->current_index + 1)
+		{
+			EquivalenceMember *em = lfirst_node(EquivalenceMember, lc);
+
+			iter->current_index = foreach_current_index(lc);
+
+			/*
+			 * Some users of this iterator will be adding new
+			 * EquivalenceMember during the loop.  We must ensure we don't
+			 * return those, so here we return NULL when the loop index goes
+			 * beyond the original length of the ec_members list.
+			 */
+			if (iter->current_index >= iter->orig_length)
+				return NULL;
+
+			/* don't return child members when with_children==false */
+			if (!iter->with_children && em->em_is_child)
+				continue;
+
+			/*
+			 * When with_norel_members==true, make sure we return all members
+			 * without Vars.
+			 */
+			if (iter->with_norel_members && em->em_norel_expr)
+				return em;
+
+			/*
+			 * Don't match members where em_relids that don't contain all rels
+			 * mentioned in with_relids.
+			 */
+			if (iter->relids_empty ||
+				!bms_is_subset(iter->with_relids, em->em_relids))
+				continue;
+
+			return em;
+		}
+		return NULL;
+	}
+}
+
+/*
+ * eclass_member_iterator_dispose
+ *		Free any memory allocated by the iterator
+ */
+void
+eclass_member_iterator_dispose(EquivalenceMemberIterator *iter)
+{
+	bms_free(iter->matching_ems);
+}
+
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_esis = bms_add_members(rel_esis, rte->eclass_source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 rte->eclass_source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			esis = bms_int_members(esis, rte->eclass_source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		rel_edis = bms_add_members(rel_edis, rte->eclass_derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		RangeTblEntry *rte = root->simple_rte_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 rte->eclass_derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			rte = root->simple_rte_array[i];
+			edis = bms_int_members(edis, rte->eclass_derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5f428e835b0..f658041f6b0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,8 +190,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-									List **orderby_clauses_p,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+									List *pathkeys, List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 										 int indexcol, Expr *clause, Oid pk_opfamily);
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3726,8 +3726,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
-						List **orderby_clauses_p,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index,
+						List *pathkeys, List **orderby_clauses_p,
 						List **clause_columns_p)
 {
 	ListCell   *lc1;
@@ -3742,8 +3742,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
+		Bitmapset  *matching_ems;
 		bool		found = false;
-		ListCell   *lc2;
+		int			i;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3763,9 +3764,16 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		matching_ems =
+			bms_intersect(pathkey->pk_eclass->ec_member_indexes,
+						  root->simple_rte_array[index->rel->relid]
+							  ->eclass_member_indexes);
+
+		i = -1;
+		while ((i = bms_next_member(matching_ems, i)) >= 0)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *member = list_nth_node(EquivalenceMember,
+													  root->eq_members, i);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..59e3f8c136d 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1141,18 +1141,19 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			 * outer query.
 			 */
 			int			best_score = -1;
-			ListCell   *j;
+			int			j = -1;
 
-			foreach(j, sub_eclass->ec_members)
+			while ((j = bms_next_member(sub_eclass->ec_nonchild_indexes, j)) >= 0)
 			{
-				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
+				EquivalenceMember *sub_member = list_nth_node(EquivalenceMember,
+															  rel->subroot->eq_members,
+															  j);
 				Expr	   *sub_expr = sub_member->em_expr;
 				Oid			sub_expr_type = sub_member->em_datatype;
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1684,7 +1685,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 		EquivalenceClass *oeclass;
 		int			score;
-		ListCell   *lc2;
+		int			i;
 
 		/* get the outer eclass */
 		update_mergeclause_eclasses(root, rinfo);
@@ -1705,13 +1706,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 
 		/* compute score */
 		score = 0;
-		foreach(lc2, oeclass->ec_members)
+		i = -1;
+		while ((i = bms_next_member(oeclass->ec_nonchild_indexes, i)) >= 0)
 		{
-			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
+			EquivalenceMember *em = list_nth_node(EquivalenceMember,
+												  root->eq_members, i);
 
-			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+			/* shouldn't be consts or child members in ec_nonchild_indexes */
+			Assert(!em->em_is_const && !em->em_is_child);
+
+			if (!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
 
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b33fc671775..fa5eb40d34d 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -38,7 +38,7 @@ static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -474,7 +474,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -607,9 +607,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -627,18 +629,41 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 		if (bms_is_member(relid, cur_em->em_relids) ||
 			bms_is_member(ojrelid, cur_em->em_relids))
 		{
+			RangeTblEntry  *rte;
+
 			Assert(!cur_em->em_is_const);
+
+			/* Delete 'relid' from em_relids */
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
+			rte = root->simple_rte_array[relid];
+			rte->eclass_member_indexes =
+				bms_del_member(rte->eclass_member_indexes, cur_em->em_index);
+
+			/* Delete 'ojrelid' from em_relids */
 			cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
+			rte = root->simple_rte_array[ojrelid];
+			rte->eclass_member_indexes =
+				bms_del_member(rte->eclass_member_indexes, cur_em->em_index);
+
 			if (bms_is_empty(cur_em->em_relids))
+			{
+				/* Delete this EquivalenceMember with updating indexes */
 				ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+				ec->ec_member_indexes =
+					bms_del_member(ec->ec_member_indexes, cur_em->em_index);
+				ec->ec_nonchild_indexes =
+					bms_del_member(ec->ec_nonchild_indexes, cur_em->em_index);
+				ec->ec_norel_indexes =
+					bms_del_member(ec->ec_norel_indexes, cur_em->em_index);
+			}
 		}
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
 	}
@@ -648,7 +673,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1caad5f3a61..2023f8d280e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,8 +264,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-										Relids relids,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+										List *pathkeys, Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
 										int *p_numsortkeys,
@@ -273,10 +273,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
-									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
-														   List *pathkeys, Relids relids, int nPresortedCols);
+static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+									 List *pathkeys, Relids relids);
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
+														   Plan *lefttree,
+														   List *pathkeys,
+														   Relids relids,
+														   int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
 									  Plan *lefttree);
@@ -297,7 +300,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1285,7 +1288,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root,
+										  (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1329,7 +1333,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1470,7 +1474,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1501,7 +1505,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1981,7 +1985,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2195,7 +2199,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2219,7 +2223,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2288,7 +2292,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4554,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4563,7 +4568,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4588,7 +4594,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6238,7 +6244,8 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root,
+						   Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6305,7 +6312,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6333,7 +6340,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6349,7 +6356,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6420,7 +6427,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6429,7 +6437,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6455,7 +6465,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
+make_incrementalsort_from_pathkeys(PlannerInfo *root,
+								   Plan *lefttree, List *pathkeys,
 								   Relids relids, int nPresortedCols)
 {
 	int			numsortkeys;
@@ -6465,7 +6476,9 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root,
+										  lefttree,
+										  pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6824,7 +6837,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6887,7 +6901,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 64605be3178..ca5cb79a680 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -356,6 +356,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	Assert(subroot->join_info_list == NIL);
 	/* and we haven't made equivalence classes, either */
 	Assert(subroot->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	/* and we haven't created PlaceHolderInfos, either */
 	Assert(subroot->placeholder_list == NIL);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e92e108b6b6..f25a6ece2fc 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -661,6 +661,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_members = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 82775a3dd51..3bbace3329d 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1153,6 +1153,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_members = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 7c27dc24e21..8d6678e9922 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -37,7 +37,7 @@
 #include "parser/parse_coerce.h"
 #include "utils/selfuncs.h"
 
-
+static void setup_append_rel_entry(PlannerInfo *root);
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
 										  SetOperationStmt *parentOp,
 										  List *colTypes, List *colCollations,
@@ -117,6 +117,7 @@ plan_set_operations(PlannerInfo *root)
 	 * merging.  Mark that merging is complete to allow us to make pathkeys.
 	 */
 	Assert(root->eq_classes == NIL);
+	Assert(root->eq_members == NIL);
 	root->ec_merging_done = true;
 
 	/*
@@ -138,6 +139,8 @@ plan_set_operations(PlannerInfo *root)
 	leftmostQuery = leftmostRTE->subquery;
 	Assert(leftmostQuery != NULL);
 
+	setup_append_rel_entry(root);
+
 	/*
 	 * If the topmost node is a recursive union, it needs special processing.
 	 */
@@ -170,6 +173,26 @@ plan_set_operations(PlannerInfo *root)
 	return setop_rel;
 }
 
+/*
+ * setup_append_rel_entry
+ *		Add entry into root's simple_rte_array at element 0.  This is required
+ *		because generate_append_tlist() makes Vars with varno=0.  In
+ *		add_eq_member() we need to index EquivalenceMembers belonging to this
+ *		relation.
+ */
+static void
+setup_append_rel_entry(PlannerInfo *root)
+{
+	RangeTblEntry *rte = makeNode(RangeTblEntry);
+
+	memset(rte, 0, sizeof(RangeTblEntry));
+	rte->eclass_member_indexes = NULL;
+
+	/* make sure nothing else has made use of this element */
+	Assert(root->simple_rte_array[0] == NULL);
+	root->simple_rte_array[0] = rte;
+}
+
 /*
  * recurse_set_operations
  *	  Recursively handle one step in a tree of set operations
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 17e51cd75d7..a5d9357bc68 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -491,6 +491,14 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 */
 	childrte = makeNode(RangeTblEntry);
 	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	/*
+	 * We do not want to inherit the EquivalenceMember indexes of the parent
+	 * to its child
+	 */
+	childrte->eclass_member_indexes = NULL;
+	childrte->eclass_source_indexes = NULL;
+	childrte->eclass_derive_indexes = NULL;
+
 	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 38d6ad7dcbd..2be219ff36b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1247,6 +1247,15 @@ typedef struct RangeTblEntry
 	bool		inFromCl pg_node_attr(query_jumble_ignore);
 	/* security barrier quals to apply, if any */
 	List	   *securityQuals pg_node_attr(query_jumble_ignore);
+	Bitmapset  *eclass_member_indexes;	/* Indexes in PlannerInfo's eq_members
+										 * list of EquivalenceMembers that
+										 * mention this relation */
+	Bitmapset  *eclass_source_indexes;	/* Indexes in PlannerInfo's eq_sources
+										 * list for RestrictInfos that mention
+										 * this relation */
+	Bitmapset  *eclass_derive_indexes;	/* Indexes in PlannerInfo's eq_derives
+										 * list for RestrictInfos that mention
+										 * this relation */
 } RangeTblEntry;
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 54ee17697e5..8be0b00df15 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -313,6 +313,15 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of each EquivalenceMember */
+	List	   *eq_members;
+
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -950,6 +959,7 @@ typedef struct RelOptInfo
 	double		allvisfrac;
 	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
 	Bitmapset  *eclass_indexes;
+
 	PlannerInfo *subroot;		/* if subquery */
 	List	   *subplan_params; /* if subquery */
 	/* wanted number of parallel workers */
@@ -1376,6 +1386,39 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * At various locations in the query planner, we must search for
+ * EquivalenceMembers within a given EquivalenceClass.  For the common case,
+ * an EquivalenceClass does not have a large number of EquivalenceMembers,
+ * however, in cases such as planning queries to partitioned tables, the number
+ * of members can become large.  To maintain planning performance, we make use
+ * of a bitmap index to allow us to quickly find EquivalenceMembers in a given
+ * EquivalenceClass belonging to a given relation or set of relations.  This
+ * is done by storing a list of EquivalenceMembers belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each EquivalenceMember in that list
+ * which relates to the given relation.  We also store a Bitmapset to mark all
+ * of the indexes in the PlannerInfo's list of EquivalenceMembers in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.
+ *
+ * To further speed up the lookup of EquivalenceMembers we also record the
+ * non-child indexes.  This allows us to deal with fewer bits for searches
+ * that don't require "is_child" EquivalenceMembers.  We must also store the
+ * indexes into EquivalenceMembers which have no relids mentioned as some
+ * searches require that.
+ *
+ * Additionally, we also store the EquivalenceMembers of a given
+ * EquivalenceClass in the ec_members list.  Technically, we could obtain this
+ * from looking at the bits in ec_member_indexes, however, finding all members
+ * of a given EquivalenceClass is common enough that it pays to have fast
+ * access to a dense list containing all members.
+ *
+ * The source and derived RestrictInfos are indexed in a similar method,
+ * although we don't maintain a List in the EquivalenceClass for these.  These
+ * must be looked up in PlannerInfo using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1393,9 +1436,19 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_members;		/* list of EquivalenceMembers in the class */
+	Bitmapset  *ec_member_indexes;	/* Indexes into all PlannerInfos
+									 * eq_members for this EquivalenceClass */
+	Bitmapset  *ec_nonchild_indexes;	/* Indexes into PlannerInfo's
+										 * eq_members with em_is_child ==
+										 * false */
+	Bitmapset  *ec_norel_indexes;	/* Indexes into PlannerInfo's eq_members
+									 * for members where pull_varno on the
+									 * em_expr is an empty set */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1443,8 +1496,10 @@ typedef struct EquivalenceMember
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
-	Relids		em_relids;		/* all relids appearing in em_expr */
-	bool		em_is_const;	/* expression is pseudoconstant? */
+	int			em_index;		/* index in root->eq_members */
+	Relids		em_relids;		/* relids for this member */
+	bool		em_is_const;	/* is em_relids empty? */
+	bool		em_norel_expr;	/* true if em_expr contains no Vars */
 	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 */
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index a872fc501e0..7bc29752b85 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -680,6 +680,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 
+extern int	list_ptr_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 46955d128f0..140027ab21f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -116,6 +116,31 @@ extern void init_dummy_sjinfo(SpecialJoinInfo *sjinfo, Relids left_relids,
  * equivclass.c
  *	  routines for managing EquivalenceClasses
  */
+
+/*
+ * EquivalenceMemberIterator
+ *		Data structure used for iteration over an EquivalenceClass's
+ *		EquivalenceMember in order to quickly find the members matching our
+ *		search pattern.
+ */
+typedef struct EquivalenceMemberIterator
+{
+	bool		use_index;		/* use matching_ems index? */
+	bool		relids_empty;	/* is with_relids empty? */
+	bool		with_children;	/* include em_is_child members? */
+	bool		with_norel_members; /* include members with empty em_relids */
+#ifdef USE_ASSERT_CHECKING
+	bool		isstrict;		/* ensure the correct next function is used */
+#endif
+	int			orig_length;	/* elements in eclass->ec_members at the start */
+	int			current_index;	/* current iterator position, or -1. */
+	Relids		with_relids;	/* relids to match in em_relids */
+	PlannerInfo *root;			/* PlannerInfo the eclass belongs to */
+	EquivalenceClass *eclass;	/* the EquivalenceClass we're looking at */
+	Bitmapset  *matching_ems;	/* when use_index == true, these are the
+								 * matching indexes in root eq_members */
+} EquivalenceMemberIterator;
+
 typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 										  RelOptInfo *rel,
 										  EquivalenceClass *ec,
@@ -137,7 +162,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -164,7 +190,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -194,6 +221,42 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ecmember_indexes(PlannerInfo *root,
+									   EquivalenceClass *ec,
+									   Relids relids,
+									   bool with_children,
+									   bool with_norel_members);
+extern Bitmapset *get_ecmember_indexes_strict(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  Relids relids,
+											  bool with_children,
+											  bool with_norel_members);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *iter,
+										 PlannerInfo *root,
+										 EquivalenceClass *ec, Relids relids,
+										 bool with_children,
+										 bool with_norel_members);
+extern void setup_eclass_member_strict_iterator(EquivalenceMemberIterator *iter,
+												PlannerInfo *root,
+												EquivalenceClass *ec,
+												Relids relids,
+												bool with_children,
+												bool with_norel_members);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *iter);
+extern EquivalenceMember *eclass_member_iterator_strict_next(EquivalenceMemberIterator *iter);
+extern void eclass_member_iterator_dispose(EquivalenceMemberIterator *iter);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
 
 /*
  * pathkeys.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e1c4f913f84..f44ab1c2140 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -687,6 +687,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
create-tables-c.sqlapplication/octet-stream; name=create-tables-c.sqlDownload
query-c.sqlapplication/octet-stream; name=query-c.sqlDownload
#100Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#99)
6 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello all,

On Tue, Jan 7, 2025 at 3:56 PM Yuya Watari <watari.yuya@gmail.com> wrote:

Overall, v30 offers a balanced approach to both planning time and
memory usage. I would greatly appreciate any feedback, reviews, or
further suggestions.

While looking at the patches, I noticed a simpler way to implement
this optimization, so I refactored the code.

1. Reducing Redundancy

What I thought was not good in the previous patches is quoted below.
The previous approach required getting the parent representation of
the given Relids and then introducing child members if they could
satisfy certain conditions based on the parent representation. This
resulted in repetitive code changes that had a negative impact on code
maintainability.

=====
+   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);
    ...
-   foreach(lc, ec->ec_members)
+   /*
+    * If we need to see child EquivalenceMembers, we access them via
+    * EquivalenceChildMemberIterator during the iteration.
+    */
+   setup_eclass_child_member_iterator(&it, ec);
+   while ((em = eclass_child_member_iterator_next(&it)) != NULL)
    {
-       EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
        ...
+       /*
+        * 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);
=====

In the new v31 patch, these procedures have been encapsulated inside
an iterator, reducing the overall patch size by about 300 lines. A
diff between v30 and v31 is attached for quick reference. I hope this
change will make the patches easier to review.

2. Experimental Setup

I ran experiments to test the new version of the patches. In the
experiments, I tested three queries, A and B (from [1]/messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com) and C (from
[2]: /messages/by-id/CAJ2pMkZZHrhgQ5UV0y+STKqx7XVGzENMhL98UbKM-OArvK9dmA@mail.gmail.com
* Master
* v30
* v31

3. Memory Usage

The table below and the attached figure show the memory usage for the
three versions. Here, "n" is the number of partitions per table, and
"PWJ" stands for partition-wise join. For all queries, v31
demonstrated a slight reduction compared to v30.

Table 1: Memory usage (MB)
-----------------------------------------------------
Query | n | PWJ | Master | v30 | v31
-----------------------------------------------------
A | 1024 | OFF | 47.822 | 59.184 | 58.479
A | 1024 | ON | 123.348 | 134.709 | 134.005
B | 256 | OFF | 90.410 | 110.085 | 108.911
B | 256 | ON | 5198.580 | 5218.255 | 5217.081
C | 1024 | OFF | 36.854 | 38.023 | 37.787
C | 1024 | ON | 85.575 | 88.933 | 88.531
-----------------------------------------------------

4. Planning Time (installcheck)

The following tables and the attached figure show the total planning
time during installcheck. The purpose of this test is to confirm that
the patches did not cause regressions in non-partitioned or
less-partitioned cases. The results indicate that any regression in
v31 was either non-existent or negligible, and slightly smaller than
in v30.

Table 2: Total planning time during installcheck (seconds)
------------------------------------------
Version | Mean | Median | Stddev
------------------------------------------
Master | 1.009155 | 0.978412 | 0.085537
v30 | 1.010750 | 0.985674 | 0.052681
v31 | 1.008644 | 0.983096 | 0.059137
------------------------------------------

Table 3: Speedup for installcheck (higher is better)
---------------------------
Version | Mean | Median
---------------------------
v30 | 99.8% | 99.3%
v31 | 100.1% | 99.5%
---------------------------

5. Planning Time (Queries A, B, and C)

The following tables and the attached figure present the planning
times and the corresponding speedups. In these experiments, v31
outperformed v30 for both small and large sizes, and the design
changes introduced in v31 did not appear to cause any regressions.

Table 4: Planning time for query A (ms)
----------------------------------
n | Master | v30 | v31
----------------------------------
1 | 0.246 | 0.249 | 0.246
2 | 0.276 | 0.281 | 0.275
4 | 0.344 | 0.352 | 0.343
8 | 0.440 | 0.454 | 0.439
16 | 0.629 | 0.619 | 0.607
32 | 1.143 | 1.051 | 1.043
64 | 2.296 | 2.176 | 2.103
128 | 6.321 | 4.633 | 4.174
256 | 18.530 | 11.180 | 11.123
384 | 35.876 | 17.286 | 16.595
512 | 67.345 | 24.072 | 23.608
640 | 93.604 | 31.745 | 30.681
768 | 151.207 | 39.276 | 38.428
896 | 265.949 | 55.430 | 53.876
1024 | 310.239 | 56.925 | 55.630
----------------------------------

Table 5: Speedup of query A (higher is better)
------------------------
n | v30 | v31
------------------------
1 | 98.6% | 99.9%
2 | 98.1% | 100.3%
4 | 97.6% | 100.1%
8 | 97.0% | 100.3%
16 | 101.5% | 103.6%
32 | 108.8% | 109.6%
64 | 105.5% | 109.2%
128 | 136.4% | 151.4%
256 | 165.7% | 166.6%
384 | 207.5% | 216.2%
512 | 279.8% | 285.3%
640 | 294.9% | 305.1%
768 | 385.0% | 393.5%
896 | 479.8% | 493.6%
1024 | 545.0% | 557.7%
------------------------

Table 6: Planning time for query B (ms)
-----------------------------------
n | Master | v30 | v31
-----------------------------------
1 | 12.074 | 12.099 | 11.981
2 | 11.617 | 11.636 | 11.503
4 | 12.102 | 12.026 | 11.876
8 | 13.325 | 12.857 | 12.688
16 | 16.142 | 14.673 | 14.436
32 | 22.995 | 18.165 | 17.775
64 | 44.591 | 27.220 | 26.690
128 | 127.826 | 51.541 | 50.088
256 | 840.771 | 135.511 | 132.788
-----------------------------------

Table 7: Speedup of query B (higher is better)
-----------------------
n | v30 | v31
-----------------------
1 | 99.8% | 100.8%
2 | 99.8% | 101.0%
4 | 100.6% | 101.9%
8 | 103.6% | 105.0%
16 | 110.0% | 111.8%
32 | 126.6% | 129.4%
64 | 163.8% | 167.1%
128 | 248.0% | 255.2%
256 | 620.4% | 633.2%
-----------------------

Table 8: Planning time for query C (ms)
------------------------------------
n | Master | v30 | v31
------------------------------------
1 | 0.272 | 0.273 | 0.273
2 | 0.386 | 0.386 | 0.385
4 | 0.537 | 0.534 | 0.535
8 | 0.842 | 0.837 | 0.832
16 | 1.585 | 1.567 | 1.562
32 | 3.265 | 3.224 | 3.176
64 | 6.914 | 6.446 | 6.896
128 | 15.906 | 13.356 | 14.509
256 | 35.962 | 31.218 | 31.112
512 | 92.177 | 67.573 | 66.822
1024 | 356.469 | 151.058 | 148.820
------------------------------------

Table 9: Speedup of query C (higher is better)
------------------------
n | v30 | v31
------------------------
1 | 99.5% | 99.7%
2 | 100.0% | 100.2%
4 | 100.4% | 100.2%
8 | 100.7% | 101.3%
16 | 101.2% | 101.5%
32 | 101.3% | 102.8%
64 | 107.3% | 100.3%
128 | 119.1% | 109.6%
256 | 115.2% | 115.6%
512 | 136.4% | 137.9%
1024 | 236.0% | 239.5%
------------------------

6. Conclusion

Compared to previous versions, v31 reduces code size while maintaining
(or slightly improving) high speedups and low memory consumption. I
would greatly appreciate any feedback, reviews or further suggestions.

[1]: /messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com
[2]: /messages/by-id/CAJ2pMkZZHrhgQ5UV0y+STKqx7XVGzENMhL98UbKM-OArvK9dmA@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

diff-v30-v31.txttext/plain; charset=US-ASCII; name=diff-v30-v31.txtDownload
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,
figure.pngimage/png; name=figure.pngDownload
�PNG


IHDRa��sRGB���gAMA���a	pHYs2�2�z�����IDATx^���\�}'��|�� �%`I
�(	�him��E�Ls�
��/��Q�K��V,�njW�\W3�]�ZNaW5������-I�lm����YL����\��:+�2 �����aRA�5����!���~O���Su����sN��48�����7%`���m[:u�T��f||<�<y2���hM~F����rX�m��N�:�{)�r�-��[o�=��=����'����������'sX�
�*S4���F�nF���
�A0B
�4#D��A0B
�4#D��A0B
�4#D��A0B
�4#D��A0B
�4#D��A0B
�4#D��A0B
�4#D��A0B
�4#D��A0B
�4#D��A�e���illl`���w�5�������y��������m���T�3X�z��a�Y������j�?o�cC=���|?����=��Z���
���``)&\��K����a����8^
9$���wT���`�D!P�G����Q��`���������� h*P1�"p �#������c���1@5A�@���P�:�q6@��w��Sn���m���S�r/����t�����!
��u����E1�v�J���LLL��`+��-,,�6T����:����x�k-���s5,W�����Gs��Z�����A��d~V9oh���1?o�c�=4P^6����sc�����yff&MMM�+��
V7�[��#hV��^1�(�������U��+�8n����R3��`��(��b�	
�	���
V7�[���&�@��H)�J����"`p�c����������J4@W8p ��t����`�	�+&&&r�A&h������������+M�P���b���M�w�.
ccc�
1.�����V.o�����������������d����c�r���y�c��e��Z*���y���7o���C=���[�x���&�f�Yk�zz��Y���{���?1����_b��yS=};�L��n-����h{�u����m�����:��Z�rjmw����r���M����,��|��g�Q=O�n��B�_}~-1����R���~~��������F��S�������c�v���e����������m����������>���m���^X��(��b=j�C����������=�O��Ue|||)��_1D333�]���k��<E��:���Z�FC��������z�����v��b�V��Po��YV'j�C��W��bh�������t�c\�F���P��f�ZV�V��J\�������������+���z�ARo{����<ob��7�>�����uK|v��`�����~ku{�����M��u��b9�����i�|�u��M��Z�Ukf�*�Z���V�����53��[�?���_�\	����Yn�^F;�s[=/�!>��������ng�K�}����>���v��:��b���V���e��1}'�^��s���QX�c���F��������=k�������������>������Ojl���)���]����'N���v���[�3��<��mUlO���������T��A��Wj;*��V���|�<�4/�{�V�����1+��V��L7��b[Z=Cq���v�g���b[�9�b���������V��Z�3�mw����yQ������
���s����9��!���c��m�e��='���A:6�������o��G�������g�!
�U4��nnO!�!Vz��o��y,����B�������N���J���vt��c�~uQ�b�vU�N��v��X�N���?���nO7�����snff&�z��B��������;��Jq~����E;���s�(���~�t��q�wz���qP~���iF��'dV��q����rby�m���TW��[9M�PO[��=�lS���[�2*���s�r�����}�~����ZY�N��+�je]�����H���v,���f�#�S�����+�9V���7���]Vh�S������\�k�[�Eb�(�Tq�h����Z��^<�V��G3��*?�����5�W6�������bht�4s~�V�'4�������j+�������F��V���{�r�whe]���tl�Q��Zb���
YV������_�}`x���\v
��]������y���:����Pw}�������ZF�oV�eC�m)4�M14��rZ��n��1�J���a�}_k�f��a8��ClW��`����s�����F�h����5^o�4�-���5�c;B3���]����bhe�������:��y���X�z/�5:���W��514Z���9��[�o�u)�X�v�k��h�j���>���������71>�Zj-��m�5_�������u+���e5:o�����u�T���Sk����>	���F�V�5o���������u��-���J��G����4s�5��j����������������Po:=����54�����A+j�_�9��g����v�2h�f��o���n;�[~��gMFR<Q���JO����c.711Qzoii����{�h�<x0�.��Yo[
�lS��S�]���.���5l�����s���c�z�ms���G����M\�+}=�{;����o�c���\�?�;o�6:V�??�9����f��F��f�/�9Xo]b�Zyry�b�=�8�3���~mtm�f��M�������B��{��h��������Q�{�����~L[���8q"�z+����|��}����>�'��z����1�<��tr<�)���X�x���>�?��T[�g}�c���Q�h/W<S�Q���t���W�N�B�m�W��0���z��QqO����G1O��UL733�{�������le5�v%��~o�r����X�F�]�V+���r��f�}�����_�i�������]o��c��5U������r�{��x���Ob�N��*�o�|�i��{���P�<�}��������vP5
�k�������;��?#���� �j�!0�
����z��U��iG��\�+�jF�}1�O��F!�W$�t��)L�'����:-�i���+DZ�����Q/�����BL_���[��7��8���zO���b�z�C3O.��zE��|����A��w�^q/\���Q�����dn5/�����n?/�����=|9���~lO;���v��z���'�s����9h?��T2�A���zE\�#U�W���'"6Ro��-
����,�e�G����X���z�+�0L���uj�8o9��A���q��;��]���k��i4O��a��h�w��tP-��U����tC�������v����c�m���m���j������G��Z'��f5�7ub�i���yu�3�8���	���{�r�?h?��T��&d���A�
:)�Q�P����'".'B���h�V�e��E#�\!L��N��F�����&:�����c��y�+���z���i������������M;��;���d�z�������6ur�z�3gP�=��xp
���v����KKK����\��5^�{&��_��n��t��Y'a	Ea}1�����N���XVj���R?����p4#��S�a��p�@�(@����0�S��/��E$�p���m�\�lG���8;�I��k�x����t�^���9�nqp�"���N������^<����#
�w��]*>��/�����c�TP��������1�x�
���A?6B`�J�U0Y���^A!��"��
Ra�j)�]���U����X-�e�
�~�����o���;����0�?S�����J�k8�[G�Y\��:@� \������A�w	��$hFL�'$�%�%�$�*��Z������daaa(�Hs�W���i�_E�����W�=�C������������x�-w�E�X"�066�V��j�Clg��A�:����X����|lb����~��V�����=Uq��b}���(0�!���H"
��yP�9�xz���R)``bb"�;�s�"Y���n�v�����\7B��P�V���$������S�*����0�:W�,�_�1�E��� �k����v�V��i���s$����B����������(0o��c�EX�C�
�1LMM���A:��T�Z�(e;<9}�("n�0��?���m����FU;��Y<0���>��1+�_V�\m\g����?,�&��+`�V�(b(
����~��(kwT�9�2�O�e�9����0<"�*��:Q:����,P(�������}q���"hXu��<�:-��kWn1�s���TKKK������<�=�/���[DG�@t�wLFE��q��;�<�[� 4t���|n�WQ�n�yCD!TQq��QEA�1g%y�-+��o�r�Bat���-�#SSS�{J'��]��aE�U����������0����/���
�������p�`4�M�&�-�����("
�b`88������s�o��?�4�^<�j����(P��������h6����e*C��r�����yX�9�rl�Z(BB�����NB<��4#frr2�.7�Ol�������\���s�Y-���s�7����q�{q�1�w�"x`9������o�F�Q(P��r5�
�?/o���|l�C
������r@����@���W� ��SG���FE�4�4S��1���v���9G#���a>ozyM
{p���nq|��[:������<�[W��j7P`P�:��������
�ggg��������c�M-����(4#����U�	�V� ���~�9�V����f
�hF���������*^�.�����}����W�y����(�n'``��������j_q�b���a�
���a=6������;rX)�`���@!�{����dn���r�=+�@gsz��=��sf���.���n(�b\�$�^�Wt���v�}��E������xp��^�;*�'���{]�������n�[�fffr�r�����;��}�V�����F�r��%�A0��(tZ��MMM�V����N#�\tRtHw8��[�{X�s�Y�Tx���W\��{S��?�C���8/�mS'����<x0�����O&/����aR/p����F��A���}{n]��u��3�^�������zl��h�w���A��
�D���z
Q�����{a'E
�Mt�-1o7
H�/��^����v��N�WFKW��Y������W�X��B����N��N�����>~���{�`�����Y�����i������������~Vu��k���w��M�{�}A%��4#��S���#�(P�W�s������z�'E��b����������i���9G�=U��b�F����+tK�mj��j�=�S�\�����{�`�W����W����Po:������K��#�9�q����:��4h?���4���������Q�u�����
��DAx3����i�	���?�y�c8(8|�9+!�S�������9�hU��*�Z��`�����#tS�mj���V!o��H7����t��x05���D���P�:�w�+�����:,�
��{A�"�V����:�W0?h���0����-����4#����(:��F��G1C�B�(�8z�h���(�(��*5*l��,��>�������^�S�����+���4zp�����qo�'��x?��F?��v[�mj��j�=�U����I�+�,����i�(����zE�qM��.��7�4���3�S��C�N�cy��g�E���f�g��,�s���A3h?���4Zv�uzG���FOgQ���Q�P��a��V���R���m�������X~���za�
7�=������+���������A3���������[�s������v������5Ul_���������c�u}wRMc���g��g�����R����;Z|�rR�P'N����5SP^�s�w�����
�_j������<������X6������S���w��M�u�k	�A0����QAP!��a9������
�*MNN���������X�(��mO@4*Tbe9���f��B�k�PO3��J����s��j�c7�zM5��5������B4��x0��o�F���l�)��\w�����W�:W��g%�����ub_�����������1��&�=>�����@�J�
�**�'��[H�C�
�U?}44[���XF�C-�>��s�Y)q�tz�����o�H�7q����/t���[�k��m��5��������N�Up��/�'q_��8F����=/j_w��*T�{�P�f
�WZ��G�c_���>H?���4�����k����h����(N���}�h=Qp�I�F���64S�������c�Ji��,������\�Nq���vn���w���[��h������k*��lg]b\�����x��}��k�R'�����U����5�mv�gggskp�v���s<�{t3���� ���c�]Kp�u����2Q��
�G�Q�P�x"���1���,N�e�]�����s�J���x������b�9���8��|*��Z�_q��9���2(D�a3�`�<c�8�)�������5�����e��?[b�V�_����
��^<x�k(�Kq�:.�8f1Mq=uz������P��]�����X������=���4�w��i�x��1m���+�����������d�M|v����7����[�����m���S�r/����t����`������%Q�i�%�s/F��-��Y�_� h`�,..����o��[��{10�
,#�Pw��]fggKC���V�8q"�X�{1@��-�)��U`��m���S�����x:y�d���(d��cG�]����&&&r�uccc�u9����{1@�����5��:��v��zO�����-*�t���6MOO�V�j=�;l��=�h�{1@�
4a��]�u���w�Vs���ccc�w�zO����[
4�����u�c����Uggg�����5���������[��^�cKo�m`��m[:u�T��4>>�N�<�{t"������|�v3��(l�m���t��-��Y�_XF<I��p��(h-�f(lh�{1@�
� 
\gffr�s
[Z�^�A-���JKKKM=Q��(��e(lh�{1@���������m��t����Ki||<�<y2����������|����c��JQ;99��o��������w0z
�*���V����5��`��"hF��!�`��"hF��!�`��"hF��!�`��"hF��!�`��"hF��!�`��"hF��!�`��"h��������JC�W����K��t�����skeE���c�r�^40�v��=��2�c����W
������B�C����������[9���i����@�	AQ�?(!;v�H���c�5A#$
�#d`
�gggK!����Q�_2�k������{�acccizz:�)������
������B�=z4��'�*E����R����c�%A#f�
�W*�`�	E�� �G�����A#`jjj ����%hF��R<:
X5�m��N�:�{)�����'O�\iqq1���#�RZXXH��_�w�N��+�w����=Zj�.�@��[�.����`��U��*A�y������>�{��a����w�3�z��`�����2"d���^J2|�a&h�p���������_~9���	�������Ue��m���S�����x:y�d�����;r/����411�{��{��t���R{��]������ ����g��^J7nLk���=��Y�n]n���-��4��?�U��S4�y�����/@/����5��`��"hF��!�`��"h������������3X�������j~��~-o%�
A0B
�4#D������HKKKo
�o����e�F�G�}kY���
�4#D��A0B
�4#D��A0B
�4#D�@��x==���,��0[zSn���m���S�r/����t�����u���t����Ki���i��u�7����������r��w��m:E������s�9�^����s���)����t��y����f��yl������[�>�[��#hV�!h`�jyt�K����K��������t�������`��U�Bo<��a��������n�-X��u~��G��2�zC���4�/���H_�w��h����>�%����4p������]�������������Fz������4@7���A���c@�
A�G�0V&h��W�c��
0���_�V�\8����M�^J�^5�n���t�uW�1��p������W^�t=��gf�u��{�����rn@��nF��Xe�1 �����#h+4��
�Z}������{)�v���!�x�������O�������`��U�Bo<��a��������������_����������������~pcn��r���A����/�o\wUz�/�-����^L�]x��^���4�/��R��-��4��?��40x
\)B
>��?������_W�]��������Gr/�w���t����^}���jz��Ws/�_�/���*�`p����5��^x���Uv�5��TOW�X)���}����_}=����z9�R
40����~������_y#���.������o�'�x��
4O��2�����VJ�]x=�p����-���
���9�|�����W�����/~1}�k_+�F?��;w.O	�#h`�w~*���x���a1��7������[@'|����/9=���W
D?�G��C=���X��������>�Rz����������bz��W��_}�������b���?�{@����o�����k��������
4�=�>�n�}{��=w�������<�r�5��b����C=�}���+[�~}�����-��Rz�~�����=�����rX�m��N�:�{)�����'O���.��g��^J�7oN������u�=��x �V����{�n@������3_���K��_����n�=�[~|���o���W�>�y!��'�x"����g�Msss����W_]
������K^z�������j����{��7��{�s����?�[��#hV�!h����O�Gy�T���
\I����g������o�|']8�X���M�K���ci��O����Cy,�~�������B����M�j�"l���s��;;v�H�����{0��nF���
��#G��-[��={���2���0j�o�p��oL���L�_<�!�1^�tO�sT��_�0d ��1P���i0P���o_0���+�}cc����k��Vc�N�jl�2�z��mK�N����������'sh�����gs/���7�u��������{�<�{���k����~������m��wp>}��{)}���>�������?���_���K�W~�O�;�������'������O������K��[ni*D������'����������w�+����[o�-~~��gM~h[�w{��_�����hF�t��Z��d�a&hh��3gr����Y�6m��{�
�����A���/})�.w�������o�9�Rz���K/��{�]�p�4]�r~�L���X\\Lccc�!��0;;�v�����C��t�-;��{���������%{��I����=�����g�y�n�@�<����W���},�����!q������������t���<���c���4��	�������_�
��^���C�������#G�]�q��499�{)���k���s��?�iz�����K����O~Rz��s���i���
��@��*���(���f>/��i�
(���V�M�D�@�����1�l��%����:�����P����J��v[��������g�IO>�d�5��b�;��3��J����l�B���s�l��]��/,,��Uj&<�V�@�e����w��
�(�~��'?Y
=k���diz�6A,B���s���C
"�����W611QW6�6R`���=55Uz�R3A�~w�yg�����w��6l����E?����Mw�uW�"h`@�����!���j�T�Xn]�"T��xoff&��s�Q�i��t����={��1e���7n�c�z
�(�������~���������������UV��B;���}{n��8q"�����;v�������;����U655�[�MLL�V���tn5V"@�������G�����7h���Ln�'X`u�����������KKKibb"�������j���bn�w������za�c���=��40@����G��1��L8@=�D`Bl[���c
?3��i
1�J�.�6����T�
'N����f���^V�z�v���fgg��q�^���[�Gc���m���S�r/����t����cXE1~e����BW�������������mff��P������c�r��]�v�B�����p�B:{�l���q���v���\�����o����]������>����<�R�7����/���������{�����G~�O����ws����z*}������>��O��o�9�`��[�.����`��U��N�)h 46����Du�@�������\�����~��o_��sg�����7��'�w_���s����_H��G�g���{��K����!��^���_������^J���� ���C���3�<����s/����t�M7��
6�w���q~��gM~����Q���S�#� �a�9r$�>}��p����g����j1]�����b���izz:��fffJ�����PW)��yL�.��l���6�o���k��}�?���/��Uc��m���S�����x:y�d�1�KO�/DA���D�unvv���V�?66�[�������+�^��]����Gs��8v�X��^n?]�p!�={6�R��qcZ�vm�
�������s��N�>�t�@�9�2Pi�����������&*�R��>����<�R�7����/���������{�����G~�O����ws����z*}������>��O��o�9�`��[�.����`��U��N�4P�n�*��C����o%��vu�������������{�<�{��J�@X.h �6p��w�d��������?����������3�<���K��uS��c�M����@W=���L������~�w�4�����{@/=���k_�Z����O:�z������`�����bP)��
1�i+E�+/�Z	�v������>�����xci)��w'�����N�������/�����{1ML�H��o���k����=q�Dn�����9�[����}����x��7�o�K�������b��6�`�	�
��WO�\����Dn-�z�����b�9r�P-�������X��uk[�#�<�[W��sgn�������=�{e����t�;n)
����<�6A�T�_����e-..�����o���E����{s���.	"��Z��p����\������K��k�K�oz_�n��t�uo/
��q�^!��y]�(���������s�5�v��-F���\n��K_�Rn]�e���`9�����r�l���i��ks������r��z^F��jZ\\���b�c���^J����u����s�����������*��|��V���{s�5��-�q����VJ�^��t���r�JW]s]i�B���A�LMM�V���s���i��&&&r�lzz���0??�[e��������=�|��t����V}�����F���233�[);v,���;����4�]�v���r�l����
��|V��}�r�~y���U������_?��s���4�������������355�[eE�@e(@�_�=�[W����"( �bY�����6Z>+k��������O��%g���-���
�U��ks��7�k�m���[�A\faa!����?B������+B���%���bY����|!�enn.�.��gO��o_�5'���eK���7d������K��W_���^��41�����=F��.311Q
��%��ic�fD`���L�-O���9~�xn]���CM�
�>}�4}-�<�Hn��-���[)�r�o��
�^�Bz���s��y=��BD���
���42P���zk����e/--	@���+Ex@��E�@���-[��+���7�X���bn�]�����ks��w����WV=/�E���b��H;�V�����r��7+>�z�"���T+�����������V�����r�f���M���Gr/��^}9���c�����^�Yi�v���
1O���4��������3�����{����'�1������+{�������4D�RL�0�
-��uk��b9�<Zw��k�������1��41m��h�$@["`��������9!Z36���������_M�r��Iov�r�
��_��{K��41-�v���466V
h��3gJ��|{���c�����`�'������G������(��W;K�@A������������5�4[�n0+H��5�.
$PkV��`E������=A@K�����q������zk�q@o	V���{s���o�=�j;�|�������\S����<�G�0|����/9=�����g��c���������z(�j4��}����%[�l�-��}��H���������K��	�r���t����C-��[n8t�P����z(=����W�~��t�M7�[n����J1��>�{@%A�[���[�li8�Rk���f�;w��o���W_�6m�����w�n�!�]�����5��)S���O����=� hx������3gr��V�������|'��n���t������b��
r����S�
���ukn��#�<�[eO>�dn��~���!�x?�+<��S�
W��������won���o�����^Jk�����*�;w�\i9�%��+D���3gr�?����[eO=�Tn��4���N�P���[Ka�C-��[n���+
0066�>��Pv��7�V���s�������N�PW�
T���n�a����A�P��5k��M�r�����?�\�7h������s+�_|1���K�W��J�*��
-9s��@�����V�3�<S7l B�~���+���>�[@A����[�^1tb��}o
�6n��&''s/��^{-�;w.���?M/��B�x�b�5�?��OJ�v���6m��{@A������+�v9r�4��C��b@����+�v�m�W���/�g�y&=������W�������*	V���{s���o�=�.��O~2���3����,M�&h
w�yg�����w��6l����E?����Mw�uW�"hX�����K�l��[�m��)�{��i��=yLY�c�����A�[�9�N�>�p���t�
��s�$hx������-[���n�X���>|8�9s&��o%?F���2[�n���{��Gr�A����r������[@���g�������n�$h�i���������Z�-7�����K��KzI�PW�
T���n�a����A�����!���Xz�{�������{���>�b|7����c\�@�	�����(����N���c/��;v�(M�N�@�Po�1.��i��@���9s�AC 
�k��B�G�@3���������<��igggs�A�u��+`xpQd����p����*��kWZZZzkXXX(���lp@To������q����[
3�>A,B���_�C
"P�����W611QW6�.�:� B���r�,����J��}���#G�\6�C�����{w_CB���
�T6�����T� ��'��\~ ,..��&�N�>������C���={.��t`e�F��j�E�QX�O����U699�[�8p ����Q�r{fff���G�@e��#�"4 ��l���6�o���k��}�?
'����>������;v�5�b��~������U�G�}<������s�ljj*���������U4����B��G���f�	����O��Z�s���*;t����#A�@(�"< ������'��41}e�����>�����~�7h���Ln�Wb���\2�e�������A�����"t 
��5t ���I�+������j]�}^yl;Y6����#��~���u��}�s����`E����������rO�������"`����yLt��o��[���S��0�!��^P@���3���/��o_��"h��( �0P
D�}Ql_SSSW1��vaa�n�@e�� +�i%�8q"��:	�^Vu`@��"\ �O��P��F��`9r�H)�Z���������[���y�������;s�����\`���m[:u�T��4>>�N�<�{+'��<xEAyLNN����)>/
��`�R|�����D3b{����
���a��W�,�z�"���x�Z�Z�B#���v\�p!�={6�R��qcZ�vm�
�k�����{�;}�t����T�����C�eE`�rA�8�Z9�F�_����������������~����=�~��H�������>�;������{@/=��S����f����O|"�|����m��u������w
�*3�X]����{�^�� ��b��j�B���X^� ��>f�A���k��O}�S]��
���K{�����f�����:�{�4�_�~���=�?x������^J�����>��r���O�uz���|��������[>�{@/=��3i~~>�R���7�tS��p��aCz�;�'hF���
�3�E�Q���Q����]�,��3��G����G�8by1>��������P2������O������~�R+p��Q�4R���c����b�������Z`���m[:u�T��4>>�N�<�{+#���}�}��:5��c���Kiaa���?;;����s�������V*9TJT/;TOSO�v7;_/\�p!�={6�R��qcZ�vm�
���w�o�����Z�V��:|�p��UOWy�T���z�V�s#(���~�t��_���K�����K���w��?{�?����o�^J��?Io���=���z����o~3�R��'>�n�������n�����A���[�`������ 
,P+h���5]Yt�k��t�����������7���}�q�=��x �:W�������{��^Y�t��N�>��l��{eg��I[�n��������n�j���O�����^J_�����~pc�����2��#�R���������r����O��}�k����?��t�����
�[���&�@Ol��=��",�]�^��"T���={��}�r�91}u��0������$��}�����:t������O�����G�-������:}���G�h��+����3o�c��g�����4�����0;;[z��N�8�[���v�C�����s�J���T�E�@���-[��+���7����}5�������o���c���O����4"A��)B���{w��cGi���.�����Bh���Dn�n~~>����U�?v�Xn�n��]��J��n5�Q�@ann.������o����XL���4����8x�`{��W���>��������lG� ��e�FA�:	&�7>���9�{����wo���jz���Wv������qM��;�+�F�RL�� 4�(<���Fb�������������T����������s���������>���O�m���ka��X� {�����|�r/�k�K���6�������&���5�����am����<e*�����:�`�	Bu�@�@��i������W>I����������*;x�`n�W=M�2
�U��� ��cYo�������gnnN�0����Uv�
����*�.w���J�W:{���+O�����#4`ii)��jMs���R{�G�R���iN�6"�a����w�x�2�2����e�)h6P����3>����J��:s�Li��o���y,�`{����VJ7���n�@!�b���~�����'hXq'N���K�*�	��K*����s�]���bh��
�����@���:d �:n�b��a1��;J�[n�1_��1X"0 ��������5�4[�n0��7^O?�������m��-*�{��#��� 4������� ��&&&r���B��"x�����[eE @T��z�V�SO�T�
�X^����`0>|�4D�@�`���ws���k�������^�A�@� �O���%��i��Z"4`ff&�2� ��}����_}=����z9@{��?�����477���E?��;w.��4�����*k�H��Ep@�G�@��1>�i%d�055����
�_`���*��|{����W����*���c9@g|����/9=�����g��c���������z(�j4�'N�V����sktD1����[C+��Q�_9o���S=��rj)���^v���}���#G����O_v<���&��`����������zz����W[��*����o|#����^c��/M�&hXq�O��6==�[e�
�s���p�C��={��-[��wj�ib���d��O�V�/�R7l���_OO<�J���{�����z(=����W�~��t�M7�[n����J1��>�{@%A�@�����������������s��t@w���S���������>�Rz����������bz��W��_}�������^y})O�J������Zu���t����K����N�6mJ�z���
7����][z�~����k��)��������P4������Jizz��p�����}����\�s�����?�2�n�@5a��x����o����]x==����O�\z�~��>�����|'��n���t������b��
r����S�
+njj*��v���v������.�U6;;[�6��T=�=G��Z�@A�0,���?J���s���.�:���O�VJ����2P��c��SO=�[@A�0r��:` TO�������t@����'��+���{v}&����n��xZ��}ylY�c���;R���o����?�{)�]�6������s������D���x/�YN���9r�Hn�v���477W�������o���l�78}����{��_*�7��1�m��Pt�������v�B�r`�	F,--������F������������o���E�@�	l��5����4>|��!��������s��\n���
t��7��[e/^������^�:A������lh�G8�r���e����\D�@3b��������f���i���k/h �����
������j����s���o�9�Rz���K/��{�]�p�4]�r~�L����{��Vk>�[�����V���>[7l �?����W���},������������b���X���w�0H��9�[e7nL������������;�~����^x!]�x�����k��)S��sg��iS�A@_����T������.���c�V����@��u�]���n���_|1=��3��'�,�F�RL��w�PI��s�v�*�Fq�����=p ����
(�	h����s��}���Vk�9�[�<��#�p�O~��i��������diz�6A@�=z4����^J���}	��W�X�X'�u�Nsss�Wv����j^����'����9�����{W���;�}��������a��<�,�1����l�����X�A@_LMM�����k��<�R�@t+t�2\ �_0����/�"����R)<�G��N��/})����iS���{�/�~���qc�3�����mK�N����������'so0D�@4233Sz��}{���'N�^���K����by������gs/���7�u��������{�<�{���fU��������s�l������o�=m��������t����k��w���������|���,�R��~=}���-�����_����o�����7�_��?M�x�/��z��'�H_���r/�O����[o�=������
�*3L8;;����s�7�-�40�6���	������?��������=��t��cylJ�6�/�����wO���~���X�
@����&��]DQ���B��v�*������c_M'ozj�[������S��?(Mt������������������s���1@=��711�V�@DP@�*��=*`��������|�r���.����L_�����?��}��<�,�1�+_�Jz����X������[`���m[:u�T��4>>�N�<�{�eqq��z����k�����B
�.\����=�{)m��9�[�.��=���x���[� ��`ll,�����[�6�Q���O�����^J_�����~pc������^2p������]�������������Fz������|���=�>�{@����o�G}4��w�m��O~���4��~�4gM~8"���T�A��//���e!�^5�n�qmz�/�M7]Mz�5kJ���m��t�����b��?��������"d`������nJ��rK�5��b�|0��J���������[o�6�p�U�w��^Uz����_�-�U���K����������i����w�+�p�
i��������k��&O����|:�|�A�[���V|�=���������y/�#������-M�����:��c����n\wU���B�
�t����{���;��Nn��t�M���������
6�^��S�r(bo,-����D������/�Iz����^x9=�����ibZ�uKo��~���s/��]���[TN���GJ�Z���O�VJ����2P��c��SO=�[@A���z��7�o�K�����zb��6�Z��c�������*����z9���x��t����Ki�����X�t���+-�D�����z*���3�Wv��nL������J1m�����}4��^~���j�z����{���r�����P�u��������o�����������������oLW_�����b��h���������^J?��'�WN��r���|���Uv����j�z�����40������V�������|�s�[��M�WV=/�����#���s^O/\|=�j��c�B��@���Y�6m��s�����?�\2����V�m���S�N�^J������������������������u�r�9�N�>�l�������{,�V���������t����
��}�r`e��?�?�=�|�}���H������������W^*O������?����@s~��_����7�����K��pm�a�Uy�%���zz��W�+�_����������=���z���s�p��������o?��Or/�;��#�{�����w0z
�*���7�4��?���_���{��O�i����{+�����_��Or/�u7lJ���������.�p.�Rz�+���]wu�����������^����Jo�vM���������~������w�>�������L��:�|�����{)]s�5��Za/��Rz��g�k�����t�}���	���nF���
������2����r�a!d4O�!���\}]n5r���}����a7��=��"T���_M?x���ku�@L/d:�q��499�{)�������s��?�iz�����K�����!;w�25�>60\����7�V���]��F���e��rcn���w�(}������b����]w��n����+{���3�<��|���k�+��w�yg��
�
60��jl,������K��W_���^��41�����=�U����4�/�}�y��i�����e����~�Hi:�{>��O��;w�^c�������
�
60���n�{6�VJ�\���z����_��^y����|^�=�78}����{��_*�7��1�m��Pt��w������t�w�
.�y����~6�u�]y,P���!�������JC��avv6������-���u�e����	LB�a������[ev.������$�]|�|��U�tn,���i��t����={��1e���7n�c�z
-�����r�����Ap��������Q�?==��;��^�w��Q���� ��5���`X���M���Gr/��^}9���c�����^�Yi�v���
1O���4�"\ �h��Qx^9T���Qx�}l_���^�������ib�n�
�3La�B�a�O~c<m}�M�W����K/=�di�v��6�`�	JQ���S���=�U�>H"t�_!�����]iii��aaa�4�R�N�3LaP	XYB�at��k�������1��41m��h�$�����V����Q�>Hb������P�8z�h��MLL��U�
������Q(XB�a�fl,M��������~����������K�������ibZ4�(4�.��B�������������W���~�����2P�:l��u]m�6�_B����O|0����XZZ�#������f������ h���.��SSS�'���/--]V�>�O������v����V���dn�w����*�^F3*�!�8V�Q&l�?�0�
+.
�+E�@�p�Z����N�� ���]����}�k����U!��>N������8�����K�
���F��`��8q"�R���i9d�P�����a��E�!�U|_n��8f��P�B?������2�(4�����o��=�ZW�����a��KKKm�.t�2��U����������3=����+�xi�bK�j�e�����"wjre75�h{&�0���u*UgL�������Kz���l��k�a���4���e�(G��EyH���%[�[-��$�$x����{�{��C������>��}����A�����9s&W��w�>��@���2�v'h�P��b�r���+W�����p��,%���K3� ������
�!�Jy���������?�����$�i�_�Q����
���s��������*YJx@Y����P�����.���k��y�
,��`%L�����������V���C��#%�O��v�����796����Q3�.�l����7o�.����q����mL555�����g��1i���'s7�����sK��O�8����t��3��z������������9s&���������	Wx���R(���[����455�nm�����������������`���wz�����E��k??�����599z��������Z�H��������� `���{7�z���E����q�������������_[�G�l1�������Z��������i��J�*A	�,A�����/���_������������0����?�����{}����'�������x������?b���q�J:[���&j�~L�������d<�GK�~������?�X)>��W��.���S����;�����{��]p���~���.-(�\��i�{���s��HPH^��\��������o���V��7��hi��W:��;bok}t��oS�������4/���B�����~��h��W�O�?�+6�������(`n��'dXI���g�*9��!�g�E{c]�x�����j&����;7o��]���G������.]�����]IZ�^!���)�&�_k�������;o��S�v���.��~�{���g��?�Gtuu���O��+W��l3v-
��u�V�"���MMM�[_o��F�9d����7~��~-w�C�W�������_����Ri��M��W����q�/�vK�������_���5����A���7=���q��������\`e��{7�z���E����q�������������Y_[�N�l1����������5n#-L_I9h��c��>��~-�Y��9��o����>���������?����x��7s��ML}o���_���+��{f��?����g?�A����_}���.���cWK}����0�2���O���D���X�����������G����={���/�������y/��`�����.-(O��g��lf)��2d ��:>����g?���m_B��ftl"�������$iL���S������Z��^�����o��o����HR����o�v���a��F�������,FOs�,����s���_����:PSS3�V$���cg���G��v2lG���n����0w%�m��u�����Jil�,N
h�5}u����v�N�*����~ w�R����^\�z5w���W�R�N����;w.&''���O�.n�I��]�V���r��S=v�BX��6 d����������7sQ��������Q�����:�K����4X�/�T�"��Fat���t�gj\Y�|`����0���������=:;;���C���WJ�������$h��Rx��+W�[
����'N��,�r���IzN��v2lW��������Q[���ii_���+�'�s����/|2W%�zGc`d����?�T=X�{���W����E�����b��}���MMM�����


yd�g�����(4@���sq���\-����sUr����v����J�|��R��]����_��(W��;���%w��kh.�)��,���7��_�?�.�0:o?��zG�gp,�oS����ei^�,��������3Z[[s7S��g������y3W@��f�������1]]]��8u�T�JN�8���:���������Yah,��7wu����[��47�X����y�����<���#�^�H�6�����Xq�tw������T{��!e�xWv���\e�(:w�\�J.\����U��>�o��
�����rUR��������{�q�����������#?{6��_������<`i&&&������hjj���*���w�x`Z���\[��c�������8z�h��q#w���K�r�t[i�{www�<y2w��][�+���������8}�t\�r%w3�9s&���r7����<�J�s%
��u�V�"�9---���>��������mnB"���?��~'w�/Dc����n��$
}�r�O~�����>w�b=�������G�w�C�����y(:^�h|�����?���X���o����sq����
��;�_�g��������U^[N�l1����i1}�B~�����J��\N�@ux����
a����UIZ�����������^�����R��������L�w~��������]��q�X�e���12X���;����Y����}����7?����������Gs,������:;;���#ws������.���^������
����3`CH���B��
`�R�A�����&�-�T?O�s��>����g?���m>Bf����*b��$�G�r�����B�����p�X)����0����\��r\�/df�
�����l'N�(�>}:��[���9l�5l@�������sU2��^L�=��2����������
����������9_�k���b�����V�,���s��������]�P(�+������rU��_�x1&''���t)8���+���\T+?Gi����g��
��O���x���D�"�F�����b��~�
=-n�N����4'�����/��_������������%�O�o���Q,���GsU����9�R�@OOO�J>����
(���n[�R�;7o��]���7n����t������*�i{Z�MZ�r����E9r$ZZZr�9}�s��/|������otl"~�W�Q|�������;�w~�_��z���T�?���z��o���������T_��W�����+ioo�����6<<\�������O~���3?�3���_[���G�uWH������?���gs��x���\���_����=sKc�X!�t�v}�����-u���
�����m�+��i�<���'���^�]I
x��a��s�x[2����y	lc5l@���������x�������~���j`wGs�K�`�X��K��[������E4���K����]M���m
�������)���������"w�R����7����d��w����x`v�`��haB��_=�J�G��xLN�S����W�����1`y�������pGct4��n������J�����
X������_��_������={���%�O�����?�O|�y/0A���x�b�"���s���6 d������\E�n��3d�,�
�qeO���\�USS���MV���4������*������z�
6����x�[��hk\��-*������y����W���[����=�{KR����o�v���a��F���N�8/^,�]]]���]����^aB����{��UIsC]��W=��<������^\�z5w���W�R�N��!�;w.N�>]�O�<g��8�d���]��>������\��z\�y����?����w�����������C�oS_)���W��;���`��r�J�"�����555K���[��!�fPS[�G~,w#��_��4?�X�{���W����E�����b��}���MMM�����


yd���W������
��3grl�6 d�Lv|�'r��0}����]:���U������rU��������)���gO�Jn���+��frJ��-���c3�@����q�F�6�2�����������B��n��]��#G���%w[��>��������!d`u|�����7����/�����+�s�V1���z|�X�����-�od�q��?���.���&�l�����gZ
x�w$F��&�����h�s0w����?T�m�} �v�P���~��~+���_����c��}�z><����b}�����_��b
�n���,���b6��/]���������O�ji�\��+X��4��d�����#h`{�}����7?�;�����hk���������x\�G�>���b����������:;;���#ws������.���^������
����3`�]�z5W%/^�������+����||������	��*�^�h��3T�2+�����*ijj�����U��;A������U)d���s�6���
�����*�����U�����tY0`�8v�X��y3wG��7n�nc����U�_Y�Q
��u�V�"�9---��^>��������-���������g��r�"�����#���[E�;_�����b�����������75���4��&r7��������6��7��6��'���#�� ��k_��/4wlF�������+�����o��b=�D�>p�@��/�B�f�_[�G�l1�`u�i1aB�������G�����������J�vw4�G�@���0����v��U
$��m����������������n�����������������|��`Gc�o���m`t"��������������?�����Z[[s�����������������_��r�F�l?�il(����6�������~6ws2��&&'��}=~����k>2�<���G�/Kc�X`i
�3B�j���M�����lm��-u������)��������������=z�����)�����]��?��\e��uw���\������������^����������1il�,^��X�Jw4FGs]�f�h�+���j>�p����S�N�n����h��w/<x}}}1<<\�M}�?66����O~28�;�L�����;����'O�
��
2�6��/��o�a�J�vG��C�-����4X�������-us�����4��0*�����D���K�+�������;w����������L��J��
���k��8s�Ltww����������������E�74G{����}�7�(n�N����4'�nrj�
hk\��-*����t`�������O~2w�;u�Tq<0���)���c������W�=z�h��q#w[
8y�d�J.^�X�=~�x�v1N�8�+X�B��n��]��#G���%w$����bbbB����������������/F]�����G�b���s�W���������;6��w�_{����xeos�5���j���{�r���|��X����c�})v���������y���O���'1>>��F�����]������?�c?����_[�F�l1����555�ZY~��J4�F����?����-���;�y��z.CO������������b��#h`���,���`�����
������=��zGs�S[cu~���j
������O��������|�;��o}+wsKA/��r�XK���+����#:::�5���~
�#h`�_��
�����_����]DK��hh������D��^�"��o�\�4����D����v�PF'�����xqWS����O��q�t���������+V�j
��};._��;�y��=�����~��`��}�/W%��Y�<�����y�+`!RP@Y
�.�%/�$����������q����m��	!���Lz5
���x�����\��'N�
��P(��[�rq���hii����*������]Ds����3w�-<�B���E�����hi���I�;_������E���9�,b_uC��W�o����;���.���B�������d����$l`mL=WoW<W{�K�����n�n���/_������,�����L�G����h��z9{�l>|8wlt���`y
�����:
���;����w{�uc��h�8P��2���,������~��,�l>��������7����-u��X�
u14:#��0����h��m�=��Z
���;���
�`��������sz���YC�R���{��:�������;V���x<~�8w�6�-��#h�o��!h���s�����?�n�"��|0��s7��H!������/���������X_�y8�#3����X�l�ka��:MM�����{����h�N����+��y��A1� ill��}
��;wr'h`���l?�i�	�������d��������o��~�J���Bv4/�*�i��X�t
�r�@��p��q###���
lB?�#��������������16����:�K����4X��<��C���[�����������JK	H���]���n�R�;7o��]���G����[����*9q�D�J��������(
q����E9r$ZZZr�ctl"~�W�Q|�������;�w~�_��z���Y�;_������E���9�<�k���X����XywK]�5��dMm�LN���D<.�"8�����s�j���{�V>���b�������};._����C�-x�;K������}/w�����������������?��EMM�a���;wrq���8|�p���6�k��4[�F|3`���-�Zo���OV��6������������o�=�����������Z�,7=A��0:��X��XW�;���.���7<��Fbdl�g��z��=OkB�������R�G{{{����X�������_�-x_�67A��x`K������o������)*s�j#��j-Kc�������d�������x�������U:��������.��C��,A����d\�����x�/�����j`|"���`�X��K382���n��3d�,�
�qe������ttt�����Gs�
��===�+��D�LN�5�;v,n������G���7r�>���sUr���\�T_)������y����E9r$ZZZr�ctl"~�W�Q|�������;�w~�_��z���Y�;_������E���9�<��)���O�N/j�����lm�����{��������Q�kV���D��3����������Gs�t�o����/�.���C�����<y�?�]I{{{�9H���pq����GK���;w���)=�w���]���g�����`����-�K�l1��u�@��'O�k���I���K�����������\�x1�?���a�s�>}:N�:����4AlD�����������4����������B��\���_���O����;6#Ak�z��B���cy
l]���~�g��J?�����c�	������W�6�.�j��P�3g������	H��z������jjj�<w�Wy~`v��[���oh���Es���o�Q�R���ceiN�,\kU8���x��W=��<�����={��n~�w�2��
6���E�iK������~�E��!=~i��B�/�Ic6P/X�t~a0����?�UIS�������i_���+����fjk�	����*����<��LNN�j~)��[��B_}6�c�����7sq����q�F�6��7�/�W���e[���)4�zq��k�����[Y���O��+W����8_�p�� ��=���%\�x1��;�����_����[�1��B��n��]��#G���%w���G�-y����[������{���ug4�����CO�����b���%�����b��x����������}����7?���W�6G�+���[�#�hp,w/�i�����=�ox<��h8w{Z��������F'�����E|��/���?����}�v\�|9w����g^X=���~�g��J?������Y9���q����E�={6>�;6�����<�`�4���fHV+h����[�_PP���I��9,��kA���������_����]W^;�����NK��hh������D��^����`���F�����#��/�Z"A�cht"��b�zc]M��8k�@
x�w$F����~x�yj�<�	A[Sooo<z�(w%����� mi�{���������w��]��de��
���l�
*-��-d`5-4d I�����>�.��d!A	���z�J�����mf@@m}s��S���D����6v4�.bd|2��h8�}2G����m������4O�,����������8p�@���/:::�A�6�iCCC�����|`����pI/�6�U�����R��<]�~���o�_�������W���������������s��]�XO�<������L_��y��?�����P(��[�rq���hii��������g���T���N�}��j�x![v�nv��'Q����$=_^.^;���/�����+�K����������t�"^��m����<�����=_{c]|��U��R
}x�g(w{�K�����n�n���/_�]��C���Y}===�����E1L���5w��{�������#����;V���p��s'wg��������n����A��l�����"�B�oDg��y&` -�OW��\�����{��>�Hf�W9f1�����o:h����3jj�s�j�c���eW4w(�sz�~�������i��b�MN=GCS�U�������X��{��v4��6'�5A[�{���������=���W���������/������A����~��XiayZ0�V�?���#-��H�z�������<W)8`>�����������kl���]�5���oF
Ob|tzQm����B�4w�s�V~K_l=)<���6����h�
yH}��#S�����0�r�@��p��q###���B��f�U�W[Z���W�_)��H� ���8u�T�/TJ�C
KH[��R��|�m�sU2��^L�
�nZ�7�?w%�s��K�Gv6~�`k��oS��7WK�B*-%h �>lw^��LZp�B��J�_����--f��!����y���p��8~�x�V���WsU��$`%�5����������16����:�K����4X95S[[Cm�XY����*~6Tg6�������`M����[���/d�
����:`������*YNx@������K�����R0SS����o�]������S�R])�Ms`���������4?��&hXwiyy���W������_�P�M�t�����gniLk�%�MS����������
�Bq\Y�|��frJ��-���cq����E=z4n���;6����8y�d�"�]�'N����]�t)��?������c�x�b�;w.wKs�������])����+�[i���[�r��~�U���;=��o�A�"���u
-�c���{��cCyO
H�������h�z��
����BL=Ge��������7wK��{�����]�+{����u�l`t"����{�'~�wc��~2wKw���x���rq��!?������}�v�"b��=�����LK!>�����'��������;V���p��s'w���j<x0w,VK���l���~
����[�FH*+.7h�:d ����H�E�o����;�;Akm��~�<�]Ic����m(����on��;V[u�����O�+������������;AP�:h`����h:�#�[��x�����4���<y�������^|�������?-I�;w����:h���S����;+}��wy!e����~���
c���z<kldpF�@}Cs�w~�"������:�K����4X9����[`u�����+;�P��������4^��N��\!'N��Pit�I�J���Gm��W�N��v�]I�\`��G����G�[
����_�oS��LV�/�P���B����8�]���\[��c�������8z�h��q#wlV���q�������K�.����s���Wl/^�s���na6C�@�P�[�n�.b�������bN�������_|�r�����k�y�aVG�wcb|�X7�����/�z�~������h��R�fu��b��������~6~����[�������o�\�"^��m
�3�V��,���������y�mk�C;��&�`M�N��=C����_�����������{7�z���E:t���ddd$���bxx�X�566�����b��J���;wr�������s�b������^[��G�l1��5m����2�T
9rd����}�����7��;Akfr2��;7-��eW�f7Zx��{������a�����������?wK�����ko~:w��Rz��_�Eat��cZ����z�|�������������Gs�t�o����/�N��F�����R�@��k��:h����q������ym��4 �?����
�"������Oo���h&�������7�j>3_�^�,����gB:[�����@Gc�6����4X���� dG���~�z���z�BBR����'sW�B�\�"d��a�����s5�����N�=.������E�4��+�����
���>:������pgS�xY����F!h�e-��z�j�J�w��B����k�U���P��6>6=�8���aQ���8��!�g�E{c]�x�����
Pt���\E�?>W���#dVN]}S�"F
Ob|�����B���n�\`a
#������-
���"O��*��z4��R(���1]]]��8u�T��%dVVc��\�?�c�����
���]I�\`~#��1:1������\��r\��������;���.�jn�c��Q�:d����B`�Zf��E����P��zZ�R���ceiN�,\]U�@]���`�4����e]]]q����=+Kc�N�>��gU�'��/�X���}QW�������1�{����R�����*���'r5����\Eq~cub�A[��K�����[��R���6�������W2�\�r%W��{����y�U~@���t�����gniL�����X�����Q�?l ��WV9���f�v�Z�JRH���'Tp���g���TJ���9��]�~=�H&''���VL�
�=)| S*�4&�Ms����<�-�zGc`d����?�T=��W��uW^��[�J�,��'���O��{�����i������P�JZwG����-����4X��-���Z�����x��p(������x80V�M}�����yi>l��-�|%�:������<)8���+���/���R�@���</d���+W�J�������oh���Es{)` m�N����4'�����hi��V����q�w$��)���R���F"h�6N�<g�������ONN�`{���J����17����z~�R��B�S=w9�b>v��F�����}��7�nZ����@�J��3�
����d���Lm?��9��5�v<G���y������z4��t���]9?m�q�����j������������s���3�[r�����������L�?��>�w��6p��!>�������~f�@���W:����
H�O������`��������m�B���W�O[W��������$p�����47O��N�<�+�Mj��8�#����o���*��N���oh<�������6��7��w7���k��|�5~hos�6�i[c]9S9d �?I������`C�p�B�J!)$`�R�@e�@u�@
&�������3162��. h�������\e-����0�������������|�C�t�~`-y�Xw�.]����b�����&���U	��/^�]����s���64��d|l8W����%u
'�����&^��������+d �_�_XK��uw���\E�:u*W������*�����v���\E�?>W���@LN������1W�C����������������>��	2�F#hXw]]]��8w�\������y�������@O>yoj��������r1Rx���
��bd�7w3��s�s��H��l���6"A�����X��GQ�����}c���m���{116��ii�p�����`����9���t_�>��	2�W(b||�kf��s����N���tww�
������o�]��H!
O��������ac�C���{1����T�}�XY����sN�+��,�W�O�d9aB`�?~���~��?&&J�:+)����{��H����`��>}:W��_���]�t)W3�YVb0�q�����!Zw��������(<�]����E]��`����1�{����R��$�cS�*K���+�'0m)aB`����'�zhh���a������b��+�'lW��uw���\E�?~F �B�9inY�9�.\��+�����1Zv�����'blx 
�w���&Z;_���]�����46�Is�9�����1u_��6 d��������������n����9����A��;~�x�JN�<����46��t���\��1]]]��=�`���k��]���v:l`t�i)l`�Os��h�y0��g�:�K���4��{�8�����x�t��6 d����>���_�-+
q����-]:G:W�l����`��8q".^�������{���YC��t���3���>}:W�q���"������m�����~�7��_��;���{?<������cI;:�W��t���������6086!d����!80#`pp0������Ks�9����}����L��!����e����A�@����c]]]ydI:��+Wrq���g�\�v-W�KmCK18 �{�l��COKae�u
��R��^qlY:G:W:'�ps�
��3!�l���� ���������x��A�.�Is��9���}�v'h�0R@���s�xine��lR��'r���5�F��DE�@������������.�D��\���60999���H�2�S���~kS�����4�,�K�L4l(����k��-*p ��E=in���������>/�`3�o,�
T|���
�?(����p�����#���"�#��N�i��X����x��f|���������-��H�H!��@��`�9q�D14 ��C�V(��X3_x@�x��y�����h�u8w%�)l``z�p�T�(w%�Ss���s,���d�z2S?sL��B��3H������R@@e����O������*�ciLY������ 4lh��������`�khj����rW2��0L�Nm�Zw2+ ���h$�F�s������i:.l����%���������x�p���$�K�*��B�Y�6������`�JR��HO�JZ:N����T�!e-
u����h��-6+���58������x��q��X�}���4x���M���#�;^�]I�������"d�k���.����h��-�
���������'O��2�n+��B`n���������#w�����]��j������b�n�
��ikk�}����d���4&��&h�P������3����fY�v2Rx#COs7-�)��X����	�����{�������4���`�H�'O����������b��^�"�#��������������2a��v��mmm�����c��	6�2 \`������w7w%��{���3w%���Sc��X����	����������/�O��!�2p���em[��p��H�-�
�����Ss��[N�@��Xy)H���'w�J���}���4��K�.��$�LNN��+W��le���'�sW����0P���7�Zw���05gl���@�V"d�L������gBv���w��^�����X`n��uw���\�B��old0
�3C[wGS���Mk���x�R��{�s�Z���2a�|���q������C�m�*��i0;A������Ux����bP�d�����]��c����5���]��s�s�V#d�L�,]
�w�^�Jv��Y(KuGGG�J�a0;A���h!
On�dE�@c��h�8���5�x�8�,�#�+�()�L�J�@�\a�~��


����sW������i�����Jin��{T4�	��
�@
��^�����/�������������)���h���
�z�C���������B���;)d 
�%��1��.��w�^�=@"hXw�O��UDwww�(�����191}�����`��e��a�c�s��"^��/�nZ����r�@��t��R0@
�hoo�7d�l�����
��a0M���N�:�������
�dbl$
�S�X�Q��VX�47��,��xS�Dt4��Z�@Y:��Y###��������2�o���=_
Hs����9��A�p���\E�?>���s��M���`����-�ol��]�s�t�
S�*K���+�'��r����x���������4���5w�{����
��k�rq��aS�G�bblz1d}cK��:5S�+��e�\��e���}�z-����=---����-��f�
�s��H����`C8q����������QSS�.]*n)x`��fW��#��;�u]CK��<Q���y'�G��sM�+�3�;I����Z
���3!/��B��.�
�s��v_���]
H[
�v������-vcs:s�L���tD4����]��EMm]������(<��=������w������s�s��H��add���577����s�|�\�a�����6��������PV��5����ir��P��(���������:s���t,�Icg�����Kkkk����X���Dm����)�+�
455�t_�>`�4���BN�<�;`!&''c���1Rx���-�Ic���v��/��B1`%C��9S�A��t_��	���k�Vecs2�7<� ���rW���;Zw*n�����9�Q���D]]]�V^:w���
�������9tww��3gr,���`�<�]D}Cs�w~(���G}������/+Ks�\�/A��2p�������{��|������Q[���ii_���+����"h�uq���b��4�c���hl�u�-�{V]CsqLY�\�A�������������=%��]���O����dL���&���9Ws�S�;u�'A��K�+�j��Wjcc:y�d�JR����d�8q"��gbl:d �]@�@����cC�`�4�����r�V}|�66�k����+Wr,TmCS�J���s5��|[R��p�"A��0099'N��{����k�U���P��6>6=�8��&wl7�XS`e��7�*b��$��	)��`o�f�`�4��t����Z����������*~z/&��s7-��������l/5���Q��q����y�f�"�=7n��llg������b}����r�J��
�B��u+w�����&W���wz�����E��y1�Zr�j��#�sW�B��K���Xa�����s�j-���ws����l���{s�4O���������E���9�\g�F'�����E��/�n���O�n����o��V�":�g�0<<w���]����������?[{m�A��x3 ��f
H�^��������{}����'�4������G����������7G��s�{��������x��[��;=��s'h�U
����M�~$wK�����z�j�
@����S�NEggg�X�={�����)[�-�����T
h�|1[v�=sKc�X!��j��4w������=j����(�i_:����P39%��p����y�f�"�=7n��llg������b}����r�J��
�B��u+w��������l_�NO�������=/F]CK�X/��������-�g|����]�o�����/����<����7��r����hkp�(���{�r������C?����{�n���[��8t���	����p��s'w���j<x0w,VK���l���~
�������.\(���K�����f
8r�����`#�����3o|9w��Zu����T|����[��w�_{���4���>���b�������};._��;AP�:h����q������	���;
�
#-2?y�dq��rC��	6��+��G��!�2p���em��
����K�*I!���q���em��
������*�	��#hXw]]]�
!��
�6"h�A��;}�t�"���s�A��;u�T�"�_��+`5���s�rq���������
��k�rq��a���+Wbrr�����'h�N�8�������8y�d�����K��[
X�<K���R�@�R�@�����tl��,A���mD����]��*�,A��;q���l��
�6"h�A���mD���tww��3g�[MM��6�Y��
#��<y2������
B
.�O��!�2p���em��
����K�*I!���q���em��
������*�	��#hXw]]]�
!��
�6"h�A��;}�t�"���s�A��;u�T�"�_��+`5���s�rq�������K11>Z�`6��
���k���p����E�������������}��/�2A��p����
tuu���'���&.]�T�R��b7��`r��P��(��������G�X�}�X����`��@���p�j���/n��b7��nrr2�#�'y����46�VV������!h`�x�cC�+il��;�TWJc�`��F'�V�H|�g(���`��oS�����F%h`�������oh���Es���o�Q�R���ceiN�,��������x48�L�@���8���F$hXw��][�
`+|������Q[���ii_���+��,�w������/�K�`�����k`8v�X��y3wG��7n�X�B��n��]��#G���%w��7���y����h��b�5��q-��|7&�K��F����\���#������1���T�Y]���x�n�"�����#�������������.����j����b��z\��c�+��Rm����PC��10215n<-�l��W�;V[al2�{2����������Gs�t�o����/�.���C���l�lW���q����E�={6>�;6:�-��#h�o��!h`v����d���vn"Z:DC����n��$
}�r����#j,N_m�4��i�������\��L���}#126������
�C��
A���`����lc#�*��o��|f�<<>6�+`)�
H:����+	`#4���6������p��3�oK�N�MV$�n��3d�,�
�q��LN�5�;v,n������G���7r�=\�t)�^�W�\�{`�
�B��u+wG������l_�x�~|��/�.�m��Q����������)��-����@������cd��X��5F����5�k|����]���T|����[��w�_{���:����]M�R_S�Y�q�N�P�����
������=��zGs������Z}���x�����������Gs�t�o����/�.���C��43������422���QS�+l=
��;wrq���8|�p������Om��RH��3g�[Z ������������5��O/n)<����E���G
?H*����%�)d������[������\��z\:�l������&,7��R�@OOO1��{��^qq{�M}����4l)\�������!
iwww��V�����i_:���mw�J��������MK�������z.0����o������U�X�'O��>}�L�@���t��w:`x�W��
!-._�p���O�jsI���cQ\����<y�8f5�B���5>�4~�s�}�X���#lvu
-3�F�����b��~�
=-n�N����4'�.]��"$``d"W������>,�����?���=zT�N��!T/8OA���\��.^����q����g�H�������WKc���X�_:X��~��q�V�s����m_��7��dd�q��)n�����9��U
<.�G��x�f���qe��������B�����������C�oS_)���I��J^��]�z~����[e��������)P���k3�_�z5W���rU�>��9����5Y����H��l�������J	�-d`��q)������&Z;_���]y����46�o_k}�Jn���6�;4�{GrW��j>����H��`����G��G>���>8�������hjj*��>�ohh�##?~\�?�ym+���	6�����M`�8v�X��y3wG��7n�ncJW�//nO�S�@����|������s��R�9�b�'N����sLf{�*?�d��d�f�����������{��i��e�����y����E9r$ZZZr�}}�����7�����=/F]�����P_�
=�����+���������o�
��}����B<z7w_|�S��W��niz��z|��O�.������j�k���X�����vwK]�5�FsC]�����D<.� 8����
�����x�g(w{�K�����n�n���/_��)�	�����Y���q�����^��=���f|mX�4�]���S�N�j����sq���\�T���z��f����$Kc�*�.��rU��c���������R�<�s������J���b�@�������S��y�pq��X),���.w%)T�������t[2������koo�7d I��8`n��
e�����+�W�\����_�t)W%��-T{���sUR}���~l�;(��~�z�fZ��X0��un�Y�u
�
Xy�l�����_���SSS������}M-tlW5������w����y�f�"�=7n����T�``�_Y.dL
8y�d�N��]�#9s���E���m�c�,�����]���/��?>w��c�]��LHA��|L��P(��[�rq���hii��������g��r�"���u
�~����B<z7w_|�S��W��niz��z|��O�.�������:k���X��������_�h��m�������x�g(w{�K�����n�n���/_�]���{��qa�,���H����.���C
�;w���������g�����s�F�_[��;
�M�����*-@�M����E�]e�@���<i��J�~�z������*������w�2P���4.�gkJ����w��n����/IQ=.�g���Vn���6��BN�:���m9��\��R,'`!�.\�����+`���������n���l���\�M}�4>�����f���
���<�4A��[����s���f9��i���'�����������sO����47��Kat"����.���&^��/�j����hk�-����=M�X?��9���,M�Z~Y�nv�B�8��r>P"h�P���}�������j�K�.�j��MXNx�z0T����Ws5��+WrUr����sV8���c�^��\����U�����h���LMu���T����#W%�=�3l �����]I�|@��T.�OW�?s��s��?�����W����V+�`rrr�s�B#R�@MM��� �4���kq��������D�"v���2P��������|`qc������{�������/������O�����=���S��T3���	l����7o�.����q����m\�*����*��RA��������2P}���s����?��,�Oa�W�_��\}������+��_������s���B
�B��u+w��������l_�NO�������=/F]CK����B<z7w��������7wK��{�����]�+{����uVS���O�N_=�;���!ws{88����.���FM�Y=��v�P�"~��~7v|�'s�tw�����z+w��3�{����?�-TKKK����c����;w��.��W_�����J����f}mX:A��l�7>o�z�b��l�_}.'h ���^���p��s%�Xf��/h`�@��l��������X�c���^_����I�
@����_����W>���������9w��B����>�K���T?�{���G�����=|��rW&h`}�����G�r7���w��]�r�Z�8u�Ttvv�����gO�������4��W��
#-NO������E�����x��[
uH�sI���$����3-�/o�c�~���4�KkU8���x��W=��<����������������
l()l���<]��Z���|A���������V���.]��L�@���-T�����(�OZ����J�cH�����%�M�fbb��+�fjk�	����*����<���������n~}}}3���T3)���c�����7sq����q�F����������+�l6i�|�U���>����)�a%�3g��x��1_�p�sR@@
��TS3sI�B?��y��+�B��n��]�������)w���������.�m��Q���;��h�7F�����PLL���\[[W|>�����egqkk|����]�o�����/����<����7��r����hs��Uw�w$
��.��=M��T��g�
��w
�.bOk}���;V���D��3�����������-���w�����]��C��L��zzz�����+��cG466vL?/����:f��������wx��W�����`�ZZ��g����P"h�o�\6b�@R�q�&
�������2����>�b>��<>+�:h���#k�f_���o�������E�t��z�.����x�<�����gv�M����{�����Yyc�Q�����/�����+�s�4��|=����s'h`��N�_T,^o�������
�������~K�O=O-��5Q4����;_�h������q����	XK��������E�q�������G������/���s�j�8{�l>|8wlt^[��G�l1���T�l���$���������S�N����
l��q1
�n:h ��8���5�������������������X���]����hk���������x\�GKv4�������
l=�������@��x���1w�)>_e���q�����Z
ln^[���;
6������?~<W+#��E�)��r�r�����)���>���$�c�^��+��*8���6��;�e�������������������P��zG���mu�@/d�'-`/����7d I������@��`������m�Yt_�z��r������86�s�BEp@C��h��R4����������/V��;�bG������qi<�tccc1>>�������U�K��y�i5���S��q����y�f�"�=7n������I��]{�����.]�����n���3g����X�>}:�\�R����>������k�
E���g=?�B��n��]��#G���%w��7���y����h�8�������16R(�55��@������>11=���w�D��oh����5�kbl8
}�r��7>ye����������t�"^��m
�3�V��p8�G�=�%�+6����Q��Z���{�r����;_�h������q����E:t(��<��mbb"�����.��^X��bq�����~������$+cxx8�������g�����s�F�_[V�wl0��S^���:u*WK�����p��X��q���\�\�~=W�W�q���	��J!u
-�5�&�F�����{���$kl�������Y�i[�M���r`�����-u���
qxWS�6�I9>,�O�����������U�B�bhh:l"�23	Xg����U��r5��1��X�rh��������������*I�^H�Br���\����	������6�����9Ws������sW���}u5����xqWSt�6�����m�_����5yd�
M����i:����7FF��g������n�|��frJ��-���cq����E=z4n�������������(==F���O�>W�\��Lg���A @2���H��O�<��������x����y���k��=BPi�>��JW��u�V�"�9---���������_�]D���Wqgu������.�e��hhj���F�����v�"�����
�c���b������������?wK�����ko~:w��m���Xm�zG���X��2��<}��j}����G�����ZGv6���40:o�L_��c�})v����-����������8t���k���?<x��Rp@gg������p<|��x[�o��ho���$���;w��.����q������m����%h�o��jj��x����������0�t�������~�[b0_ ��>�d��c!!��I�X�?�"�_m�f'h`��?x;&&��uS{g4��-�s���������.���R�Y]����x0Cc�zwK]������}2�����������+V�������'�>}���;vDccc�g��������c����{$+C������~��`�HW�����<y��`"m),�z�~���z��0�����4&�����R�����az����������z�t,�)�����!I[���nQ9�0:Q<�4), �
TJ�>,������4^��N�������W/��M���9+i1Cr���E���i�B	��54��U���D?�?k�@����1e�M��bpt��'in������U�X�C����;s7�4.�f'h�4�����V�����i��l��������([��099����{.�)�{�@�����5���60:�4z��=1:�_�R���ce
�;�s��km�������\��z\�y������={�Dggg����J�O����[W}��l`��t�R�9s��0 m'O�,n�>Kc����?-�O��+���~��������+�1T+�;�k5��Es�����~�wrr"��F��������,�m�q w�B���-!#�_W����[���~f|��Q<|�0����}����===�1���i��
�.p�������{����1i�V
�OMm]�w�
�;���54�(�Ms���x\������.O��*�Ks�����������������L���-���cq����E=z4n��������;N�<���;}�t��������J+
q����E9r$ZZZr�}}�����7�����=/F]����h�7���c|������uQ��\hh�Y���J����ws��7>ye����������t�N/����1��\+���O���#��h��������lxG��x�����9e�������H���S�U����k_��/4wKw����|�r�":MMM�c�������OsW�c��hll,��@����:f����c5
�w8{�l>|8wlt���`i
��Y�8_�@
8u�T�J�^�]]]����=Y
�f'h`c��-���6oY?�4����Rm����PC��102���6A�_<x� wQ|�;;;g}�������G�E�e���������Z
ln�`���������*I���]+�\�r%��;7cK���4&��t���\lS)wI���*�^�h��3T�2+o`` W%{���3�����x�R�|@��\�t)���rW
HA'N��{������a�\�����F�����v��|gj{'o�)�K�`�JW�/���������.O��*�%5���_��q����y�f�"�=7n����t���
�C���</^�s��kX	�B!n�����#G�DKKK���o�}?>���s�����k���Z���3�w?F
O���5������Q3���3>Z��G��.��o|*>����-����c���b��/����G�������?�����������Pcu����m������b��o����/�.���Cs^U��3666�g����G[[[��600����]�g������R���;wrq���8|�p���6�k���-f3���fz��r�����������OV����	X_��;��������g~u���:�U�����A��?�����~=w��O�}e��������;�c34�>&&&����~�"^x���,&h`�	��
��S�o6���$'N�R(��<x&d��mw��<T�R])�Ms��y������n���l���\�M}�4>�����6�����R�B
ML���L�6����x������h��P4������-�i_:V���������V|��=w�u5����xqWSt�6D[Cm�6�/�i����<2����������������������������6����\-����s�5�>�UI��Q[��B��/�T=x����w�*9����WY���TW<^��W��\������������Li:^�z> h�.^��������j�.]��+��o|lz�ec���kh�����4��r.�0}��F�"v���2P��������f���joo�;v��&p���b�@___<}��x����2� �K���
�������8�|tww�n���4����+���&'cb|$7u�s��U�)��:�0��1���r�����[T�������K�w��hll�]I
x��a1` ���R���4��'N��k�rq���E�
��iN���s�5M�M�$��~yx|l(W���}��*in������U�X�C����;s7�4.�f'h�f����K�.�:���9sfF����������9��6����\���
�j>���nA�@������dht<W��W}`q��	jk�v�����x`v��u������������� t�zK�����������6����\EL��jn�c�c�s��=,LMm]����E����K��4?�X���??~���=z��8����M���)W#�'1>O���H!F{s7s.�0;>���x\������.O��*������B!w%�������*���R������
lB�m�sU2��^L�
�nZ�7�?w%�s��;�������}#s�
��������|����+`�FFF���G��������}����#������O�������3�LN�5�;v,n������G���7r�1uww�jm�8q"W�|�J��n��]��#G���%w��7���y����h��b�5��q��������+I!����`l�0������c���b������������������b|��=w%�[����6��bht<F&�qaf����_�O&wlV�o����/�.�W�O�Y}===�����E1L���5w��{���.b���w����Z������;��8{�l>|8wlt���`y
�����:
�N���J/�>z7��������7G��sTSS����
l=�?���z��o���������Yu��������.w������x)����=���W���������/�����k��b��I�Qz��
l.^[��G�l1��C���
����?�}�c��$��]c��h��5SX;���w�����_���>�o�r�x�3�c��`}tvvFGGG�����>��A������~j�-[@ME�@e
��p�?����+Z|(�-I}��W��B`455�j~����Mjrr2�#�'yO���)�u���9��������B���_,�������m���XI������B��vU3�]$��;v,n������G���7r,U�P�[�n�.���#����;���o�������E��y1�����ll�y�w���{�?�?tW��VVVj9U+E� �t��D�4�k��[4B$���S���%�u�B�?M��R�(q�4�S���&$��l�v`;Q�H�Z�+���'���w�}f��{x������C��8��9s�;g8g��r�|���������7_{1Wm���p�|����������_��v��^z��o�*�/<�������H^��W��?���������>����>�������_N���Jk���~�u�����h����������w��G����W_m�|��477�3z����h��8|��#h.�!h�;Ag������_��o�������']�|%�i{���������7��kR����t��{s�(	��^�������_���������7ymJ�����?���������P^��~���W���\������{�=������g�y&W)}��M?�c?�+��-@�\���#7^�~�]y��;BB������������/�����_>2���;������E/��Bz���suP�����������yt8�n���G)�s�C������Nq_l�Q���W������/�b������`�]����z�R�q�Fz��g�w������/�����n���7o��[���O~2=����@���������f.R����!�mZ��=��o�}!��;[�j{x������N|�j�6���>���O|"=����j{��W���?�~���uuQl��?����
�3o��2.�4P�z���7���������/�b�R���Zz��+��u%=z�����/�n�~��+�����ej����/W�I����L��'?��������@w����1pLOO����\�455����s����^����UJ���ibb"W���_{.}��_�UJW�4�.]������y�������{&�Uw7��`��O�v4#���������*�/<������+�������w69W�2����ru����J�x�z�Rz��O����\����������?J������[����������w�+��?���������@/|��#h.�!h���A������r������Uwo��Lz���r��k�G
�_���g�k�����������Jk|�o}�zzq��}���������1pr��������su�O~���G�Gs�wP=������0��40>�{���������[o���^�V�8�N�K_�j
���[���w�Q�Rz�Cw�G��;W�{����/��UJ���[�V����x��#����������F�����O������p�-@����h�N����|��t����K�^e�����/?�n�x#��l8-���5���Ws5�O�>v5�w�����x+}�{��{�����/�'����+����?�'�7~�7r�v����+W��������W_}5��V���'>��\���T���`�1 ������{������[���_���H�����j���������n��Ko�v�u��w~ ��?�d�8-x����k���Z�Q
������������=������n�j4
P�>�l���?�����s����>���������_=�������������M��	y���n�G�\0�FC�������_��� ��o>����z���o���Nw_>�J�p^�F���w�|��\�[���^�����4@����Z��W�������!6���G������
��wP=����?\XX�������G����JZ__OkkkyM[��L333ivv6��Q�a�����z���F^s�
0����O����L�����t���P��O}"]�����^��W�hx���zz����J��G������;�|�V���s���������,W���>�Gpq��/�Rz���Z����?����n�����~7�����q|�3�i���
@��f��vF�l�������iyy�����h�_ZZY�@,..��p'=�x�sss�:�����+E���4�������X���o�^���?�����\KO<xO�������~��7���n�?�����t���r�����J����r����>�|��\���_N�?�|�R��g?�.]��+�L�T�o��D������������-,,�2Nr1�!!����N7n������'d �����&��}�����N?��?��������������_O��~#�v���m�����Bb?!p2���w�����+yt��v�y��
�����<j���O����,qe�XW�O�~/���t��l6G�E��
�6���s6�"T�������/�y�j����N�>��%�E�m����:=�#3�j{q�V��K7����F�6���>�N���}o�]�~=��V��<T��`��M����'��h�_]]�U���lk]9l`XW��F�h�/���x�F�����#�(*%tSF�9�s�A��*����L�[����UJ���������_K��>�Zb�����'�N�����H?��?����v�=pr�.]J�?�x�N4��<�m�A��n!E���r8�I���1�!EP���+8���B���1�a����<n~��_������+<�.]����b]�WT����:M���K����t�����mQ������������7�Rz��W����������Z�u��
�����<j���yt����<j+�q��_tT�AG9(`kk+��T���l2��y�8PR�W�z!�R�����]w_���������/p2�O~(}��/����������>��/�n������������������^84l ��{��U��?��<:j�o�c����N;;;�j�1���v�Gq��b~�����jy�6��{777���\��A�F#WG������\u?�^�wqE����\�499�&&&r�k�������UJ>���xW�����������J�����&�^�����������������?�U������\��Z�_��Z^}��|o[�|����p=��y����z
�����q��+���b`@\�uu5WGf�~y�~��!w���a
�<�q"h�q��o��������*��y����������i����*��������>��\�`��?��������?�G�h��|��T��|06�q=�h~?��������^��Q��g6,333y�����<��2y�@�M>�Gm�n^�����o������.�^��	�F4�wBb�+��d9O	$`�D8���B���,�n��	p����ez�{�UJo�x#�w���mb����sO��%p����n�P������N�R���J����_�T��t~�
��p����4;;����f�f35�\�g��(�;??�VWWsu�qv��p��������kii��c����������&''���D����7��������\�t�#�Ow�}5W�zs/����r����G�L�����
����������[o���>=����������O<�Gp������;�o�1���h��fvz733�Gm[[[y4����&��+��?������+���n^��m���������/C4���!p��G[�����Ym�<\���y��P��������NSzq}4����hX��J��`ee%-..����/��"`��h��?��������j��V
(�[~�E�m�^?p�q,�����_�����������]���\��+8�o9��_��\��s�������������k/��?�'~8��?��\0�&&��_wZ��w�`
�s����^��e���[3}�I�<��s���P���h���P�+�r,�Bz
8.�����V����3��v��~z�_M�|����h�����O$]���a���GI�=�X�FO�T��|pf�M��,_W��X__����>��;�A���!�"��0q_��^�P�?B�:��x�3�nz����5��mb[!����L���U�#l ��C�*����3�������;�c]�wR����������7o���>������z^�R��#P�6�m�@�������0==�vvvr����T�����x�:���e/�D�{�
��f�����V<�������h�/6��9/,,��������>V������s��j�����>M{{{iww7W)]�v-]�r%Wp6����r�;��{�j�����]��n�o�u#�����q�O��N��},W�����<:���`0�����A����~����,��1�A�[8@Y�����Z�[|
��Q>�~_�����~�]���<�?��������|��*��w_MW|O�t�`�[7��7^~&���F^���������{r@�	����o��h\?N?W�?K333y4^"( B"4�,�b}���0�x<�N�����GmWx����������T��������y����V]<�<����-�����?�D�@��(�f������y��=�>����j����6�}�A���7D`}}=�:,�`�E3�I���5��kV>�r����G�+�R@���q3}�;/�*��.2�Q�&��9�&A�XY\\�P�J�a������f��G��g��_<8�8��1�
1H0\TO?�r�]�!h����7~��<�j
g��hhP���Kiee%�����qQ4�������I/M��M��^������4�Z�v�g�^��z
�(�0�'\?4�H���y=���V�m{��#�����<.����������������v��W4�G�@Y�W��t^��t��������,��;�9������(>�a���$s��O�9�^^��>g�+����������&''���D���������7��Rk|�����o���+��7_oo���>���/���1�������o�T4�G�yQ4�5�;�u�m�S�@h6�y�~N���rs�grRKKKy�v�1��q�C7�����[�)?�q�@���|$�Rzs�����7ru�[o��2��P=����	�4�����mQ4��X����������N�@�ee����p����j��;K�����C�v�s/?��rPD�?77��������P5?�'�P�]��������-�]��\����Zj�o�c����N;;;�Jijj*moo��|���r�{G4����hF�+���f�x��x��f8�g�?����\�s:�1���-,�(�����vwws����d�������v������/������U�=�=�._n�u��^z��[��?��'�O������Zp^\�������o�����|l#~4�/--2�	�C8i�C��3.!A�'�K�����_�J�F��������l{2����������
���wP=��-c ���~cc���_���>��%d�$z=���=I�@��:��|EQ���K���~����Rm?��0��U����'m\�XXXh�������a���K����Jirr2MLL�
���_�r���|%W��VKo�w��������h����X�8��w�`
�s��p� �����Lsss�l.(4�I�������������}�]��nO��?x-�8/
@��F���FC�pQ	����o�����<#h8�V�u]���������gIi~~>��"A��XZZ���Q���(4������l6s5Z�����h�
(���-��`zz:����*��������������G�����Q;$�$��z�vff�j�����vwws����d������5��-�'h.������Z-�R�+K�����4�s)� h8s�,�h	�����;0Z��sass�����`,E����B��j�enn���Y���"x� h;0akkky��[��>�x���A�Z������'�
��j�o�c����N;;;�Jijj*moo�j�E�@���|��^��n�������4;;�+����������&''���D������p2��-��ZXX�����������h4�X"G5���t,//�P&hkkky��p������p�Ml[��677s	����J��Wy����<�
gn}}=�R���y�����<:8'p��������QJ�F#�S��M�pa����pAP!�������G�[YY�������(4��f��G)-//�Q�����8��������QJkkkiss3W��}b��z��G@��������Q���\_a+++�}:���S���P$hy���Z�"����������������---�P&h�����l������N�@g�:�[[[�[�������4��F��666r��*X]]����`������"4�W���i�T��}\���igg'W)MMM����\�?���ikk+W)����z�������	�4�������\�499�&&&rp~]����	���h.*�-@�\��#���y`���`�����Y����Z�����R�V;����k8����;��
�p	#�������imm-��-�G`Cl3���N��iG�s0:��D4�G�~�����&�E�@'���>����<`T
��r����|���g���h�+�&�aZYY�#� ��l8����6��,0Ac���������j���m�+�
���A����j�@�x�F�����#�(*%�#[���40�
��B��a�p��*�c�"p�6Apsssy��	8c+++y�V����pKKKy�V��$"(������(������-,,�Q;�����	8c���y��h4��p�����<:����<jk6�yt����G<n1���p#h������?a�����������Q[9��(sssy����F0J����1pLOO����\�455����su6j�Z����z�F��h��x+�|"<���^YYI����j7������?������^�����;!q^�������?k{{{iww7W)MNN����\�_���0Z�������t~�y�&�P��<
���Z	8]��z.�[����V�����Q��s��������QwLP5������%�����U�j�Z�����������~vv6WG������\��l6S���U���������j���������Z�n�w�Y���K����J���k���+�����<:���0Z������h������6���q
����������4�^?�
�b{��G�c�=���4�#h.�q��"$h �g[n���X��(�6����
�v)�Pq��_�p���2p�b�@�"��������K��1�W�U�/�������]��0���.,,����\���333��|�E���iuu5Wm�y��_����ioo/����*�k���+W��
`x&&&��t��w�h	�F���2H����f������~��������|h8@�9�k��4099y��0
��z.�[h���-8N�t����z=��a���^�`�
�����</�F�"�- b}D��Q�����-Bj���K1� ����=����1Rn�?Jy�a�D�@���X"` �wlnn�Q���'�&h�E3�I����Q� s
�xpV�@o
��f��G)-..�Q��������133�666�Z�b��}����NJ�0r����,�F#��0���yt��fmm-W)���<:�����R�����J^{���cvv���VP���	8c������<:\y�a8tB[���q0:��@����v����B���u���|
fii)���:�P>��s`|	�F#��:a���yMJ+++w4�����<�.���j�,Qw3;;�Gm��!t;�;(?����1����Gm��?77�N@����!�}u�1t;�8.���!h`L������q�8�Ml�S?������sF���F�h��f�h�/��Ns��C:z=�����h4�Z���~�%(paLOO����\�455����s����^����UJ���ibb"W����z.�[�
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@�C+++iaa!�j�K�����[��(����c�i?O�*40F��>����Z^{[����km3�F�h����Q�qs�������1
��\����,��mG6�	:�E�����!�6�����IDAT0\�����r��������w�������h�����;��1�����������(�]�����
���1Pn�������\��������b�a������\�E�<f���k��u����b�r�@�_�7t�������}��<�N��(6�w(*�
��N��Q(�P�b�@�D�@7��c���(KKKy�6��	Pu��X�J��z=�Wn����#(������rX���VT�;��M���U�aA�N��[__���F��G�+7�z��r���(�m{9�^�Ag�|��^�p����<����L��C�����s`t
��A��_n�D9<�����������Z���S����p
��A�	8/�����-B���3T��� ���NK�q��'�������r����|0������������N�R���J����b�D3���b�R����������inn.W)5���h4r��A�*?�X]]���-,,��g3
{{{iww7W)]�v-]�r%W�311�G��wP=���������
�PF��(�T9h ~�^��r0<�<�Hz���r5z�[����o8g�yu��\�������b �0�C�Y�\$�h���������#������\
G����q0������������N�R���J����b����h�������\�V��QJ�f35�\�L�F��wff��q���h~~>�����w����������������]���\��+�����������Ap��c��e������477���
�n
�G�c^^^~g��
�{�@(
LNN�������z.�[h���-8N
t��!�z=�zsB.Aghff&��K��h5�w���X���lnn�Z�v d�^�h#[[[yt����-�f����?�D�@������^�#�����U[�^��
��A��������������Q!����3�l6�(����<�O4���b��q�!d��	3����fmm-W)���<:�����R�����J^{�~�C��x4p��F�-//�������8�Nh���b��8�G9d��l
8��@4�wD����B���u���|
fii)���:�P>��s(+��<�p�S�[���t����UJSSSi{{;W��Z��Gm��������zee%���h����7�[\\�U;��&���!t;��nuu5Ww*�{T(�Qfff�i���K����Jirr2MLL�
����T���`�1���������r�����c��	8�12���pG8�Iu��&h��|��s)�p�"0 ��q�8�M/!���B4�2��40F��?���?���"���?����^�a?5���pkkky�8���_����t����UJSSSi{{;W�I�������\�499�&&&rp~�n��R�*@�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T����YYYI�V�Xb���f�j����1����
A��������������b���\k�Qt�#+�,�-�w�.A�����-`�,��mG6��q��P�G�@,//�Q���|���g���h�+�@�a�6g��l=~�X�.K�A���J�Y�#��WWWs�6;;�ZW�}����F���Q��%�ss3WB������G�C��
��}!������t `��|,���y� 
\`+++y�V����pPT��$�������l2�Q<�rP'#h�[__���F��G�+,..�����8By�a�T���lmm-�R������5��<��q��x����'h�"��z�oss3��O�A� ��	��	����#.A���V�
P��4����Q� 
�T�[���t����UJSSSi{{;WT���JZ\\�UJivv6WG�f����\��l6S���U�����Z��Gm�<�a���K����J���k���+�����<:�[��4�?�c��z=��q�q	�_��C���GI�=�X�F�wP=��-�c����z=�������h����W�?��z����\4��5�4??���������B��;���W���{��Zm?.�\���igg'W)MMM����\Q%+++iqq1W)mll����\��L�l6[a����Lsss�j���z�~`��nyy�F�M��c����`�&&&��t�n�G�\0���A�������AKKK������wP=��-�\DP@'�
VWW���V^�&d`0�.����</$�-p �GA���k&A����G�+o;���������K��0�����<J]�	���������bsd�A����QJ�z=�8)AX�������?���yt�677��mff&�8)ARn��&�Y[[�UJ�z=�N.�\XXH�Z���jyy9��fgg���4p�5�<j+7�wS��<�I�VVV��p���f��GB��Wl�������\�)�+6������`fgg�����
8L�777���aT�������	(6�����2VWW�����V���D}�z��Gm$P�>�)��C666��A	��r�~
D3' `qq����a7�G����|���q�AqL��(�cvv6WJ�@D�~4�����mF��������f��6���2A
����������?��6�l�o4i��X��b}�����j������1==�vvvr����T�����A�[����o�
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@�������0==�vvvr����T������x���Z�8���}����/>��x��\���?��<��\q������W^��xx����>�+����z
����q��_{.}��_��x��S?�>��k��������������x��g����Gr�y���O�/~��������O<�+����z.�[�
@�������0==�vvvr����T���������=�>����*�{zo��uw�N����������������Z�S�����A�~]YYI�������������������UJ��J���������������\����~)=����j���i}}=����5m�f3�����������{��~����/~1W)]�v-]�|9W����������UJ�����O<����������������O�z=5��fxZ�Y<���j^0|�[����oF&B��{�T��6��:�h\���9E�6�������t��iC4fGH4���B����km��{6���F1�Y���+W���r�����2��xm�!!��X���-��y0��$�t=���P�i��f���*p�����}����k @l��2�s3�:�{�"p ��A����40$��t��*\���&m8�n!�f3�����lll����|o[������y��kn�S�f�x���.��q��y8�g�s5ACt�+���?�\���!��F�����������;�k
�����<E'���U����x���.��,�+:i�L���0j�Pn*��������?�\�!|ii)��+��^��[�@Q9l��
���n��������l������x���9��B������'i*.6�F3k9�8Z?
����h����,^����p���~�9{�����+B��������<:^�#2pZ
hff&��kX>L���&f��
��=Tn/7�wS:8I	g+>������^q��E�V�����:�����P�W�>N�j��41&V��������b����������rT�B����(�sq����{nq_/������<�q���)����Gq�����X���O�:���������|�h���^?:��d�3!������j��:�^B/�����-��x�Qt�bSq?W//6������F�hX��.6Fw��X�����&��?�n;�u�3��%����~���=�^�WYls�������s�����9o��6b�k���{������{n���yP|]{Q|������<33�G����h�ACPn(���x}}=�N���i��W�s�1�|�h�-^
|X:a�����y=��Q?����<bX�
�E���8����q�s���� ��6�sr�������YQ>?��Y���5#��������!(7����~�b3�M��@��h��4��X_�j�aW�/����sF]n���Rn������(�,����6���a��_�8�������3_��i���y�k�E��<I����Z�������+��	��3���_�^������c/���%>�����9�����Y�ACRlx��*���i�������0��i5�������]\�)�+�u�Yn�V3u4����{~�����{������h���<j+_U������?L�s��������|>���[��)��G�V�9K���~�Q�� h`H�W�._���x5�~�\C���+���|������;���'	K8L��|ii�������*���c�|���T�}�M�-z���`����,�W�����7�.r�9��>S��@�����K���1(��+��c��{m�.oW>�~����~������<���X�K�n�g�Q�����������s���q#�")z9�����t��VV<�����S�V�#�`���U����0�&��6�v����TM���=�&���X^8�����N��-:�)��>��\��Gg�����������>��8/N���
YZZ:p^���?���#�`\	�r��a�����y4����1����P}�nM��O�
���F���	�4��{U�Q4p�bcv�5�(�k����u��h5����������$��q�9���y�GxF����Q^����!*7���
��6�&Z# ���v��A�u������c��\���a+�lB�y����q>6��yG��3�`��*}����5����|�3�%_�p^�f��G����h��(7��D��i��������^D�l�
wl���x��s�^�6��C��O�}5q���<�1������0�������<j��`�
���L���l����G�]M;��&�n��e�|��6kGv4���OC�����b�E�}��R�}5��9��d����F�K7��`�
Y4����^��� W�������"�*���~k������q:��}c�N��QM���i�X��p�e�W+g��A�+���7���(�����t���BNG?�����C0�
�@4�w���;VVV�h��^�	1WDc}'T`���������V'x�l�������|��R�(����M���#������R�����=�{�Z0��#����7D�v����h
�@������b�� W�.6���������"�q:�8P4����+Kt�������,):����m��'kZ�� �"������^���sN�xR�^�0�
�@�����\l@=�j��V#k�*�����}��c�����r�w��b���c������}XX@?�w?a�s��@���9��������^>�}Mz�*�B&�q%h`D�WB/6��E�i4������w,'�z��M���8�^:��y��x����x�������n!'	�c�6W�|\^^�����f�������;?�q�I�s����k����G�E������^lT��Tp�����znn.W�+7�.,,�����vU��1Pn.�c?�y�|1G?W�����������bSy8�|��~8�!�x>��{��"�+p�����y��kR�8�u�^{�A��40"��b��W8/t�bw4S������n�\�����(o�����������9����\E��(�V'9�b�������n����|.�1�<�������jq��G�J���������>�z}�� h`��W��������a���Y��:�b�k\���_����|u����8<^q������n��y�!���9�u{��n��~�;_��m��������s*�)�]~?������������5%����?�����q�������Zn�?�n�����zm|�4��:w��Y�[8�q�y�����x�����u���[��ab�^BB?���x���������=x���B�0r��n�[7�Nu����M��~X:����Zn��:������s,�F��:���J�n�mc���3Yg�������;�[����Gw�}�1�;����hn��O��x�T�x��R~_t;Oz�v�F]<o�}/��e����yx���t���S]�1OK�u:��o�u��S������Km?�9.����������������v�N�o�����~5W��O�x����
��K_�j���~:W��c��Rz�����O?����/�j<|�S�JO<�D�8�[����o�
4R�[���t����UJSSSi{{;W���^k-���#��8/���Lz��gs5�>�x���{r�y���/�W^y%W���H>�`�8�[��4�?�����K��AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���AP!��B
@��
4"h*D�T���A���w��q3����']�
�*EN )V�H�C��
5����@)d�H�!e�����&#��!��v�������pH������"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��\���,���������w���w������_�U��������������w���������5?�H��0�BS(4����_�

@E�^~h�����������?7?L��?�������7?�H�����@��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��&��o�������?��?_������Zl������k}��m�o�����>������f��Z�}{���x�oe����m�m�����g��6�6��[Y��oo�}o����������7�V�����f���}+k}��m�o�����>������f��Z�}{���x�oe����m�m�����g��6�6��[Y��oo�}o����������7�V�����f���}+k}��m�o�����>������f��Z�}{���o������@%��(4Qh*r��C�\�?��
(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E�^~h��������>>>�����o?�������O�>�\~����>�\������=??�\�s(�m���������,��\+���Sh`����P�]���N�.Z\+777�OyOOO����K��!mU9����fi}��`�.�2�L(����:8�v��C�SEp���W����N��k�����	��b������"�)Q8��C�b�B9rN�r�L(���e���A����2��nsI��^�,�U��.�9�sh===�F�2�L�_���c���O�7�v��v�I�\��y���T�R�L>d��Yh �F��eB�����\+�r��Bgv��.E��lN]o�L���
���g�.4Wx%��r���l�2���������@�@=�u8����f�\\o�y�r���"��H����y� ��r���|{{�,�W/?4�������	�{�B(G4���T�(��ol2�����(s�����}�������sB������E��~�bq�*�r4K���Ux�h��oENye*���"��*2��iq}��`�Z�2������u��V�\'��}�����f��P{����V�������Oe"���C������n����	��Sd�[������rn�����P+Y&��2�2���_�]+q����3����_N$&N	�#���E���"�+55h�sY��mL� oJ^9d��S?L�g���"W����"
�N�Fj$��2S��v�r�������[�kd���,�������4K�"��hwM	��`�#x~~n��R�M�	#�r��?�L�����%�k�����)oJ�c�K!��r������S�-���6K���K�c�$"�>EP�w}}]<����{,o?��:m�������uq�B�x�$�����v>���U���r��_����*�_���Jdw��<y����S�cG���V�_�-�?���2�K�y�����VJ�M�&��)4pB���"\.
�J&=��S'O���s�6�.	I���cc�������d?�L^-9O��H�8E�B�8y9������v���D��|nN�W�����.)�[g�%����_I�d�]%�����,s�,S��U���V?�9_�i�h��JB�.���_�i��^��#,�h�8^�F����7<J������\����m�������C���Ui�6�V�9M�����s���Jr��|�H.��XB�y��i��e�x�(�g\b��Dl'Z\����
�+�N������[(
���{a��a�V�vi����X�.�����\tc�J���"q������,Js�5��=�t"ci�z��c�~+Y����\o��}���p����eN��,��(G|���b�i���������`�mum0�B'R���-�s��s�=���C���
�����K��9���M�sM�-	���x�8�q���`
�9��h.��.���%��%�I����1�2k?���v�I�{&�<=Y��g�k�?�8D��ST`���6��J-
��[k�����R����
�K��8W�]R	���\SS�@�����I���s���d����|������4_2N&��%�\�,��YfI��,���)4p"[��[�[yub���c�.	KL�g��[�c}:�#����i��r�tml�H���3�r����f��b�_�~m��)�$����U�7�===5Ku�e��,��,�K���	���%�^�4-	7��d�e���1������ZiE�9�_���-����~�s4���pj2m�8�����8��\�4���4��:�sOj�1K�:uR��d������q��`���)=~1�{�_�S�e��,�����s�����L

��)��~8zIJ?,av|����RVO9��SoH�l���R���5E�]Ri8����G.�pJ��/}BcM9f���2��d�U,�"#>Gfp���d���ep�8��UL���C�{T�Fh=T6n+	�����B��|������k�%�s����*�P����j4���-wm��cF?K
��fs{-�z1���+@�,�4d�o�2�4

pJ�����Vi`�f��������C6d/��P26{��[��P����"	����l����`j�D��K=�8���0�!�L;Z�y�"��B\��0�������SV��:t[I�^26{��[Z�w����"	G�r
����c�ew���hIVx�b���u}}���k��e��2�4

pJ����k�bl���K,	�K*����^�	�����b�clb.[�5�,���&��d����|n���`m��<Y&�D��0���cc�������~i�]���sT��>��YQRl!���ZoT���c�dxc�o����}<�8�WWWE��k�e��28�N����{q��d�V�_:ys�~-
�K>\���%U}CiU����7*L�ju�\�h�k�9fI�7Vttl��Vcw?~l�.�,s[��aG�2�v�pz

@�)�5��4�������������K���m�`���c�dy�I�����R�9���xzzz���2��cb��M�4}}�,���2�c�Y��P\�v�A������d��C�WWW�Z�7@-=f���?fn���P��*��7J&�^�5��Sd'�:����$�������YJ;�7�����\6���CQ���\��o�B�9�,�r�����p�8��u�>}j�8����^'���Yo<������7�J
0\����&HK��������\��3�(8PZl����Y�\�L�Tc�96�t���y}}m>&��)4�#��q-���U�FD���k����0f���Rl�3�;b;%S���_r���/]�cIa�S|S�Q�2�����s�����N����'�N��������J?<]���KTr�u�����{�#�h�)���,�X������KI�����f�����d���8�7�lE���.)���N):�O

�L?x�[�m%�uI��s�S���J*�v�y���^�<o�&��M�f��/K'
���%Y���;o�M�NM>R���K��-�,�%j�2K��Rce�U����`�N�4,��!��C<����?��s��NY8�dl#�}yyY����s����`{�9B�=M��FP�����yc�=�I�����9S���N���1)�6',9���o��,s;��_.%�,����t�i.Q2�lK�����*
���q��+8P���_�~m��c��^�)������OY��D��p)J�O�N8L�=�I��L3'&1�c�R2�y3�k�N&���2�#���������_��D���k����	�}�|+����m(�k��v�����9C��Bsm���(
��>@�P��M�S���hM)>96�02���'nk��F+���sK�29r�Z�����=o�r�����"5��V��<Y�z.%�|xxh���~,9�\OOO�����	M	�#h
�J
D��tK��<)s����c�co�->��!�%��X��sa����XQ�KSZ05���	�%��uDZ:�0��������Y�:�B�������xMv��-`@-d���e�rIY��1m��_����r}\�N�4�Ca]T����1k��C���������S>0��7�����hq\"���G%���x�#�.
�_^^Vk%j�����
E���D���t�b����hJfx{{�,�'��%&�.��|���SM�����{'�L�e��������fi\��
]S��1�C������Ral�����������_��q%d�"	}k��%��ti���c�~#`-K����fI����v�\��co\�)9L�'1�0��Dn�#K[�;���d_%�+-2��FN$�|5%�N���M�����)tK�{�K!���,s}��eN�^��C\3�y�m~���a�K�w�D��3��F�A\r������|{j�]j��@�G�k���$l�0s,�.������e��p�������������}M���,r�)�i#�����p���"K���S���t2gJ�{,/���:Ir���%�Zr�Z������x\��	�]{��� �|%���%e�S���^;���aN�/�^J�J��3��q��#��`.Z?�����
���i������Ar,Gp��YK�~�v�7	��~��i��Oa��V��a�%��c^�����.���0�S��<0&N��YZd���f�����5G��jn��Q,��~��"��en����8��*����p��z��Y��J�-EX�s�w�J�))�p�1-�ot�c�������Sxb�x3!�h3�
�S��8G�s*�n�67�)���V�c�1�{~�e��9���sd�{"��N�Y��Yf���mK�	�O4�p��
���0��~%�1K�1?gn���S�i��G$�:G�����fi�VUlK�����Y�Gd2{����iLz���S����a9�/s2�sg�{5'�$����2�u�Y�)�0�vb{��Bg��Z��nG���p;B�5�5��-'TF?���0������8�J*�ny��>�������'��sO�l���$�Rkf���k�gr�_�f�(��wG:�[�e�"���%f�����X���Rh`"�� m"�������o���f�vP���G
�[S�s������,
{��}����@����D����.�t#�,�V�6+\*�{���r�_�f�.��gq�1���,S���K�2��///�_�%�1(4��EP�ue�R777��2D�u��f�Q��)���o.������c_�?>>6Ku�i�	�s��
6&����\��n���1�A��������8�q��x��,s]����2���Qp �C�%�q(4�3me���(:�_�kN�\������
��c��s���g������>G�=V����uP�d�
���,2�v�ad7kdKm7u]��Q�yjN�����[�j�1�~��,
���S�T�8��8��h���.�,s}����2�k&�s{���w{[�/����P~	pW?���,�S������������a��6���2��>�]__g�������k����};E��o��>5����;��c>�x@�-�8���n�9\d�%���������>�%�-����g�)�7�Y�zd�i�L�B��
�����x9�����<���+��x��()�����`od���epi�h��"Q����g���6����<$w�9�D�������1617�����2�!���(4P�(6�v�j_����f)O�
�^EV�����X�b����Xj�[�g���'��]�x!��,P�����{~~��,�����E�s�$�(b"�����"�\����f)/&g;��,���2�D

���?~l~�j�RZ,��������NK�	�����8�(&�+~�@���@���S���T����J'�F�Ts�-�2�����2p WWW��<j?<<4?�+&����4?�sww����/�O��eP�?���|���YX������y�X�����d��J�8�����~����	`K�@�	�Q)4��"�O�����X*�{�L�L�8�O�>5K����\�d��BP���;E���:�6
�����,�Z�����Y���������������w��?����U�������������������hn8������0��o��g�������������V���eP����tP�?��
(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD������2����%�iN��2��d�����24�Ph*��TD������2�����W��;w�(�J�2�#�%����_�

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*wuu5�����{o+���~�����{S���h[Km3�����d�pl��)��R��0G*g�7Y&[�z����6��\

���m���4K��2�#�e�

��}��,m������<�L�d���B@����fi�����|�L�d���B@����fi777��|�L�d���B@�o��5K����{���,8Y&���P����YZ���s���,8Y&�����������=>>6K����i���2�#�e�G

I�P���o��z���,L'��@�	��(4$=<<4K����o������,����k���2�#�e�7

Y��������:nnn��W���o���2�#�e�'

Y�>}j�^}���YZ������+Us��d���2����/_�4K����o��y~~n�^��jn�?�����vuu�[������}�XO���������3���v]����������!n?���65���m�~�T_�\
bs�U�5��!�9��G���q�h[�����7���k���S��q���2�g!����s���������v]����������!n?���65���m�~�T_�\
bs�U�5��!�9��G���q�h[�����7���k���S��q��\b�y������6r�n�����>��:�>�t���C��[*��_�Rc������F?C��k��1���*������~Dk�r��m��hK��o�;����q;�d/LC�opd��m�?nn}��<t���FOOO���n���.�?c-7W<vl}����q���XW��Zb�s�xn?R��k�������]��������f����f�����9��-9��b=��mJ[�\
s��?%�)5w|��.Q�o�)�{������n��z���}��z��xM��m�v�5vj����!����������a��b]kf���c<������J���C����o�T_�w[�o�m[Km3�Tq\��W���[}����6��u.�9�������;>���(������}X[�o[7[I�N���D��B��M��d��z���f?�����q��������u��5��qs�����]��?�8DK��]��������f����f�����9��-9��b]s��=��2g\��/�O�x\�zkk��I�o�����\����Z�pb�/��lC��m����"�75�I�9�Kj���9�_����[I�;��%���vl��)��J�m
������R�����6�M���m�8�����y>�Z������}������/���y�t����R���'s�O�Je_m���������-y����^�w�sn�Rc���6wl[K��m��L14�S����-�R����S��mk�mF�b�k7��q^�m��|���?��=u�Z�uS[�_p)R�N��������u_O�^�.�����!���}�{����mw�3����������6w�ZK��m��L14�S����-�R����S��mk�mF�b�5�oK�y���mK���d\��O��;U�:�������<���� d}���Y����}�4���s��������z��?~|�����7��z�����������~���}i�7W�o�1�~,�v����jc�)����f�����%r�����v��=���_]]���V\3s�����]�:�}�u��.���^z�P/Y��X��������E���+�'��E��S��Y��x�Z�Y�C�K�	�9���k�X�,�-Y&]S��������w��N�t\�n�k����eRh����f����c�4]��������:JC�����Y+
�J�}��t,�.��9�Ti�V�Oa�1��3��T?�o���XI�	3E��)�Z��=vn��2uLS�kf��0{j_������+���Y��3��P��V�S�
�\�����eJ�������,�-Y���2�J���~�q��2���k��g�{�=�a����e�}�)�7Y�/��}�C�Y��0�aj_Z�L�z/LC�opd��m�?nn����.y��R�{zzjn}����=Z�_Cr}n[���{|��C�������:���K�>����c�
�K��z���C��x|���D����7~��cj��H�/��-��mk�mF+�z\���*��[�k�o������ZG�)���u��I���O��m�������������kK�m������+���k�X��m�k����q{N������c�i�����u������6r��64.}C����cll��%��-����}���uD["�������v�����D�q����Prn�����s<n�ch���s�U2&�V:>���ml|��&n�K��;wJ��>�-����2�x�����yc�{�)�mS�o�)_c��C}ZG�����������2��hq�P?��&_bl=��>�������ZG�%R�Km{�mG�Zj��J�m��
%�V�Z�������cW��hS�]s�>���m%�R�m[�uh=����)��`�R�X�N�$����������������rARi��{|��!�P�R���H%���n+���~�1��J��54�%���O�K+��9�
���Jj�����f�1k��7wn��W���c�M=����5��u�����n����{��wJ��>�-��-��1u��< %����k�S��o-}���d��d=C��m�����5�8Z�>������)}i��9g]!��SIm;��R��6f���������*�zl�����������n�V�C����Yc�`�R�N���������^?��W������{m[��vO��s�L���i�/��t�J?��)�<t�J���1���Vn�s�r�;����m-��hc���o��*9��^#]C�x�>��R��V���ut[I������~�������?`������W����R����7K������u���6K��}xxxh~*�O�{�mg����w>|h~����������}���Y64�?��Y�O��1���I�3�b�S��z��}������fi���2v<k��)�2���������y5&w���j�9��k=e�k����~������k2��������,���W���,S��}�e�%��F���,��^��1�<�k��>�}���)��,s�,����e����k�k�/=�d��(4���S���4��J�7��p�\�2%����QK�����R��+����H������������_�����KI�����Y���+����<w��|��,M}���c}Y�ym�u����S��V<&j��Cd����,s��x�2���8����d�\��d�Gz�-�|%����Yf��)�u����%[d����&�����T 8'���K������S'�
�j8>dl�r��>��8����=gI��}�kd�$O�i�0��7Z��s5w�s�D��ob�����/�}����K��xl�M���g0D�)�l�2e�G"�|+�}���28�#d�G{�-���,�-��2s���c��\�e�Rh(�
J�>�RA��P%g��%��
�K��r�9���V,�LK���m�$���Vn|>~��,�I�#k�Ly��������g��hq������-�U{��^3K����\ss���~"���o�@H�/�LYfK��&�\�,�w�'Y&����=e�Gz��v�%�|K�yYN�e�sF7��<f�LY&�J��X*l���)� d��n�����d�\@�����}��j��B.XZ+�;�����������k���6�\s}}=�z��
�� <B������{���9o���XX�9(��}3���2e���_d��'���,��=�<�klYf�,��Sf�q��Yf[�`�9��,S�	�N��X.l��v}�
�k����k��k��{
n��F�r���o[[c�s���k}� .B�5��K�Z#������A�)��s��ym�:Rob���VXs]�M���z����Yf�,sY�+Y&���,�H��e�i�L�,��,�wW/Q�bWWW��+C�����E����+�����[<E��U*$O�7�m'���Z!Ij����������Y����O�������x���������c�7�?]����V���s��;7sbL#���m��t����su�k.7����-�c���*l%���S�];��`m��mC��,���e���hj�%��d������,E�9On�e������N�w��������
ec{�2��[�9���K�9n�>,1w�C�s�����1��D���~e��l}��VR��wm��1d�G����������������z���Ss������F�W��S��������+��cR�86.9�q����)�����='K�=�[�k�T?�m-��h%r�gI���{������6��h��[_s�u���O��
������}X[�o�P667�I=n����~�)���-�-'7^C����>��KNn���'7�k�m#7fs��Rs�����"��h[Km3Z���Y�b��^cs������My>����\j�����S�?E��:���so����6����gR���zy�_{}������I�C�2$7f����s���Fn���������5E�����f�K������\���n�8�s�q��]�[_s�u��n�1Y����][�?�X��gT/��l��*�sDE��.e������fi�"kJTC�:/>�T]�q����5?M�7Q�5*��sST|]K�+Z�7���6��������*�����h�c@=d�o�2�%�$�2�d�PF��������<Y&a�Yf�c���n�)�\�,h)4L�
S�&�����A��-$��{�!�&MW�|�[X�w����S��<�p{I(!v�)�s���6�&��,���2�G�)�����ez��Vd����,3�����L��Sh�,�
�.�$W��(�L. �U��U�>�%�UO%�Pxyy�l/}#,�����1� {�
�*'���2��,�|d���Lj#�<�sg�q�x����1���0Y.���x�Im�j�[�0l�����l�5Ua>��0�J��}8g�y	aj<g�sO\sK���KC�6��+�-�����ks�2K����>v��h����e�e��d���e�O��J��>Y&{v�,�k���e�O���4�l����
c��,3��y�ud�@�fI3�/U�sn5�B;��h��S�W���R�c���%�U�]�:M��S�W�����k����q�S��v"n��c�%�����T<��R��e�lI�y,�LY&�E���9�L��/�,�X��e�K�������1��@�e.��x���,�

����T���,z�P$�1].��a�s�����\P�4�\"w�r�������p�p��������N�����|�e����=V��.Yf�d�o�2�d��'��G�2��^�,�-Yf�������f����@7�\�����2�#Rh�%����Th����s�v�/�����������
��T���r��Z���
���>����6Ko�:c�s���<m�&�����9u����2��,���2��e�r{�2��������,3~�{��u�E���|$���B�l���������s����j��9�SrUg�"u����I�{�����;nC�c��C5�`#������v(���<�ne�kn�1��G%���,S�YJ��Y&�coY�����e�2K5��L�u��NM�	�;���R�a^*p��3[��.�Oa����������m[c�G����V0�����Z*w���@*����:�����Mp�:�i������m�5�PX�8��N���r�h�8Nkh�c�����,S���e�2�d�o�2��2��Q�L���#��e���,s�:���,S�	�N�`��`*�l�
�{�*���D�m�sX��\��5B��!����u{~��=��[_}K���?����C{��Jn<���7��A��[�f��c��5���O��~�c�e�e�2�d����,�-Y�8Y&[�[��5�i�2��e���Yf�\���+���^�,��B�"Q�/B�T��e�����,�.B�%�O�*dj�/]�zl-c�:���-��%��]K�[J.�,9�[��k���@����m�4]��[�����s�8.���7w����:�I�)�l�2���q�LJ�9�,S�	�:��ez�}�LY����r�,s�����s��,S�	�RhX$UE1��T�t�Pb��eT��J
�Q{��zjcc<Ga�1���k����8��)�����}C����C������s+�����
��/	����q����X��yi�����O:�V-��,S��%���,�w��yd��L���,�k���2e�C���#d��c6�XA����A�)�^)4,�
�"�HU@<U(��&����	�Q8FE����������1
�b��fM���&oG�sUi� r�����cYbh|��1��\��?E�uhL������Rk�'��r�7�?cc4�\S�2e�}��W��W��ed�����L��OC�YF���(Yf���s��m�2�,����J��\������S(C�TQq�T0��[E�S
GC�q���G�����TZ=�T�����M����/9n)c�2L9��sJ��4t�.
k�o������L1��D�kl3��M14~%�)�8Cop�����2e�]c�2�2��e�:��ez��=Y�,�k�X��d�Cc�.����{!S�2~�z��Y�@����pd��mS��%����J��:"������J��Xw��1������H����77�����c<u|����T����{\��;�)q.�������>>>�z�t�9�B�c����1j����;�\�>���)����\��xM9���g�����;K�epid��L}�-��E��7��lM�)�#����2`]��e��5�,���cd���rm��e�l;���1��#��:W���,�E����1d�G���������r��t�OOO�u��W<&��%-�W���.���R�3g\Bn���S�=�S�g�1.��n����)���m������[z~�=F�vS�4��_c\b�sx�1��\�:�g��u�t����R����L������6���5^������k�l�����?7�S3���x���=��r�m��q<�-��\�r��mq���_k�Q���>M=���XG��r��8����Y�o�)�{������9�#S���)r����k�����b��u�}����\�OK����Tn�m�:������k����>����_ko;�!7�S�h���:r������iI+=Fp��u,���AX�s�ze���]�tzQy�����i���X��
��n�1�s�(�2Se�J��C��k�!K��8�|�����_k�Qw��j�aje�X��s��x�Z�Y��������u���K�Y�5�X�y��q��2�L�h�Y����c�V�#�<
Y�������x�q�����s�Tfi���k]g�g�SShXEj�Y���^��
�"�iC�%��%[c���{��������-E�#������\�z�s"��$�������1���n����Y*�G��)b?�1[�m��U�x���8.Yf���x�,�t��K��!��\�z�e��2`�=g�^co/�Y�����)-������$�s�,3�;���%����M�T�}^�"��)��T�g�SQhXE.�XB�-�S�����}�6��F�Rf����1>�����_��}���
"s�2E�-�g�s����_K��v�b=�vs�n�}����\_��r���1��G�G�Ok�2Vm����q��o��{}�U�bN����K���L���;�G�O�e�2���l.�Y�,����>���{�5�i�X�e+��hq����S����2/'�\����S���,��q�[w<�O[>���S���O�O��S��=F[�
�W?����,S����Yze�����zA����)E�����s9&�~����r_TF�����#(u��o`=��e��d���#�e��d���,S�yd�L��,q}

L�$�u}}�&��j�K*����`{&���L�L�	��e��L�L�	��%�O���������#K�!s����7&���o���2`{�L���
92Y&l����G�/0AT��0��s�8��}���g[�fp�d���,�%d���,��B0���c�����O��+�����������noo�[�I�!���G��)�2�K��)�2~�z��Y����U����%������-lq�E%�T�\�0���Y�,Y&�$����epn�LNI�	�%K\���@�T����?7Kpt��K���a��#�dd���e��L�F

���������������������������>}��,p	d���,�9d���,�����2�����W���DH����[�?G��\�Q�6[�j�� :���_�����C�������78�`��e��O����2��d�lM����2��d��Sh`"'!��
S�����,?~l~�t+`w	�`;&��5Y&{#��c�e�5Y&{#��c�%�O������oJx���4X��2��,%��m����d���,�G�	��d���,�G���?���"fs*q��(q��A�
PY&�$�`.Y&�$�Phf���rrj�y��\d����u�er�L��er�L�vW/?4����j�^B��s�������o����
?}�����/�o����������r�|m�y��pz��e�u�er�L�7Y&� ��d��o���)40��(ar.p�L�d�������BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���BP��"

@E��(4Qh*��TD���Bgv����*�����k�������[�+�������>W�k��z��:��}����!��u3�\&������K[�A��<�]���k����5s�\f��2��/����m�|��c�Z�;Z�c��Ow=P;�v����YZfN�p*�~����������8�se�s������K�|������U���<K�!�K�����������K�T��9dd����CN����C������#h�#�!^[�����G}��~�������B;�V�����,�K�mP����Y|/��2�!&�������1(}��!�Kw{{�,�{����,Ms)���S���;9y��`����99�^�1�2\�s���0�B;�Fp��H{���O�>5K����2���3%��N���b,!�.UL��>?��U�����T��j9����1��]W<��{p$k�{~~n��e���{z��g���c�%L����-
^L���;�5����t���+�������
����6�?)�s>s�!�KJ���Z���s���2&
�2�~k3��~G���	��I�����[�������9�rO����3�����(4�3��ci���2�
S�!&�v���_�6Ke.��2B��6�|3Xw�p�������C5�N(������1��\��*6P�-]1I����$f���k�%���9��y����/��<W�	�)4�3�o�i�
-"$�I�u���	���\�
�������n���P9$p��9m�d�Kz��(��n��{��]:'�[�����t�e���J�/`�v����Y�enh��f����7?�O���	���\�dbqw,T����d���a�d�pI��s� |���Y�����Y�}����[0�?�fNC���5����+���������-�X#HX[�o?�.u���S�����@	9$p���m�������)Ybw���pj�o����o���_3/)�����_�K~�4��)4�C��eju�~��$HjEb��-5WWW��������������C������{���?�>����?��]G�������u�~?U�g�:���2e����>����u<R����m�m��c=�u�����G�v��Hmgk��TO�����LUu�j����#�e�ms��]G�����=ZIf��s�����I�{�n����h����>�~?U�g�:����6E������s$���:��v���6����Xr���e��(���/0����S����8��G�"����R�n��%E[[d'�ut����6��x\��c���'����������h���{[���c�������PI��N���*�������{y{��n�Q��]0�Y��[K��
/��W���ev��3���$3�����0�w�����tU�Q�p�s��}�TS�}���/�t��g��'B�����������G���<�ODf~�s~�.c�������u���T����Q�X~�:b�cu������y��j[���6��x���������.K'�M]xo�W�������xl������e���+/o��t���qM�W�����u4���r����J�q<�������:����u6���6����������6vY~Lm�9��]7�s)����i�-1��u�+u����By����U�%]L]m�-��n�x�W�.��v�~`\�>wn�yt���e�sY7��:�r��5]_��t���6�2��f�c�6���q�|��)~�>����,]G���Jyy1�����91��Gm�S���~L��n*�k,���U�1M�S�}�E�����Rl��M��O����^�_����v�e���J�5��|m��5~�����}D��M����L&�\\���6����5]_�z�>��v"�h��bj�{�yM<�����S����6�����y�yN���:�e1��S<��.�����P5��k�����3��6���M�J�q1��o"=u��n�T���6�7!����`8��u1��K�v"#�~�L��c���i��6�"���7/,o��7�tv���f}1OT-D��������Z-����r1���oX�m�s������:��y�xN�c���i�����)��:]���7YG(��D<���m��-1o�s��������5�ke����[��s���`��������h��Ti��r6�&���7�Y�e���"3�����U�Q�4�Y5����1S�!�������Z�����7YGXw����t�OE�m�G���.����-Z��C����wh+}��2`,���m>��X�������I���<�<�i��Lu�e��iV�����qrr����K��&�a���6�d��&����m�S(�oM�UW}��c�V�!=V�_@s

�TU��4�JC��A��0.��b���j0Z�p���b��u��1�����d�����e�eWm{h���P��������	%�P�X�TT��t_�mS�W����X~�����#��'�ZG<��?Vi���m��������P������y����}[���1�����p��6�X��Y��'�Ve.�W����YH�������m�e�(�1]�\r���i��~*����
�n����#4]OY�����m���������v@i�}�!����c������k;���k���:�P�:;��vVeA!�H��&�����4��z����Jc��R��}U��T�W����X~�c�n�n=a�:�1m��&�7����s���m��Z��t��1�Z8���e�������-�va��V�}����v�������?�R���bu�g�u�*���T���mH�U�k���}S<~��W=&�:u�W��z��T��U��F�����NzL��a��u�#]n1�����O��X����1��'��z^�T�j�u��j=}����zl����9�������J�SS�����YF���`;���b��������]?On�����C�cV=�����.3)o[�T����-�w�������v���m(TmKS������t�m����9���i��
m�G�3�7�J�1_����ZO����j���_u��lK�,?���_��U��\>iXu�����t�!����m��J����������^?0m��G��Y��3{9�H?c��B��5�?~V�~�t�b���mK�U�k���%�x����zLLu����<�����4'I�_����nK��������Xw<��S����j�!}N1���t�����W=�b�S����k�������������l����}�|��^w���[S�����YFj�}�0���bb3���DF*����j�r����dSU�fONNVVx������*�m+s��c9Ub�<��{\�7��U�L�u��U��n������e���o3*����M��nk�k���MZ%5����?�:U�<�_Z}v]q,��*������U�_uN���sw����Mba���*����U���5�>�����k�0/��l]���R���r�~����dU&��q�����}�O]�S���r�����.[s�!�������G�1O�i+����<��X�=����u��CV���)��uWe�US�[�������_`����M������u�K����$_��:�r�u�������7��}cJc���l����R�c9u�H��}4���JU�Y5	�Qh`��B�u�S�t(�H��&�_!
��q�&�\�����TU@�n��XG��3�t=�:��i�W��Y�����'�T1�~�z\��b����M����j�/vy�6��6Q��!��}kr�k�������g�t�l�����c��4�Z�IfR��u�RY�?$N?�7Yv�j?5��d_s�!����X��6�Y�6W�Sm�M�������y}"=�u��Cv=�MTm�s�6�����Z�?(/�M���]f'M������q����M����6����'�h��������lS��{1O��b�cS��}v�{�u�5�2��Z��6���6��2�F.
_��N�p'
���ZG�@�*,k:��������d�4 k�U
\7���.A��C�*M�i��T7�3~9���m��������9M��.��]����oqG��M��m��5�����i��6L�����3t�A�M������rz���m���s���4�1�%�� ��9�+2����f�4=n�s��x��C>7�2��+r�M~o��w90���V�y���[�2;����in�d�4�J��u�2�Mr�:m�q����56M����_�]��b�vq��[<g�/��F.
_��N�p���^]�m2KC�>��dMW���l�`��4�/�	��Po�!w��c0dls�D�p0�s�1 x�>������������}�1\+�mM�:��6	�S}�#��1|�n�l{�W������iV��+������CV���i�
2n�� W
��T�����1S�do���K�f�u�*����6-���~F^����]y����B�O�\���TH��6�_���.�Hs��s�mK������}��~o����a���B<�&S�"�������B#W���i��5K�Pc�m�4tm:��k��Wh��0n����i�7��;T=�����/E���7T���u6
$����c��sw�&����.��u�������JL�K��Z�I���u�y��u��P�m�T�l��g�>?so++j��m��-���i"w+�	)�]����XOL��4���>�m�Dc�!���}K�eU��NEyrrR9��k��)���W�|��{��C��.�������O��O����YM�Ju9Iz���9��U����_>�'U��m�T�t��/����:U9g�d�%����*-�.K'�M]xo�SM�	�o��b�h�NNN.,�j�P�������*okL�mM�{\�����:M�u���J��1�i:_W�lK�~����b���b�XF,+�xni�����tyn��S���5�:m����r����T�yBy?V=�M�dW��ULu��J��>��)_��iH�^?0.M>77�'���:]W_S������j��5y\��1u��^>u�)�<�.�.��|]m��&��P�o��l��V��S,+���VMU�����T��u�y��Q�������<�*�rW�*�>l����	��X�}��^����T��u�uS[m�;�P����v�~`����h2O�y�*W(������������|M5��*���&]3��XT-�iF�J�����t5���4�W�t=�������e��b��S���S�t�.�-]w���i3������8�)?>�*M�I�\�����nT�����V,������)_����\z����fZ��P�������m�[���vR9�I5�����)�7}�U�v�����i<.�XF,+���s��S���_+��nzN�mCCT3`?�C�s�v�-�<99i��Eq.�y9bd�U��N�[� �rn����o����j�w����0-M��4[�7����1��n?�����F�_���~o1�k�����BP���]��(�)���������{����t��D�>;;k5P�����p�|m��&��>\+�7�%
lJI����q.����Bd��CF��J�S1���G9�xL�ZY����i�'M��r^3�"�������g�}��KV�����K�F���H���Bb9��;x�P��iN��`�o�x�md���U
"��"�����]M�Uaw�]p�~��XW��c��X�q�iJ�~0���U�U�`H��l]�[6�����Ch3`ss�!����b�bZ��c?��A��Q�R� #�,�v���(�������)�Z�>���%�+inX�o��YD�*�2�������au�\��EO�/�+?�M�)��b���>T�k�/`=�&"
?�o;*�9}8��QzMap]z��u�5�.���q~������j o���"�.���c*��V:��Kx��?�������Z9V�x��=��l���w}dEC�{Y��M
�yR��g��b����m��>��.}qL9d����]]+�j���H������,�i����9O����g
��YoL��2s���r���`��4�)�t�b����@n�t	���q�	��]^�B���W|W
pL�i ��V��c��N����/���V��~A��
��z�N�������h��_z���C���b�9b�M���*���2�1����/N)���s�s9�cu��[�g���������T�Qm��cz��|'}����m���y���~o���6��r����*�I��	IC�" ,�8U�^[i(�d`a*���b��TS:�n��tv	b�����X�.C�.}f����C]�S�����}�&�n�\6=����9��eC]+����V���~�m���U�W��KE�Qd�ctY�.�9N�<�Kn�����}��C1E�~�T��u��c�*N�J��*}�WM�����s���m�<�>c�$��t���������;f�s�����U���g��4���s��E���m����_�z

LHV��j����/��&X������}7�m2��,���������J����zM�o�W����$�g�WXW����WM�y��n_!|C]+����{y��6�5��c���
��:��n�����)�p���y���lSU^�5�)��S�!�2�*���~-�z�c��3�����nr^5��1���~�{Pv�Ts����>o�<��:���4��6����a��/c�UX]~�C��mU��ub��?�=&�>s�1��c��E�2��S_��Vn�����B��Xi��W���#v5
����m�}��t j��"d.4���*|�f�����M�KU_��IH�t�Bz�V�~�f���U���|��u��SUj��&����!>[������k}�8C����s�t�5���J�M�H����i����`���U�'�`Sc�!�8���C������M.cR�o�3��2�1�����s������`����;���}���4�G�����_�����{����0���OU����4���Rh`b��B�!���w��s1�lU���jv-B��9�6���
%��U��������|����U��/��W]_Y%��u�1���c��'u���c�m��CU_�{?���M��Pv�keV����J:�=������#SJ��i����O��`�P���q�Ee���?�/�e�fE�S�W��}�I��v�s(��������?����tY�Xs��<H���)��u�����:���@�|���X�:�_V�SU�B!~^�]5��*,�f�Q���u�������u�1T�Ii�Tu��;�����?1�2�E��M�o]����-���V�a�qn�<��`�g��m�A)��D�K�@�u�R�{���������V����e��r�R���u�H���{�4:99i��y�����F�T7���w9����+�=k�����"�,�a]�i������T���s�j�u��l�q���.��;�C��]T=��}s����~�7��7������!�}��:K����q�g�0���v�<�*/)����q����{��0�~sU�,T���y�y������94����X�~�Zn��-[w���#�O���
���!�o������X��U�(�Nu����O�w���xU���2��H�K�{�I���]��������^h�emW�O��
C����=�!��U����RdD����3m�������U��U��;�e]��.�Zu�l��vQ��M��0�ke�:�>���c�e������`8��-x�i!vY:l�����{���N��bZ�������o��R/}L������4�.?.�gm���fZ��.�~�����r�j��b��q�}U6��1��M�������������6�:�J��T�}���:��[�?�����d�������@3��r1
i���e���1O�1m�X�*]�=t����q��m�&�)}L��W����.�+�>.n���?�:&]�����u��/�o{�t9��j_m��9�<6}L1����U��<__S���J���2������������}D�����i���s�����1M���B�������-�K��N]��t�l���*�H�q�}U6��1���M���������������U��6�<6}LLM��t�����sn���s�R^��s	��*_����\z���������n����FTs<99i��xL��B��
�-m��z�^��J�M�+�������j�W���Z����q�1)��F���T�v����'�m�����x�6��8�eQew[��V��8�]*`Gu�����1�g��#��`�x.�����?�cl9�*��!����"ki�G��������������m�]��P�1��4[+��m[��C�����o�������������)�!����W��}��l
�\b5�+]����q�1)��F��������������������j���.��m�%����UP��c}���"����g��*�����������0�GL���o��b��pl�c�����K���X��ut9&��t�@�un��6�T��s�yb�.�p��U�#����m�[2������UmK��0��O����t�t�s
C�������P����u9N��C�8��:��u��+���$k)�cq<�:&��t`��;w�V���b?��+��)�i�?����n�B�vmC�9<x� omG�������8���n+��cy0Ei��q��W�1;RlKlS�Q���E���\%=>��������k�S���xT���1��8��},�/C��mWh��61���0����b�����/����72gy��E�z�.`,"�y�f~�<�v����F@��Pz[��k�[�|��G������>�u�(�`[���+���0?g�i�h��D�h?�lw����d�p����d�%��?�	0n���]���@Z�4�^��H+�NI�/#�f3�>r�$���)�y-���]y?F�E�}���'�/)3���Sh�H9��A���GT���y��`1�
w������/��A��J�}�m�C�Wd�Ed��M��<�oM���w��9n7��?F�}#�/�/�3���_0w��g�6
���]�D�z����V�����;2�����%�c�~|����/M�A������,Q�	�6�u�-���[��[���r��P9�on*o�>l�.�?c��WL�,^|_�=��t=>�_�����#K����_`���P�����Nc�k�U"�L�m�������QH���>|�������!�/��� �������E�>�Q~yb�����9�"�>)�����m������������e�3�ZQ��]+��i:�3�N:Ni��Bq,o����7DxYul��[��������}j���>�u�(�`r����,�X�d��c+C��`��2��rQ�9����_�Hn��1�4����@K:!cP��� ���ok*����w�*.�|�j�/
�m�Y�,�M�!�)����om��o�/����}�d�-�<�2��uc���/�//2�r3����{���%��R�/���8>H��j#��c�-���)���J3�}�P�
E9n��`�������>�~ .T}�����������)l<n�_^d�ew�_�s�3�ZQ��1������!��UZ��*>|�lWU��p�����8��J�~Y�^�q�����o`����9����2��� �����9|��L�=�-�|�2�������w!/��X�}a��sr�vd��&K��B-��@�S ��@���K���(43����B0#

��(43����B0#

��(43����B0#

��(43����B0#

��(43��@���qv���l�X\���G��s�Pw~�}����}���������y[^NL�u����.�����gV�WU���8����w�FL�� ����x��+���S�/E�"��zJ[���]��k$]�]���7u���]#�-��6���c��|l�?����u�9�kh\K��kq��)�{��=	sW��]������Z�i_�[n��5���V���"�[��6�{����zwF+���	��������oW���)��y��U��US��m8�a<�s��9��Z��h�S��g�3'^o������k������yH�^?��� ���S�>�=9}��W�7�w�p=������}�5��������u�5�u���K�f��g����58�XW����m���l��P��I�^���S�LA���~Y5�9�0����9����c�>��>�|9W��lf�{�#i(*{��B����s����V3�^l�7n���m�
[<�o5��
qv����V=�>�G����|k{�hz��uy

M>;;�a8�|?���s��e����lS���M�����-�Sm���)]t�N�-��m^�\#Y'�f�=��/}��?�}
M_����d�@�|�b�xO���p�>�5�)��:ptt����y
`
��O��Mz�o���/��%��R�/�l��w/o���!^\�)^T����/�@{U�L.�����c���8�a�|�-�����8��s�u���8��U�Zu�����^���0��������9������60w��lK�kx���M^��'�o]����������{�.���C>{�
q���58l������i4n`�|�b�xO��2�6�f0U�����������kcWu����i�������9������g��b��0]���^8�����?yQ��<o<��gop/�o1�}��9G��0�9VL���B��U]W�������_un��[5��6���l{�y�}Qy��iH�^?��� ����U�+�}O����MT��������)]��7!�;u��P�o����������5�U��QL��`j�}L���M�c��q�c���������v�~�_��\w������xO�������'������.�}oU�N������� �{m�L�8��?��>��f�v�\T��bb3�`K:!������E�������kM����`Z����S��<�&�}��u�I�����)=����6}���U>���m���|�k�>K���a����� }��~<�}���������?�S����1��t�N6}�k$]��@�������s��,=��i��������Xy>�w����e4y\�:S��<������m�����������������\���tyh���<^�r�k�O�\���9���o��&��B�q}���|����c_�qh���D�Y:>>�[�n�������{7o�K�����y+�����n������<�`��:�������s�!������V����D��a��"~��B�x`�����u����y�^����^w��0�.���_��������������M��o�w����>|��.���>��s'o�,��&\O�Bn��t�����&�w.��[�kW��c��<����&���7��@5������@��&���:�k���y
`
��SY7S"[��f+Pypp�����}M�����q�w�^�:_�`i���oMQ�^S|h����r0����-��z�u>�n�}
^�{m`�\R�ALMyON��?�b4�c���)]�-3����y��������$���6��������e�
L��P�;�I�9�����5�N��V�sL��;x
`�d���:\��0[�����N���0��6�e�}��_�s1����kYZ��ix�e]�����30v>���<S�ks�:�6�/��_��[MyON�b0F�� �S��[06>{3���[
�����Oa�}L��o}�G���g(���$P��?�����}�x
`
d��+�:�H��g6�2�^6���
�Q�q���s��N�"���w��f��{M���j�/���sM8�aX�o������H�ks�:H��������{r��o}(��>>;����������!�ko�O����>����>�c<���������^���?������\����T���G�u8��0K��8n�*��40L��;�a���jU�v��6������5�{.�/������	�3+^{��N�Rt����YZ����{m`�\��oV���{r��~�C������E��[04��R������l�}L���>��&�����9�I�W������#k.g�q_�����B��>�3��f�v�0��YJ��d��tY�n�Y��D�>�V��v�T�^w�7U^P��������*~A�@,W*qn�}�/PVq>���p<���_v�}�A�������u��J_������l*�G�?������\O�Bn������,}�A�6��������]�uu�����=>C�9�I�W��1'i�W\����1��/���/�I_�5�)�u��d��"����V7��`
�FJ�P�9
��t
��_����CT�-����eg�2����B�0�������;���k9�p������1r��go��7���$�#�����@��1O�����`�d��O�`����`V�t`�����:[�"�ptt�<�#L.���,c�>�OT�-�h�$������l
�c��l�����2?r��go��7���$�#�����[��(��qm����;�y�������n����@K������|��`V�>t7�o��u�V�6� 988x�����ge�z��/N���������t�^�N��3�
�ks�:H����`�m�5���-�Z�����Y����[�k>{3'��c�/M70}>C��'a_���9���������C���������g�}��B

�U��������|�n��g�n�k��*~�~C`��8���������sU���s8�����@hJ;A�}m[�,�+����7�p���{����\Qd`���q��]��$����I��
f���p�.����&����L�*��k70�t@�����J�q_�,=��Uq�L]���������g�Y06rv�go��7���$�+����[�n����s���4o�����C���T����E0�������v>�[@Wi��U�_�����|���
�w���[��?NL��)����s���vQd�{�yJ�����)����7s��>��R�����3S�=	���?�(���:���g����d��}�����Ph��a�\-k��T�2�_����S�v'��f @��:Bh�x]�E��)�[y@P�?��m�[0&>{��`�'a�����9s�C���V����E�`�6`�F|�\,��m���}N����jJ� "n���L��������:�aw��?N�M���{m`�\����~���m�xON���(:P�p��rN��������m]�s�dSm�k��>{3�m�1���70n>C1��0���fV�q�LQ�~��+�f�m�O;_�B���vorax0�!d01`z����}��/5�I��������t:����V5����sdu�����
yO����2&��t��7S��>����q��3��{�����kS �������/l�B�,m��or7Y���`�-�>��[�n��v���]�s��+m��TT9�aw��h�ks�:����t�?��3&��t!�`�|�fJ����_6�S���g(���$���]zmm[�,-��5�)�uC�m�i�CQh�������e���y�Ua����}F�qC�)=����l�og<>>�[������\>��np���������2�>���i#�s������:W�YU��zJ[r��5�m�f��������}�=	sS�q�Li_Ln�ST�N�6h��0����9���f�v�0�rM>��<�}� ��l#�������_������?�����i:x���{y�\�e��!S�
�i����s>����`o�-��/���'�^�;�A���"U�'g�8�m�t�B�gM����������}��>���D����g(���$�Q��6���cu���|�Y7lf�}���V��J��t��|.��};��^�S~�=�s-�����'�+/���prrr�<����u��c�u�y]�9H��&���c��;�3�|����574}O\��{�����������7�A���o���xO�6��uM�����\Oi"��5�7�c��e���U>�1���.�����0��w+����G_�1�>_W���_��Co�����g(�Q����O�����)����kSP�s15���c�������[LM�h�W��8���F�~����O��s��x}��m��g;`�����������A�I�)���n�����q���6�����^�rx]���?�,�V�VK��x}L+���y�~+`�����srr����:�
m�{�
��� �R��:}]�������)m�-����m�1��6��}�����`;|�b�xO��5�)(_���{�Vf}�u<}|���N�
�l�O;_��ET��4�X,��sv!L[z^����w�7n,o� ��^x�
�}����\��!bq.���0.�/"��;��\M�5��%D,���t�<@sU�����[�n�=�����gN$�8����U������~~������:K���a�\����r�>��������-���M�O\OiKn�����������w)l�������?�}%��]7�L�/�����������P��I������f0M���ku\��|L������OC�/���fhI'����	h������]�
��0.�|(o{�h\����|��ty�����^��]g��L�
�A���O�����{r�������S��[0&>{�Kii{����������\C�7��,���P��I��.��^3��m�S�LA��D��j��4a�}����,���f+^0��3����xS
����#���e\1�_2�f����6���gN��-�9����^�;�A��{r����.�������F�V�>��6�7��l�6���������34�=	s�5�)h�O�Z���z
`
����`�����/l��L��VT����}NOO������s�{����j�@�^k���M�[�>�����
������_���������=+��P>��x
u>�0���U�[����k�0@�@��q��6����{r�R�����u�zJr��gov!�E�w	��c��<t���
O�	l��PL��$�\���U�f0v�u:��q�w��e}�����~�`�zO����~�����@K:!������2�)�%��R�/0

��(43����B0#

�H|���y`{����������]~`{��"|�A~`��Rd�B�#������"��(4Sd�%�`@����D�`�(2��B�e�c��l�"��(4[��0F

�(2��B�3E�1Shz��0v

@O�@���"�T(4�g>����0������0M�S�����������[�����f�����E��Qh`O��)�"�������c8�)Rh`��)Rd�:c8��Rh`���)2L�1���)40aj`���N`��(50E�Sg'�� 50E�Sg'�/�50E�Sg'�O�50E�Sg'�o�50E�Sg'��g��mX,y�9���6�{��������-`Wv�%�2�1hSd@�	�!�XmE���o/'�?��?�[�%K��B-��0�������e���7�05��?��������s��?������g?[���0m������2��W38(s�dL���O����l��������������9��O�l���A�>���@�m������eCq�f����/�O~��e[�g����s�9�����f�����e{��E`l�����w)�������}1�U>����4�������D�|.
PO@���rL�����xO������w�.���I�5[d����E���/��t�~3g���E/��sW�����0���)�c8�}���H	��)Rd�:c8�9Ph`���)2L�1��\(402j`���N`N50E�Sg'07

����"E��3��#�F@@
L�"���	��B;&��H�`����L��PS��0u�ps������)����_*2L�1�

������o�Mvtt����)�����������i������������y������(���G?�oL�1���H�����v�b���z~PG��	��)����_+2L�1��E��@�"E����z+�XG�����)z��w����]~`z���(pvv��H@�Z��L����)�"�������c8�/�t��G)0)4�ej`���N�z��g�6
,����]�����y+��]�����y��I����e��~�����//���8�a����W�fW�\Y���.�o���	�������V�����|�����������:K�e���������~���-�sxO��}��g����l���k�K/��l�������������l���d�}�K�)��"'e�����*��O�l�{�/}�K�6��s��������'?�����o}+�����e�`���������[����r�����>����;��N~�����1K^'K��B-��0m��0������

�����v�%�2a\����B��?�el�I�Dd�vQd�h.y�,���9�G���s��Sh�9�e���1�

�H���K^'K����_z��0E�Sgg������?��?�.�(S�l��cY�����M�.�/�9�z ��@���)2L���p������o�����S�,�������b���T�?+�X~����)�UL�2�y���YV~lL��,��}��s-OE!�xN����z`���"�)2L�"/��z��C��#�ux_(
����T�u����/���}���y�C���(2L�"����5.2P�Y�����~�&���]n���~��'y�C����)Rd�:c87���~7oU[��&�X���j��z���U���?�>����l�BL��G�������,�)Qd`�d��c8_���8�����-�/��������}��G������y�6�Y��]��}��'y�G�vj]H}||�����ys9E���;�mFC@
0�L��"�I�	��������k����M�q��bo��F����[�cYu��o���.+r�b��*�����w���n}���������k#-h��u�6(4�N���:\���{����q�s�%5���e���?�L����������#�U���z�������?��E������ob,�WUT@��������d����*�xGp�J�<��]P�7Y&�J���"�`����1oQ��?���U��O?�[/J����7��[�}�������/����������{zz���E�]U1�J���O��?Y&�H���#�`�"�|��w�[�&���_'���7�x#o�g��j���'��-�
�LU��p����u.
����Ivvv��n����{���;y�o�E>���/>_��������]=~�8os$�`M�������I>�i6
L�,��Sl�����yk��_=o]��[o]�i����J�2wa���Ph���Us#����7�{���p���.�s�������l����E��A�?���{�),za�d���)��M��M�Q`�d���@�i����?�[�

0��jn�L7�V�
�n��[����@_���h7�[Wa�Y&�F����4��$�K�	�)6��B����w��EU�tZY7��s���y������ ��v���p�o�2�"�`��M��M�Q`��2�;��Sh�A��sUH��������"��p��o�R`(�e�/�M��M�Q`�d���9h�5n:n�{��^�X,:Oc���e

0*���yvO���23E(�2��9��'�|������?�[����yk��}��	��G�����_���`X�PG�	��)2@A�	�\����G}�������y��7�x#o��}�>(Sh�Q�q�F���*���(2@[�L�@���e0s,6���������}���y����~��.z����V���������)
 4)�0�}�>hB��V�=>>�[������sGGGy�E��*��E���F�!h1a���l�e����/�1��uW���/��G�a�#Y&S���lsS��Um��2�t]�Z�#Y&�7�b�����������?������w������U�)��XWLo���r*2������&`i��������U!u:o]�]hc�E"@����?��2�,+������t�O�e��RE��a�:b�b�S,���e��(�K�������
n�s����QL�XV��XO<�5�'�`��^d`J����d��:�s)�u�)���W�������uYi���SA�	��e@3s-6P���0��:�|�I����?�q���n=q{U�Y���u�y��7�������{(����4�j�`�`0�o��[Y����/��>z�(�s���]VRW����.4����+��Oc0h����b�U�j��:vi��W5�L`
��2�Ed��(��S1<n�M��,��c���5#��i]���g~�a~��k����U�gU����}/o5�(/�\x��xL��������]Sh����{7o������"�y��2�.KC��#��wU�]hb�EB��m�c�
�m3��J�u��.�/���
������2���]��&���g�a�����Zl@�	��e@ss+6�T�q�
������b��O?�[�Ue����z��7��sCl��B&*�����rpp������.�T,���.4���f������j�~�D���]n����aU�]`5Y&S���f��mF��E
��.�d��Y&����>����E<c�&y\��&��d=�������c�������P���$o=WU7����xQ���������9���OUY��Tm��j�4��[u��fd���"/j���9g���?�a�zQ�-o_lo�U��B�	��2��}.6��4c����q|�1��<�N��M��*�\'
l�/�}0F

0��#��@������������nz_<^�
�&xQvFXZ�.x~��7��Eu�JU��U���(�.�<��N:�r#L.�[U����������k#�W~�Bk��,�1Rd�ES�67�'�����F����L�9,��j}]�)�6�?�Lhg��
D�9aU>�o���[~(���y�*E������?��R���?�?S��>��Y$�4'~�.������nU�
������u���'O�dO�>]���~)�|���
L���G�|�z�jv���e{Hs-2�=�
�]%>'�}�W��������dU������.���f�������9��M����9;�u������,�*�F��/��/����g�����C���>�>���e���^�^z��evE�dL�G������f�]��B�l���,��7S�6�\��]g��^���2���9�������?�����o;�����l�\8�������~���,����������[�a����w7��~����%��%��R�/�����0�^��?�c����y����~��^L��|���uQT]g�z�b,�g��,�#�l�������}~3�l�A�	��Ea�w�y'�0?

�#~Q������o�x�����m,��1l_|����,�a��d������f��M���R`�fI0�M��u����[oeggg+���J�2wa����Rl������BY�6�S�U���3��+������Q������-�F��s��i�m0F��s��;s||���sg9-���Xt!`Y&c��)Y&l��R`n`pE�}xx�=x�`9�.	��"�`� �2�?��s����0[�
��9~�����zm�����}��o��2���o��,���s��f~��W>���w���|I��d��?���Ph���5�pw�|�I�:�R�����yk��}�`�d�L���
ljn�f���M1�!���1��������s�o��������������|�����}�Q�����o����x������}�`.d�L�\�
�6���~�%���Rl�w��g�6
,����]�L��"��q�F~�����y+��]�����y��I����e��~�����//���8�a����W�fW�\Y��&���?�o��p��[o��.�o�Z��'>'�}�U���������Z�o
���O��~�;�Y�[�.�����W>.t}�S��1�svV����^����N�I����_��_f?�����1�'g>������?_�_{�����^Z�aW�I�D��w�}w9�u��k����B��������Xl�'�|�}����v��z�d.y����]���}'�d�����_��_e?��O��o�������e��9����_�u����d������������
s�/�cJc��&����y+����������?����[���)4��N����qvxx��:��{������@��?L���|��1�Ba��
��V
��o�������nPh]f�j=��c��4�����*c��1�sv��,Q���,�m�72&

0&�����'��M�[��}�67�'C��p�z�����.�dQS�6�\�4Y�4�2�"9)s����;�s��s�O��1����^�������*���%K����_�0�)������N~k��7zE��L17���������*u�Y5�5�
<�[�o����/?U��C<��-�Cm�[�L���>Xz���f�����k6�j���������:Q8�=Y&l��������
s,6b��gL1�4��H���jU���������V�������&�1�������V�c��4��j�������Fc�W�������Q�@U�B�`�(4��`�Z��}�}��n�o���O��jUaW-7���o�����>�jn��K��i>�I6��oS���`�)��B����y���G��L�>���Um�������e7��d=�~[��o�Zg���%�`��s���g�a�|r�l4�_��W��M��_�L���}����q�F�����`�����d1�t�X���T��g���~s��G�	�>��bS�67�'7�FC<������E��-Y&��b�>X�=��i ~A���9>>��[��������e������05O�>��5���+��ir>����w����_������<y�<����W'yN���������������z+��\|cV���b�7�x#{����{��|�|����G��6]�&�������|���Q����O;X��W����m����:K�ev'�d�����������������=����������yy��w��_�f����d�^�B��4�e*S�6C�6�Y�t�2�9)sV��w�����?�g���9���s������1���_������{/{����[LU���/���,�

��n���n�����?��S~���M���?��_�^y���t���T�
������:K�enF�I���0��_����<
���]�K�&��6Y&S"'���~�������e{cJ�

�K���K��0������e<�����=z�(��#����;�-��,�}��,��d��{��S�8S���.�+��������p�.�J��n��o�s�������-��������j���/����'O�dO�>]��^��]�re���mT��}2�����M�,�m�7�x���%���_�o�������
��K�&��.Y&S#'���~����������>�����{������b�����]����J��?�Z�	�9>>���rtt����m���/�Vh (6�����;M�L�E�����s�
�js��d��$�d����>�*4�S���~Ph�_���]�������;������>Xz���S��@��)6�/cJ��X�=��i`�X������=z�������w�������-������V�����&o������f���R~�{��I����e������+W��}r�����9gg��e���2�y#��~�W�o�����w�}w9�xn.y�,s�d�L���'?��_����W�[�6S��{�e���~~��*�v�Z��+Yb�hI'��h\��}��BA�xn�����@���C��z�
�Es��d��P���>iRh tS���~Ph�_���]���'1��w��oL�|��
@?�)�L��-S��@��)�jq�L����b����������u��7�����y+�~�G����O~�W�W^~)��=y�${����}������+�����?���[������}����o����?�C����|�+y��?��?�[���,Q��]�L��7�x���%���_�oQ��w�]~R���M��������e?��?����g�gyk��2��,�1����G�����_�j~��6cJ�{�������o1U�����]�[t%K��B-�������{���{�e��_n3�6�E

���C.3�j��Y&��7�x(4����d�4%���,s��239)�O�M���K��,�_���]������s'�y�f�0;.����U��1������N~���e0W|��r�+� ���3��\��Q5�i����E�-�`J��#���@�	�bL)0

0��W�}���*Bm�F00-�L8���?���-�F�	�cL)0

0�;w����"�>::����������������r�r����� �Y&\�/������?�'�-�B�	�eL)�k

0���������sdGp}pp����7n,�"�.��������M�	���?���
��,���R`�`0>�[�"�^dW�`;
�U�`���%��zQl���0��.�2`?S
��B�\5�������B����BZ��B00N�LX�_�������n~�]�e��0�����G����[�n��n������������,�����!Y&�cJ��)4� NOO����Us����)��,�Sl`wd����)����t�����A0��d��;���,�#�������-��Qh`$�H��~����y`{`'=z���2��E�	�(6�[�L�-����������������q�:�.�L����e@w�
G�	lJ�q����u���0ou�>>]>L�b�!���(60Y&�)����Q�:w�����N����o�-�/�
��,6���0d��&`0��_�[�<x��=z���Z����������`�(60<Y&lN����e�Ph����q����N��y3[,����2�N��?���/
��jn,��b��e@?�.Y&����3y�"xM���TU��"���������?�[Y�[?���0_�{�����K�-����'���O���W�fW�\Y�Y/���?�o�{������?��S��|����Ea�Y�,ss�L�$o������J��W������>�,�������^{-{����3x��w�U��m��l�_����e2rR`���w~9��_�oA5Yf�d�������:��M������;����6Y&���>XR`;d�@

�F������� ;'�`��,����v�2��`gn������-�����j�q_L���7��x��b��e@?�.Y&��B�\���:B����?�Sl`x�L��b�'��Ph`��H���Sh`��H���Rh`��H���Qh`(6L�b���;>>����)�+����)]��b��2`��H�	A��=�^}����4D������-�M��=���o�-���g����-�M�����3y��E�z�.��������e�����[��'����+/������<y�=}�t��z�jv���e���g�k���-
��e�0.�F�?���d�|���-�������?�|�~�����^�;vG6�/Y&09)�O~�;��]�������,�_���]��f@�����G����p||���sg9E��L`
d��&`8��y��iS�f<XN}�eS �6����BL���i�/Y&0�L����3y��E�z�.<�������{��e<�oe���I�j/�������������� ��x��q��������-����������_�oAwO�<��>}�l_�z5�r���
LO����k���]g���z�LvA����������~9�ES�}�Y����/����Z��K~g���&�%�/Y&�FN
����/gW���-�&���,�

�����7Cq��6�E

��`�W38w�d�����C��n`Ld���e��,�}#'��B4!���,���acQ�vn������eS ��I�zspp���u������,�Y&�M��g�6
,����]���G��{�����{��A�:�i��[�n-��U��<<~�8oe�o��o��|����j���/����'O�dO�>]��^��]�re��������ky����DY�j�L�&o������J��W������>�,�������^{-{�%�3`wd���e��,�}"'���~��������T�e�K��?�Z�	�9>>��[�� ��H������j�N�,�m�7�x(4��B��l�_��i�e2ErR`�(4@��~��w)����������o1��,�Y&�)���7����1��,�Y&�)�`F�QhfD�6v||�-�S�WV5O�S�>��,�Y&0�`F�Qhfdq�L����b����a?=~�8oe�7�?�����y���y+�._����<�L��y��������GY��W�[��<y�=}z~N_�z5�r���
LO����k���]g��L��y�w���������a�Ed�I�D��K��N�O�B�����������3����.�E[��~�2���I�3���;�s���?��e�9���s�C������?������[��,�_���)4��N�q!�����[�*��iv������)4�C����@����������W1�E[��~�2���I��������7_�o1F��~��w)�����G�.L��e/�;w�,���,�Y&�)����iv���/�>.���lF�	L�,��B0#

��,����4�X,��sva�=z�(oU;==��[Yvrr���K��������������y+���G����U^9�����_�oU{��I����e������+W�m`z����]���(�:K�eV�e�+�F����g�_���m�&�%�'Y&�HN
�����g���z~�1�e�K��?�Z�	�U���D@~�����C�
�)4�"_�����e��F���l�_����e�o���Rh`�d���%��R�/l,���������@c�L`
d��6)4@o�����������eS ��iq�L����b�����{��Qv�����E<�[��n7q�������*��U�?�[Y��?��������f�^�z~���'O��O�.�W�^��\��l�S��|����Ea�Y�,s5Y&C�7P����8���/��hK6�/Y���2�'rR�~}�7�8�����[��,�_���)4��N����qvxx������6�����|5�s�I����(Sh`3��~�2�I���I������2�%K����_``0�o��b+Y&0�L`

0�������n������l��,�Y&�)��������������q~�~z��Q�X,�S����t���/�ULq�&�
u����X~�Xny=0F��en#L��tj�_�=w�#�2����~�2��D�sr�����M����/���1O������]��C��s<�X.��>f�����W�+6y�1�6��X���q��"��������"�
����7��&��yb���v�7��+��4��]�z���LpS�<"�l*����j(�Y��L����(�nf�4��}�vvvv��trr�����`��������e�}e}�����@{��>|���s�XN������#�� �.�=�6��q�����

0�t��>��0�MU�*i ������[�n����/
�W}SW�����
����/���������J��`��%��E&�i:�zWI��z���2������q�PO������Qv�0;��Qf��������;��uy������u��&U}`,�%�",d�u�V��\�M^��{�,������l_��:�e�3.��(4�`"D-lR�uLb�j��}T�M�G���i�\�O��/B�uav�y����[���-���x�}�2��	��������s�����Y��91.s5�2`5�L��P;*�.�eAi��t�"����iX\��6>|�����zi0���m��,�J���YU�d���md����y�\�A�M��v���!�vv��!c������[�i\�j�e�7�D����w�����F0S�]�t������am� {��6
��J���"X���Yf�x���0�}�2���t�\������������`0��>��8�)��o����mZ�8�W�-^�<�~�&����?�=�)�z���L�/�}>����TM=��c\����`��� ;��M�!t��_�[�+{XhL�U���?�0�f�U�oP����M2�B���Z6�{�eV3.�mQh:���}����y��&!u����`:�u"�?<<�oe���I���K&X�?��l���B����e��U�{��9iQ\�j�M�
��q����`�`G��mM�|��V��{�n��w����u�7��c2�,�i&�a���\W��Q�MA�( PU� �e�0e�eN�q���B����M���cb�>+��%,�����.D�o���RQ�J��`1_9s�Z~�_��0F�e0F

���]�6
���p:�����.0m3��[����7�Ev�U8>>�[��Z~��.��
_m�	�\(4|�*��A�����,�U�����.�`h-���(�J��� �����@�u�,?�ylO�el�q���B�R]��.���b�o��b�8u������gggk��11����4o��u�V�Z-��d��d\&c���v||�T��������������k:��flYf�Lp��_�[�>|���5@�>�&�^d\�q���B�D������������� �vb�v	����/��F�`��x~]2�M��O�CZ� ���+�1sc\�x�~����	��`p���ys����U:�vH�/��5�6P|zz���E����!����v�g�/`L�-��4�M
����"�2��`=�T���L�%
�WI�m�G��T(�0����{1�6�oY�3A�`s�e6c\&�H�s||�k�lLGGG���A~�4m"?|�0o�[��Ua6�Y�62�XfL}J��M�Al�;�2��	�)4�`��5D}rr��TV�Y���F;�0��X8<<�[��<�g�z����T�-�L��X�S������X,����M��xl��[���s��4�Y�om`n��\��7�	/Rh����<#��0:*�SY���1_��P4��f���4�����_����C��5�����3�}�2��Clk���i�Z/�\5X����1�}c\f;M�-�1.��Ph�A��l��B�����r��4���tP��{��V�t�����t_�~�d@1��}�2��	��"�����t�k����o#[���>0.s=�2`5�DN��{7o]T�����4�M��j����n�����zQ������.'�5�^�/Y�63����L��@�,p[�_C����j������}d\f3��7..Z�=��i`�X������"t.�u����v!4{rr��JpY��"|�_��m����0;���2���~�z�mI�C����W�}��q���o��o�-`�W�4�����[��<y�=}�t��z�jv���e���g�k���-
��e���K���L0pb��U�5��,�*+s�(o��k��qv��_�o��l�_��i��,�	�2��	0o_��?�.���c$���,�

��v�4�noo���-}|��Q5 ���7��+�������|�]g���n�!�"���w�^�����n��3Gy#e

lF6�/Y�4������ob�:���NN
�/��O��/Yb�.���(4
~�^oCl[�����)����{��V�|KWd�m��Y���.�=��m�e��`��_�[���������7� ����F���<��������C�9d&��U��U�t�,3�Fw��j�q;���"��q���	/Rh��x��Q��?0���b�2x��������Av��D��M������X���L���r����$,r�r�WLm��:u���1m����0.��"o,//�6Yf��M�M��rnmhC�����*�������0ol�,�Y&�)����Q�:������u+o������8o�K�	L�,��B����y����7��tX����w/o�������c��eS �6�8{&o��b��[������s'{��A~����{pp�����E��4���������e�����[�*��iv������=y�${����}������+�60=�����]�[v�%�27#��o�F����g�_���m�&�%��6Y&S"'���~�����|=���2�%K����_���w��Ei�������\�Uavt�E�	L�,�J�u���eX}�����j1��yB�S���Y&0�L�+�\�����_�E���/��
��g1�6�2�)�e],����4�X,��sv�v=z�(;==�>|���e�n�R1��{��q���o��o�-`�W�4�����[��<y�=}�t��z�jv���e���g�k���-
��e���e�����������W1�E[��~�2�G����I��������7_�o1F��~����@K:!��@�Sh�E����@���2�6#���,���_

��,�_���]��f@�����3y��E�z�.���G�����e�������[���,��!=~�8oe�7��7���+�]z����jO�<��>}�l_�z5�r���
LO����k���]g�����e�����������W1�E[��~�2��,�1����k�����o���b�d���%�O���t��!���a~�j�o�^��m�"����y��fp���e�y#e

lF6�/Y���e2vrR�~)40~��~��w)�u||���y�q�<x��?.�l�,�Y&��B���;���*�G(�-�L`
d�@��g�6
D���]�\��Q����Q��������999�n�����~=~�8oe�7��7���+�]z����jO�<��>}�l_�z5�r���
LO����k���]g�����2�����������W1�E[��~�2�M����I��������7_�o1F��~����@K:aw�=ZV�M5
��RnU�]��mh@{

���W38w�d�l���2�6#���,s�d�L���_

��,�_���]�����w�^�:w����	�������r�x\YT���,�Y&�	�DT�}��A~������V;����t%��@�	ljq�Hil�X������"��y�f~+������p�:>>��[Yvrr��/4������e������[��<}��������<{-_6�	r>�8�_n�������[/�����������=y�dyN��W�fW�\Y���)v�v�Z����,Q���,�m)_3������`7���gY��p���������=}���Gd��I�Ddl��f������W����V��W_����vV�M���|�e��,�)��2g�>y?��8�;�s����������������l���}��l���2�%K��B-���l#�.�Mr�R~����������������~��*

������,Q���,�m�7��[o�������-�E6�/Y�4�2�"9)�f~��~-{��W�[L�,�_���]���A�rL�,�Y&��B0#

0������s���y��G���s����eS �6����q�F�:���������i�:�.�Y&0�L`S

0������e<�\=7����..`S�L`
d��&`0�����[�2�njG�}����V�\^,�/�L`
d��&`P���!��s���`;���'
�cy}�eS ��Z�=��i`�X������"��w�^~+�<x��.*����y�:��i��IDAT::Re�V?~����/��/�������g_����[�<y�${����}������+�60=�����]�[v�%�2��e�-�F�yx�����_~9����&�%��&Y&S$'������e���J~�)�e�K���K���U����`�����Y7/@�d���2�M)43����B��� ;;;���`Y&0�L`S

��(43����B0#

��(43����B0#

��(43����B0#

����{���6��,����c1������`C��XHp�����Rbl�p"{sP"�"c���cf��>��C��L�(��"��*�����%W�>������_H�A���(;;;�o����g�E��@����z��}qq����������O�����i��h��&4@o�����������m��eS�e�`�$��H�A���w��xi��h��:�n?��ia6��{wR	�������������|/�.//�������?���^����_�a|��!�������{��7o�dO�<�/�������O�����?�?~<�������/�=
�n�Z�CZ&��7��������O�K��69,-s|�Lv�N
���W��g�����-sXZ����"�W��lR�����C���������q�2�� 

l�69,-s��Lv�N
��A��eKK���#�ruu�����	L��	��Ab_�FO��@��mv�Y�O��,���%����:����/}qyy�]\\������4�����<���>|���e�����=H��7o�'O���V�������O�����g�?���S�������m�D-s9-�M�����������%�E���9~Z&�B'����W��g��KL��9,-qx
td����,;99�/y��A2h(���s�I�d]�F�44������i�2�"����G���8�G�G 
@B`#��������6�1�2�)�2�����n�1�d6��{w���\__g777���������/_����&}��!�������{��7o�dO�<�/�������O�����?�?~<�������/�=
�n�Z���L������������%�E����{�L�J'����W��g��KL��9,-qx
td'B��w������3�fo�������������A@�����;�L��7������&��e�-���I�1h`z��ai��{���:;;�:�������X-�-���6.b���I~i5�Em`��L`
�L`
�Q�����rOOO�����_[\><<�oUq���04-�-X����|�f�Y�w�K�N��������������f�///q�?�����|/�����@���y�=y�$����?f�>}��?�<{���|�����/^���(l�%j���2Y� 
�_���>}�_b]��ai���e25:)@?�^���={�_b
��ai��{����������i��n����qv~~�`�������L`
�L����Q
��v���k�vbn�������\x���7��'O�K����c����������������1iw�m�D-suZ&��7��������O�K��69,-s��L�F'����W��g��KL��9,-qx����V��������+�����	L��	�e�[�������z����|`8Z&0Z&��Al���M��E�������h��h�@_
@B��4�V����{��������������eS�e]4�F�{_\^^�{�y��]�0-�-���6���0�������o����������=�~�L`
�L�����/_�{_t��q��]���i����	L��	�a�s|||ozn�8}tt�������!;�W��!�0-�-�cv�Y�O��,���%l�n�mY5x����|�����l?�����C��e����� mo����<y�_Z����O�>���?�=~�x�LO�{�/^�{����~�L��7��������O�K��69,-s��L�D'����W��g��KL��9,-qx
td��,j�%f�n�6<d�P&�/��N�����F�44��������2�
����G���8�G�G������������u�2�)�2�U4�VD�>??�NOO[���n�����
l��	L��	t5��,����l�������:����/}���7�(`�
>|������������7��'O�K����c����������������)�����|���[���>Z&������������%�E������L�F'����W��g��KL��9,-qx
tdB:������m�L������/2~��w�W_}5�������o��=����b�q.PV���?�����`;��c������&g����1���?���#c`M2&�#ccM�����f�=�/�.����L`StRR�O��O��1������_��!����������}����Ra�K�-sXZ����"�t�KvC���G���������w^�?��|�dL~����~A���m?���_����~��K�:k�1�k�1�}���L`S|�&e����_�`�����;��=@�b�o��6������EaH��O�|/<,-qxF�@B��4	1hb�$��H�A�� !
@B��4	1hb�qvv��f���E�\`HZ&0Z&��A���-&����#-�-������|;99�f��|+��
���	L��	��A�M���L��B��@��d�����e������a���"p�u�	���eS�e�0��,�����U^��"P_^^��c�n^_�����^��x�"��������O���?��c��W_�����~��Q~??�<{���|��;/����e��e�)�f2&?��S������79����?g���|��~������>l�5��X���5���>{XZ�4h��_�I�/������������>���o���
�R����f����|����������>���'u���8�G�G���qv~~>��M�e�nU�����&�C�2�)�2��4�F�|�2��""����|k�c�����|��-r��eS�e}4���t�b�n�"r���[���6�.Z&0Z&��A�J����"r�
���eS�eM`���������+�-�-
����uvvv�_Z�����{oo/�S�zZ&0Z&�	
����Mvrr��f��vtt4��7%"7�"Z&0Z&�	
0����y�>88�+p�e�1�2�)�2�u0h����]����0Z&0Z&0�����8����o�������|[$"w1a7��v#p_^^����	L��	l��6*$�Ex��6����nnn��:�*"����I>|�������05?~�>}�4��������j�L��3������������������y�m�D-�-�u�5�1�����?��s��������9���?��?��C���_��a[�I��zdl�I������2�E�d�|�&e���K������������o���C*�H��������o�����.���o�������^xXZ���a-����!�����g�(&���u�&��0-�-�AlED�����������&r��r�.7���L`
�L�+��"p�q�lY���
�	Z&0Z&��A�^D���{uu�i�.�&i��h�@0h������7a������kl��	L��	i3h�I+"w|+-�-�a��mc�m�-���C�2�)�2��f�����0����;^B�M>|�������05?~�>}�4��������j�L��3������������������y�m�D-���L����~��������&g�~�����?��������_=��m�&����&�gK�6��oR��/�d����|����������>��{�������o��6������o��v�)��I�����%�Q�H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@Bf�����0����;^B�M��/���e������{������k�������2L�����e���|�����v����vK�2a\|�dL����=���?���#�f=26�$c�������>��k�1�}���L`S|�&e��������o�}��y�:�R�_��_������R�{�ai��3h�#�h�?��@��@K���#�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4L�l6���:;;[�qa�b�=X�������Z�Y���o��'�Y���@*��s������u�l�=k���n6}~�LRT����������0V����}���|�<6����eS0��,�����*/!lO������*����/ua���"�tg�����tss���!EM�����0{��m��V��w��-=�*���u��E�����5���[��	��e��e��ti��^Z&P�e��ek��� �����i����~rr�_j����O������(��S"���&�LYD�J2///�����A3��b���i���m�������s�H���h��i���ti�0Z�bZ&�h�m���X7m�u�]�M]���	��A�Nj�����]aJP�.,����Vl1�4���������U������s�$�����i��i�-3]Z&������I����V���=+�VY��6��8~u�
ul��2��2h�Y�Nx4I�T�s�g�����|�������?���s5\���8e��~�}�������v��	���ti�0>Z&4�4�_*�n��,�V�YY�U���8�P��-�2���Q�.777�^7�������}�������ea���������ME��Z����z�W��eB{Zf��L�>-���E��������/[����?�2P��W�
������eSf��S��m�xRi���vE��]�F����� ����|�Y5������s_��������	�h���2`�LX�:�c�/��������e��P���������k~!}��L`�f�����0����;^BR����o�7����������7G��0wpp�f���:��'�]9�������m��CY�?���5�������v?��xk����m�����U
�m�7����������a����}���<�eu!{���0%�n�Z&�7t���1���-�-�-sZ��	@:�L-s���nv�e�/���b}����M��\������
�1���e�-qx���;�E�B�)��>1-�O����?b�[|.�������c��-;f�yq��x��c-{\�G��U~<q�����1wM<��k�E��,^�B�����U�zS�,��"`�q�/�z��x�1;����-S��%����E���x���YZfS=��x�Z&,�ej����Zt�Y����0�'���+���n/_����������������vQ��������)�_�x�c�2�)2h�9��W���L9u��eED����S
:!>�#���Ny3��8f���V����yU�������1��{�_JK5�k�E��l
�]����{����#|jv������EpH���e�
-S�,L���Lh�ej���������/���H��n;@`���������e�3h�9�������*("n]�]$��(�6E�E"<����8�����������}�6�KO����kZ
�}'�V�T�{�����6��bj������@3-S��%Zf��L�}Z���+�
��/��1������X>^h���>V����L��vN5�U'�6)�����#�D���S����D��]S��N����1V9f<��6eEt*����������#>_�������q�������h���rX.��.��G�i������>�>����s�US;�eq!��2��]�e�K����ej������Q=����8Fl����?�K���`Um��.�22h�I��[��Y'�M�k��S
>r��O���|56���q�9�q�q��1��F��qT�_<��|����_~m���P>n��RV=�m�sy�t�3����+�?���@�L-sWT�������ej�����������5�H�hY�U�v��.�cu��Y�0h�Q����)O������M��������rY�-���k�oR�cq�e���#�-{�C�F(�W�M���C�
,:v��>��^t�����2��]�g������ej����z�����k]ma�,:~�7����2�3h�I���l�l9�v	@�����r�]�|����F�p{{;��u}u�q�M���������	)���.����\&�U���Z������R9��2-S��%Zf��L�}Z���K�����k]]G}��������5���	p�A��*O�����V	@�#�1y]"&m:$���?PX4��0dx�5�z����*�8�r�]]]=��Z�Mas��{H�����9zV��{H�����9�uUmVe��^=����r���Q���}�/��~Z&�}
;�H�L9�������)b`9�������q�uF�����6�������55���8]'(W�C\�cD����w���{��s�O���zl-s���t9�����~���2���z��lY���/�wU=��b�A�XqN�����e�1h�Y�8sss���W��]P��E��-"tl�c�e����d#�U
�C��^q���h��}����z�41U�l��-��K��@���~��q�2��e���2��2�����^��9����c�����(�N��c���NOO��/A�*Bp�����S����"\�	��"j/{lE���.?'vK����M!�c�1������{�}Z&�D�J�s�O�dWT�T�y.��jw���y��E��M7Z&�
;moo/���:��<������������D�B���q�6�2bW�mS-wO����4M���j����)�{��.��k���z���S8�@?]���2���)��P=���N����:Z��L�/vZ������r�^u�dL����J��x}{{;�"L��TWq��m�����U�!���*�uY�}~@�E5�Wu��Z���zg���F����}Zf��L�}Z�p���+7������U��*
��>�e��_��S�e|a���"�b�l!Bt�O<+3���z�x�L��~"�������?��4���z�>����i��
=e��z��ME������2W�e����.-v���-s|��������n-��������/�W���~Y~J�L��T�lp��p�xV
I�����SE)w��2z��Y�m~@��X�]�I��u�����E�2��)�{�-�]�e�K����e�K���X���.T�|U��X]�a���A��_}����9�H�0hH@5�Q���F�e�<�	�m�`���CWW�cu�G�1�����j���6���:�rn�k�Mx��:q�>�!S<�@7Z�j��q�2��e���2W�e�Sy�k�X����[����6�6���v
�{�.����sHA��eZ&���P�%*�6Qnhy��x�*��1��7��_���q�����OX������z�~@S�W�����;V�����N�:C��i�����i���2�I�L��	�O�\��9>���k6^������:�u���T{���o�����!���VV�dm�C��L u
I�Nx,�>��z�6�9�p95���61�z����� ���T�X\w����U}����=�P
�����4�0�����NYS����h�T���!�TL���h��i���e�K����e��eN[y���rh:�e�am�ku����~���n���N��9�J�Rg���j*��j|��.�E�����.g����\V����q�����]�cU�������>��c|��LA]��?~-�
��6�4�]�
M�u��j�aH��u�6��bc?�@?������k�Z�i���2`�U��.��ej�SS^����j�*�k��B�&����nV=�E++�q�r�zi�R�e)3hHF]X�F�U��@�-.�����VoWI��uB�z�8v5l.���i�q�����"\�}������hV7]�kh�5R}ka�ym�h�����
uk���p��~��;Z��i���2`�i�w����[�m�<4��X�s[U�nY7��^}��c���ziG��vK'��U7`{�'���wuuu�������Z/�Y�~��c7m�}Vo������c���u(4��5��c[������Ow��/�e�y���X�u����7�������7�s�����y`����+�M������N^���Z�����
�I�����>�~?�o���m��Ph:f�k��9�����?/_����_l�^�&�s�h�zm�o\��=�C��o���I�k���.+��Wl�����+��������e��WL��c7m�}��~>>�������t�����s�m�k^�>�T_�U_�.�+���N��=��m�z���^���'���Q�kI�5SR�;���g������� U�%��)��|�b�c���e������1����&����w�x\!��V��vy<M�qu�_&���M/]�:���v���������n��p�����u���)������+Od�j1��j�-Q��q�2�X�i���e�K��i�2�2-��U{��9m��szz��7������7�i�����M���k7w�c�H��i��g�@G!���[��	��eS�%�Q�H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$dv�Y�O��,��f�9V�V�eS����y�`�$��H�A����g�>����4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !����}Z��f�@7���Z&�*-��&����#�� !
@B���n?��ia6��{w��@��[��	��eS�%�Q�H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4����,��f�oGGG�-�[_M����:�S�����6������6�v��z��IW�����k��[�SZ+��u�
-����qY���U7-`�4�����|;99�o���
0Z&0Z&MF���M��J��@� 4LF����Q5-�- m
�#jS�eS�e�iv�Y�O��,���% Eggg���I~����i����_����f����r>)w����l?�T��q��-���K�6����\fo���/uS]u�=�???�/g�-Q��/�L��9�1�Lk�4-�A�d,��q�2�h��3h�#��X�����K�4�G��/X0M�W�����s`�L`hZ&�k�L-��	�-qx����a<�s����gJ'��L`
�L�F-��.��������G��@��`�0z��K���\�1�2�)�2
�����|`��L`
�L&���8�{���:��.-�-��������6'"���Yvtt��f��-�,��^l}����o:���V~L}T�[���4=���>?���W�[�c�������2��k�Y���'����k�����]�e���:)�5���(����S<�-��e��xL}T�[���4=���>-&n_=nlm�Yw�&�~�,;��gu�^��~��^����V��_e���x]kn��)�N
-���M��U������}T�[�u?��}����9f���l�u]d��+��N���OQ�k���J������XC��b�-z>�c��[:����@�NOOk�^������a��lz�WWW�5���4�����Sw��c���^g�����;VU��j�������s��Xu�����h����������W�3M�u������nW�<�Sw��m�:�z�U��u����������`����I�����s��~�����E���8W�~{�������O��V���z�e�����XU]�������9V���6�<�4=��-��������E�s���s��1���E���x�����u�����k	)��;s��}�0M����tU�������q���������U_��cUu�(}�C���6�x�����Um�u��u�������z��hz�C=�u��8F����~�/��P���������y- %ug����/"�$\\\�{�����{��!�����3�������']���x<q�!O+�c��j���0�)��~��w]sq��Xc4�s���Z���������xC}m)��*_����R��}���e����Gq�]�\��:����/���e����h^];J��n�c{<����(�_�?-��b�k��:4���Qs������G}_������"�m;jo�yq�C�1���o��[������U�M<������U`��{q-��xN}��ej�S�e>�e�v���cn�q���k�v8����m?�!��5�bZ�CZ&�N
��(�����{�k��mm�
�aY�����X}�9Z���<��j��a�~�����q�q.//�K-~`���ks~
��^�~n{���)��k��>�m��`�}?�ej�C���Xv��6�#��e����Xs[�c��E���2�5�.�g�v8����m?�e�_h�8�8Z��h���1��>v�A�$����{����F�����noo������->wzz�4�t
OeK�j�����O�O|����4=�x���{�.���m>�e1�|~���i�����&��U�Z���8o��u1��|�~q���=�E�����(K��+l�}��e>�e���I[Z��	u�������zZ���2�L`�F/"_S�Yu���l
�w"�,��{||<�^\���SM�<�e�g���+bW�M�q�u�bX��}���6�<����o���E�g�Z�����T���9����m�������R�����k�}���2�i���=-��M?-s����8o����	�A�\n��m��=�Um�yh���l��y[t~�_o���E_[��9-���Q�(�h����^�7��I����:q�E�gq���+�����!O�\m-��6&&6�<b�7��]�����ztY+o������=�����\s!�����>�v�2-��.�!��e.�e����e@�2�[;�2�2w���e�c�0Z�b�*q���I��&�.�3�W���g��!���O�\h
aC���u����45�kX�����V���8o]�w���M���iz��0c�e��e���I[Z�q-�Ti����j�i��M��"n���2��4�F��������l�0f�U��2M�9z/_������������k�J<[�M?�XsMLW=�*A��VY+�n3�y[6��i�u
�Uq���,{<05Z�j���i���e��e�
-������=�Um�yh����ZYt-`1��A������l�ck�Z[�4�qd"8�a��3{{{��c�jx]���&��n�2l+�m�y4��}��:��
�u�.���������o���5��.~X	c�en���Z&mi�i�L���Yck�Zf=-swi�i��:4LND�>����[L�������}�-�������f��qyy����w�
�yh�s���[�t�&�Zs�i�5�'�2-sZ�zh�i�2�2���9���C-�!-s�����L`�&e1{J��S�}�M��q��y�)���\s��-�J��6���9MS~m�LX?-���Z����m<-s����j��T4L���� �k7���z����e�g�N��:����8l��7����GGG�lu���s���Lv��IA����e���L
Z&�r
�S#b����������S9\�Ag6��#vl'''�5a��q���<�)�2aZ&]�u�U6H��	��2���K����A������:����y����r�.�u5\����3��gW��X������:?,�.�~�Xh�Z��i���ev�e0&Z��Z��:��9,-s�����L�/���E�^u���pm:$C�����`3�9�S�(�nc�e�.��f����F�v+-�u���4k�2��)��#`�uxx��K��@���������t�]]]e������o�?��L7e���U|�Z�6�	�0eZ&��e�iZ&L���6i�l��	L�A5���������b1��c;>>�o�����{���-�M���4kv�����Jl�5L�A������I~��j�>??�����|�V`z�Lv�>E[�
L���.��h�Z�����w���{E�^5\����{p�i-
�N�&Mk�Z���2�$-�M�2`wh�l����i���4PSs��N��UB6,sxx���".�z�:uk�-�m�2�4-�O�d�L6M�����"����'''������|�����|o5�5����Q���l6�+�X������:i����	��e�NZ&c�ecg�@ISl
�m-�`*:r||���wyy����Qh��5m���E��c���Z��i�l����i�0}Z&��e�iZ&0U
��7,�{�.��z1��*b���,���HC��n��>�sc�5�����|X'-�u�2�$-v����i�l��	L�A%M�%���"��=ix��m�w���A�8����"�������n?���e�-Z&��e��i�l����i��4P�(�t
<�l����4��K��ik����Q{������|����h�l��	��e�-Z&��eSd�@E��a���l�l�8??������)lAQ������!�~�f����&m�����E?,V�e�-Z&��e��i�l����i���4P�&�D8�-BNy�?��srr���ND��X~yy����	���b}��`l�������l�m|=�5�������k����h�l���&i�0mZ&��e�IZ&05
T�	<c��S���:��f��b
.��m�m�\h��
����i�l����i�0]Z&��e�iZ&0%
��x���Q������em�ib����8�F�����y������
�e�MZ&��e�ti�l����i��T4�������m�6Q���,��/V
���;�����^������q�`�L�I�d�L�&-�m�2�-;��(�'a���<M�'���9MA�)�\^^�{p_���J��{�������)�Z&��e�iZ&L����i�l��	����t����0����;^B����_#@
������e0%Z&��h����	�=Z����4;���:��ooo/��>-�-�]c�lY������vvv6��bt777�@Z&0Z&�3��,����l�����G�����������l?��]����
��n�Z&C�2v��	���2v��8�G�G`K��u���M�wOOO�=�n�L`
�LhgvkTC'�]�CO�m:^�i������e�Z&���2�EZ&�����(�l���a�w���Q��NL�m��A���2�)�2`9�`������...�����,�L��q�����3��\�>�L`
�LXnv�Y�OuS���!��F�^�<e���C�lSs6k�-Q�`]�L���e���L���%����,B��m��B���8�]�e�-�]�e�-qx�������g�������l`�L`
�Lhf������|����a���"��1�l`]�L`
�L�7�����>-�f�|����u:;;���������,������looO��m�D-�M�2�I� 5Z&�4i��3h�#�h�?��@��@K���#�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$��H�A�� !
@B��4	1hb�$������,;::�f���->}}�_�����nq�m��q���&8�c�M����W�e����g�>-D����)b���E~����a����l?��b�ON���>�����'$EA�!�H/H[�6����������j����t��n����I�5����]o�kkIwk�B���R[T0�7ED��F�~&����\�L���9��|<�#��7g���	���|N��%���V�.]jz�iX]SS��'��0T��D�L�9������2��,�@����2�����L/>�L��G��zH��i���k�:
��,4`��p��0�s�nf�n��d��C��l`�:)4@�	�d��7�|�P���z���0
k�m�4Hnnn6�� \�vz����GF�s�t�T=o��EM�Yf8s�f����LP7���f�H�	 ,���Vd�@d9�b�1�]�������*���m���t�R�����x�'B7WUU�^���y����!�"�E+V��h��f�H�	�^d���HY�r���,�?�S�B	b�=��J��f�2���k&�z���� �:�����ZY���t�H�l�|�I&h��+�t3o$�H/�LxY����d��,�?�So������xE�������?WZZjZ��Us����M+���j�d��wY�L�d��=�lii1�����2����a�gS�D�e��P�!!T��p�[UU* �t��P?}���.`�sS�����k_��u�2�%�e�4-++3��b�N�L�$�t3o$�H?�Lx�=��5a���@��d��B��z#�G�f\=�����v�����f��S�f'2�n@�]ZZZL+q����5H7����7�eP�"z��d�e�Qhp���_���n)//7��E�T�����h��y#Y&L�5��2�
��"�F��E��[���n����������r����y8!�p6 �7�F�L�7��������C�	�G� �4@��[�X�n���t�R�sF����2O��������
��wNH��e��=lnn6�An��d�@��^H��v�Z�JY&����z�����[��������	��sD�E�k�B.��oje�*�lPSScZ�7w�5k��p���u�����V���j���������cvZl����M+�D��T����xI���x7w0|\����<VA��pP�t�R32H+����s����L+5�d{��e�l2L���F^7��7�e�K�Kk6Y]]mZ�A�	����[������:��r[�_����HUU�9@D
��1555���i5^}�1��������#������kkk*|�o�������������e��	��t�t�H�lN�C�H�+{V�f�H�	A��:d��4�����)\� �>K���!� 6��� KL��#�a�����W@i�m��"��h���qRd@�c4d��A���!;w����~����?'�4�}�A��G��=k@�hvh�&���n���V:��R�,A��:2~��{���G���?�b��a�iz����!�DP�{��u��b�#h�k�u��a��PhpYuu�i
�j�����QZZjZ����Mk�R�<����%�v��w�y�������4�}�A^�{����g>�T����bz�--*`��c����1��f�H�	x�� ���A������ ��D�EPuOe���4��,�,���-"�=�����	�y�=���8�3����D���[�x4�n����Mi�]����I�����z73'4����,��;��YcZ"s��1-��������?�...����P�;�>��u� �����"�����kmm5-����B��t�H��0{F�����Z��=t3o$�����]���Bm~�G�� {{{C���B�������{A��G��{f�LdB2�^EE�G�	4��U��,3yd����N���	��N�*#G��?c�#���Rwww�]PP �F�
�?c�#��'��,1�F��\TZZjZ����
 �t��nt�
�v�W�uC����n��d�@p����� �p_7��:�E�����"Vd�@|2�����RO7�b�c��K8d'-"��z�����e�����e�n��d�R�,8��t��V��C���7������y���,{A�������\����y#Y&�T!������+V�5�
�����5���.T����,t$Rl@ke�[�����HUU�i%f��������\�e�n��d�@0�����]��a�9��sMMM�3�e�QhHR"�����V�~"E�s/]����sR�@�	UP�
�������N��J�I6t3o$��I����a�0��9'�2��(48)�N&���>���E��;{�������~���#�:-��4t3o$��*d�@l�v�Z�TVV� �~N�����6��jkkMkP*�V��u����,���������=���vn��d�R�,��B@f��uP������
)
�m
�����:G$Z����*�����m8����_�f���&��
�Ya�<PE�5k�w'-7�F�L�B�	DG� A��TUU�.����	������x���e,��z$3�Z�v�i
�y�s����m��/��	:���y#Y&�T!�"����h[[[kz���N6��]��[g�u~=�^� =G����@vH$TNs�07�F�L�B�	DF� IK�.
����k_����@��D��[Y�N4p��u�px~���za6������������,@��e����p,''���-2g��5�%2g������M���C���b���
��c�#�X�"��/**����P����V�)))1-d�Lg�d����e"�������/���|QGG������������jA�������#��d���2D������jO�:UF�j~��GPuuuIwww�]PP �F�
�?c�#��'��,1�F����;wn�/.?���?����@?d�@vcO&@�Qh���B��B�

�����_v���M[��>j$�B��6v��{��ko}N>]��\U��,�_��}���y�

O���-��a�<�r���x�����y=O��Qhx��o��z-0���8������ :

��b7,� ���3#�����-R@�Phx��o�=�n1=���pN�����(4<���N��0�8l���@�Qhd�=�n�M[����lE�z���|:/�B �6v��Vn5=C�$��8�wH@��YO�������K{L(

��z��BO��n!�
�@���������;t�����{_�i�k������*h@6����qcF����<<���@������:d

����!�3=w���u� Rs�Q��:������d

��:~�!���c�9��3-�(42f�������������0[7��^q�d9y���B@]]�TTTHNN��C��s�mn_�n�9+9n�
2���[���Z����==,%l�dh���\4��@��1�_/�����+V��tL?��$z����E����eee�s�47��[zE�yt���iu[7�H�^�`�i+

�Sz�^��������������9^�d�����[w� MO�4#v	TP9C>|�+M�S�0=�����sO�l��/�Q�@6��>�X�j���200���v�Z���5��������in��?�h��d����r��
����,0������lu	N�2Fn[x�\q�d3����O�y�K���9�t�rU�3��~}���u\?����B���b}�8�������5K�.]��U������d���7X ��:f/������@f�x�u�����=�f$���>V���������/�!�aF 9�<�E7��Vn������t\?����@6���S�}�e��n��e_�����b}�8?�\mm��
Z�n�i�����"���>67�����-r�C�M/��~�@���C��\4%T8��&K���Bcv:�����|�;��E�Z``�qF������l@��"��/�/6#��k���h�\{�s���'���gB��~������<�e��yy�iEWZZjZ�ZZZLk�d����6�A�9��������4�������*��1�������U'���&��A'O#_��"w�4S�P�)����PQ��}���y8E~	�D���lC�&O�����-Rd� �,X� t������EC���Y��t�I��w��P:�����|�`/"0����5h������Y�LkPUU�i
���H?�/�M��45�d�.��������H�_w���&s��x��2�c�Hd�y#��q�BE������������!���[�����\N�D
������K@$/v�yo����O����^F�Y"R�����MTD/���U����q���������+LKd����_mm�iE���H?�/���v)�
�������=��z�������m�f����E�ZL����xYYY��D.�O�X@2���sJ�����/����iZ6���A�����d��7� ��X�
�6��-��Hb�q��D��.555���'�
���M+q�6��972�����~����r���;�7#�-8�x���O�#F��A��G�H?=O/��B��A����������/ ��yt�������h=��t>��*

Y����#�9���:���f��%���7��
��6����a��:��p6��+\��� }��}����v�����BY��"�T���������G���k�d�J/�w������O���/{�p��S��@�l��w�
��h@2l��y����6

@�X�j�i0���N��p�n��D�@Vjjj2�A�y������u���������jii1�A����>��s#36o�lZ� x��_���M����f$�CG���|e�\z�D3 �"A���.]jFiaT�<��k��(���� k�~m�s����|�H-�K���/�6-w<�����B�%"�,0-@������nmE�z���|:/��
��
�UUU��999:����bm��Z�l�i
�|�r��E������6���4#�M<L�������AVSScZ��A
�_�o�7��(//7���g���fe/6��y�]����\.����2�E��"%%%�
���� 6v��Vn5=C�$��8�wHY/�����M�@�h��%K���Z<u�����v���[n�}���x��D7����~��Rx�h3 ��E4�t�O������u�������+--5�����3�AN
��(��@z�_�z��;�7�C���Ov�'�

@��Tl`��i���i%]
�	����|����O�\�@��F����!�uc)++sms��(��]�6�����
��3M@vz����{6�{��f$���s����O�CF��,.��b�D������Te�����5h������kWUU�2���v��5-w���cZ�w����pL7d����f���3g�iy���s�<�T��z��y�����CR���Q*++M/���g�������6������b���?��}�Ad]�EEE���j~�����\<������s�'&w�4����Lg�d����8���n��nx��y�F[��V�sXSS���E���]�6����ui.n���<��������+��'�x��RC7�����R-���H�>�ly��M���{�?����M�����~��:����2�?�������
�r�+�)�Omz�1~�x;v��a8"�|"������X����F�=�a]�����s����tcO�W�K�df^gg���^�3u�T9rd�
��A���%���������Q�Bm��X��D�en��O��7=�,��9i����d�%���"����Z�6������aU���������r+�v#���@v��SkA��GQhA�H����_>]���@#y��T�r�m~Bd���2���Y��FW{a�H������s�<m�����-_������2��p��N��u��u�Yr����`8���_�k������w�s��R��>)�-i��	&���,���f�e��(4�H9�4��/��=�^�/���y\p� b�#���A��GP%�/s��n����s�}�g�������a>H����CI_���J����B��]������mn�����o�M�=�7�N��YUWW�Vt�
��9�����,�����+?Z���"����M�G�Rd@p��F���r����O2|�^����P���N�����z#(�-��������u�D�P�!!$�����.���i�Y
��B�Dxa�L�j�\�F�������L/��������znfqwkkA��GY�}QQ��������%R9w��}��~���g�
g�I�7=$#�Y"Yfp������i
�n[�|�������y����kZ�r���<�s��N��u��w�y�r�J�C�m�������L���o8[�n����P{����[�������y��7���������y�|����m�_XX8�H��|6��
Y&�N����������?���v�����}�Ht^-�-�R���}�n���J~�������"�=��;�#�X��D�e�ko}N�v�kz�W<�0������d�%����C��h��?�����4[9��������<�07H�qc����G�&]�/�C��\��k�����t'/�����M��H_[�����������H�pp���{�#��A>X�~:�������6GE��`�|�'�=`>i���O�.�C�1����xE� ���"J������ ��q7s<����x��hhl-2�P�^MV+�j�m�h�Z�������p/�
�g��avX���9��������lZ����h�7�����+r��[L/�o_6M�T1�� y�.��=��f���H7��1;n����z�[��Fy����Hj�K��s�8��%g@28f�4�[C.��p9�&]����^��F*`g>��;���YcZ"s��1-o�;w����
�/�y���^��Z�JL/���g�������6�������%777����� b�#������(t�/ Z[[MK������~w�<�j[�pS������f����,�,3��p���\NrL�xY������^�5'���sg�������rY�z�������M������ff���K___����������
���.����N7��>�Y�o�kF�;��Q��+K���#�����G�	/�B�.������:j�i�
�����YSS3�1�r�Tf�������,��=����������P{���2r��P�3�=����K���C���5jT�
��A�$�\�����G�����.���K.e�&���'�W.�@k*�%���"6��������@�~�����G{��sg��7��j{�vfq�)���� b�#�(4��rh�z�v�U�6����Ezn�����e�:6l���2���B����+���h���sg����07

x�ffQhA�E�:�d�d��moK�������M���<q�,�|�}��M��d��"��h�5����2$?���Z8�
�{'�}�Tf�^/4�!y�W\!O>������g�->���!4�����e����a�#�X�"�=�*��+{>��; ���^y|�{f�J�G,������������0=W��ad��C����M�a���p�i�����k2����;���9�Bp�"�X�"�=��B�Xn�����W����������[S
�5���d:K$���������&Z�����3��Yf��

 ���A������l�����-��lz�]t������hzCQh {�e"���D���h��4�������gZs��'�V�}�_���~��������{����nzaK������o�WN%�^Lu������#L�E��z#�Gh�����I6��&�Co�4d�����;{�
������������g��Yz���)2d9{�^�b|'���MkP��>�w��~N�|���@|�<�����W.*�Zd�T]]mZ�t�������R�d�M�#��$��'���<��A��/\7�0��8�r����r(���] Q�5�jn�j�T�us�L��]������;dq������*�>��u� ��{�t� ������c�W����m{�H,�o~K6e�&J��d��a�2�%�e��.�*^���]���a�s����sg�������rY�z����ff���K___����"�f���{A��G6��? ����g�0#�����������mF"���%%%�/"�D6������t��.�kr8{+��m��w*Xs�s�=��2��.��{���������+W�2a��m��>i�$~wG ��T��o����P��N��#G������T���}�{����%���kFb;bt�|�S��_Z���[�1���^�kL����S�B	b"�M��������k7��/o��D�����>��7/�Q�Z�JL/5���Y\t��b�#�X�"

 �t�m��^�����������6M��I�mO'�N!6�"�����|P��e��tS���+���kL�w�i^�d�9w�x9��D��4�y����4��!��,

 ���A������l�m��R��f��e��n�1�J�%r����Ht�d��Fn_�o_��59���n?�T�j���=����������wS�N��;�A���%����vAA��5*���u��
��+�}O�vV`@��8J���H&�����G��+���N\q�d��ESL�F��z#�G c44�M��C7������
��G`�7�F�[ZZLk�n�M�}.7��7h��|���_�����c����[��_��E	�������5������p��S���y��}�������j�M� �l�2�P���Vz�-����e���D�(�<k�sX�����������y��2�iZ�L=��<} ���i���l��"�<y���h��"2�o�2����n���v���L��N����a��~Y����������/)����P����%3��&K�����P:����(2�lC����B|=ts�n

�94|X���H�A���_��V�u�1H�
�0;d;5}�t�6mZ�q��L����v����v�j�_���?0��L=n�|������������S����a+�����k]C�!��\R�I��Pz�.��?�&[����N�������l��n5�����p�T}�>���:���*���K���^3]�������"G��3#�$[�L}���<�(|�s�z��d������A~	��?O�&?��n������f�8V��i�\<k�J�Y~�bJ��?�~J��>3TT@?j_�����qB���5��t!~"�Aw&�m�X!�nr��,RM�h-�4���>F������/�l�"W�<#��}��:�Ur}�X~�����OmFEN-<"Z'Z,@������c��t�����Zp ���:���M��L�M�����v����D7��97�Os�D
����7��i.�f]���)�����b���P^Rdz��/Yf�����?������5�����H��������e��}���A~	��EUo��e��M����8�4����I����Hl�y#��q�BE����l�
���A�pC�h����s����/���)77��9���ER�b�>?�#T`��?n���3���r�+M������gF��h��+/�,�3�C��~^����{��Y���������1��p�������?&S456�@�������b�<�f��Ns���t����w��~�2y=����5�~C({?�"�����N���9��
*R��%����C���YY��f$��������&W]��
�����xpL�S;���i�����W��FL���5`�F��
�����zQ~���w��_������H��b]�Z%�j�9��Y���D���cZ�0w��!�/U�����(����v�"K�,�����m�����68���&����vqq����������D�{�u�I^^^�
d�G��)�j�&;����8���'�e��d����z����}��g_��3R����zne:K$�t&hY&2��Yf�����W�^j,_�<�;�Y~E���y�f)))1���43���]���Bm~�GuttHooo�]XX(����6| �X����W���Z�m��f$������I����:a��E"��C��ef�f��?'{���Ba����s+��N�#-�`}������e��'��a������jO�:UF�j~��GPuuuIwww�]PP �F�
�?c�������w�����f$�K�&�u��	Yb�Qh A,��������J��&r�{X8,��;V�������p�0�B�������\q����O�^j�*��5�vZh �`�����|���n�!7�w�n���w�G�MX�MX
�=�����]~��W��G���l�u`m;Uv�h��cd��/43~�x;v��!]2�%f��g�L��W7�Z�Qh ����y������x��f

 ���A�����/y���R{���~�~3������\Y"���W��B��,����e�/�W��9&r~��-Us+����
8�K�^�2�(4@~�0.�F��T\p� b������W�x�]�v�gFb;���r��Er��G�d�����@�X��i�ZSS1d�p9�;
j����VWW��u�r<���X���}~{����^`
V'N�hZ���/|A�~�i�K�h?o����U�B�������������~�Er�Yg����kz�;��_���^�i[���s�����S>/%�-�'��<-40a��C�d:K$���,�����V�.4�n`�9w�������M��_��(f�>�a�nfQhA�E�:�d�x��k�����4���v�	r��L5����@� ����d�������E{/�]��dn�<'������D{/�����e����%���A��GPq�5��u��E����[M/��?q�\I�~({�Yb�
��0`�/H��zQ�~����n��J�� W�����������Am�����e���Di��H�a����z����V��$��8���O~�o����7�"�'��)_;���0Y&�>��O7��4�t��V��� ���v��p�o�X|iQ��� e����_C�
���h6�G��B��^�on=��[���U��	��_+��!2����+����mp\d`T~�|��'J��(2D�3�) �0�.�o����;�+
A�
���TV�n
�3!�������������\�=�\���.�@{�1�>-P__oz�.�+�F���U��M�fz��V�=���d�����t��e������S�L����P�;�>��u� �����'s�Sd�ov�C����y��8w���e�)#��������v����2#�Y"Y���2�I^��Uyy��^�����ii������|4��Y������j�{>��Y#��@����I{����7��7�1#�yx�|����IG������E��.�L�	j��uG���Nd�D�k�kYSSsPA;-^��2����W���s��4�#��wvG��T��A������O;�����iSF���=\�=�bp~A��z#�G %4��o�L��k%Y}>^�a{,---������R�����3M�C�K�����y����j'k�����8}m�^3Gz�H�����a��~<�"}�����U��spx�x�[�?�&���.20��C���M�e7��Q����p0�L�}�M��I��HL��-���e�^0F��Hy���,S��^��_C/������#�"*���:w������Mx~���zN��q�%d��{���~�1�"��s�|���r�����BH�d�[�����)N�V����z���Se8���h�}.7� �z������E���g�7��0����/�]\$?���\��c�(�t	B�	�k������7��]�����}�h�_t�L�p��%A�2u��~
�(_�N����y?�����ux~7� �������[��'^�mFb;�`L�P��1����B6
?���*�]�bE�P����b?������V����2��D����5}�t���#k_�+k���Z�Uz�����F���/��"�W�)��oF�S��L��/^lZ�T{`�6���V��?`F�������+JB7\�=d�@f�_�w���/?��f��������hl���$��g�I�7#�����D.�onn6�A������u�L+:='\�@E*`��s#��M�fZ��0|���w��U�L`�~]�Z�W��w��g_�u�s�'�
\u�d��#���y�L+1����u����Mp�C��=�v�^l��l�|�b���0�K��'_�-_�����M'&}������W?]`F8��f��Y�f���\�?���s�+�����Vt�s�Uvsn�Oee�i
)��g�������
o����C�q����u���c����	�fA��=<������u�@M��`z~�EW��v��#������N3���F����&~�3@p�_�7���)���e��n3�g>u���[3�����������k����������sz�Uuu�i�����$T���������0�����>�*�j@�����QC����<��^��O_���b�l���u�l�Qr�
g�M7M&N8��,�V�2�����o���S��d��������mYr����+{�Ht3O+?^x��(kF6�K��;��u?~^~l�������{_8I�|�X���TH�=�
z�~���e��Y]t������<?m����"�X�#��K����pA������TSS�iE���H�h���zhE]������h���T��h��w�_��e��O^������:���#�������t��L>������d�
�jV�-�tsn�s��VYYiZ<MO����� ���1#�]t����o�&G9��Y&@~	����mr���C�7�8��	r�M3e��	f@�(4���R���999��������t��������>z~��k�X_�����k��� ��:�~-����D���H
�7o�lz��
�����7>����PE�?=��uF�
���O��kN��M��`�ec�	x��%KL�9'�&�S�=�v���bz�}��)����hz��,p��2k���K��/�2O'rG���K���x��?b�0@FX��h4��������3E����^|z��"j��Y�����"�s�\}�n���Y�l�i�N2a9������O�)_�����O�4���0�P���N�;n8Cf�~��'��e��wK�xj<:�SQ���������������}�$����f���e�#����}�kr������eFb��@�u�LYPz��
@Zi�[
��Au�*�x���o����2e����������}��������_������$Z����l��0���s@�������U���g���l7��yx�\[Y(����K�Y��Q~��,������m��yt>�m���n� ���eF��|��r��2g�3���2��!������Gj�}Y~������+�H������F��B��U�J�---�5H�Hs#qz�[�B|-"��kA���}=-0`��:wx~/�
�ia�������X�����*0���-�AO��O�v�����<S.=o��G~�2�L����-�����&]���M{��ed��}f$����8i��f���e�G~	����7��[���>����I����?]��`��j@�D���JKKMk�=�Vz�����z�}���
��'n'Rp@�l}�>�"�f�_v���=+w<�!o��cF���lb�����O��y�j���e�P���)�>�|}\ee���h|�5���^�������*g/?��S������#�R������_��&?��ey�A���fPw�x��^t����M��Av�����;
����v��jkk�����+V�0=��l��5�%2g�����s�y~��x�b�7o��
�j����={v�_hkk����;�Knnn�
�kA��GY�}QQ����������W���K]���e7������;A&}�I���V�,*�t�H��<�L���YfXyy��^�����y���i�rc.�ff���K___����������,SXX(����6| �X����)��nz�][Y�x�%����,3{�e�
^�2��'��2X:;;��g�P���Se����6�g�{UWW�tww��2j��P�3�=2���o��G�e��������\wq�|���f$9���D��z�z
ia��-���^YW�fG�hUy���B^G:RUd�������?{Q���.2p�i����3�[���j��B�	��f��2M��{����b��"�����t��>�L�]��0|�<�E��~�q���<F����a�������5T�`z��Y�u���nKK�iH�V�
��;����k�o��A���-3������_}�|�+����1f@P�e�B�	���v�+74</�6�iF�+>�0�m�iRz�n�e^@~	�Z��#7��AX����6��\��e����O���3���BH{�)�V��jkkM�Z�|�9t,�����t@�{m�r��mr�����gw�QgN<�0��/�,?��i����4���,H-k�>��F�yf��(x�����m��W�3#��:�(�_4C
�?���L`����� ��~l�\���b��f$�sN/w�4S.��1f@:Qh�RUUeZ����hZPE@�z��~�����/��/��O�fF�9��C���-��|s��w�Qfb#��������>}�i@�{x�v��/6����f$���s����O�Q�l�Y&�>�Ky����s����aF��fA������q�1#���i�g�[���(--5-���ME\�M����<����:s�a�r��L����'���gF >�L�J���M��}�����s��\� �����N�������v���N+<B�������f@�Ph�6k�,�*R�
 1�����7����U��yF~��.y���w�
1B���'���g����@�	�k����u��i�L���wz��?{I��Dw�����W�*��ESY&�z�����������r�C������q���/<]J&nFd���
�uuu�u@KK�i
���5������.�@������G��,�xn��@�j��.��������������3������?)_��@I4�`d��s��/��=Z�H"�����7���e���l��y�3�)S���E3�'iF�,������o�5�>'|�u3[�������B�7xG���L��������X�����KSS��
���k���Z9�~.��f���3g�iy���s�<?'4p�d���F���4��f����kC����I�`����b���
��c�#�X�"��/**����PPOm�#�Z�M^�x��8w����e�'��j/�������]��D����e�
^�2����e�����O���H?�"!�����v�����=A���!����vaa������@P�=� c�#����!��J���9���-K��&#���K�=�G���L�lf}��2�*^�2��I~�DtvvJOOO�=u�T9rd�
��A���%���76)((�Q�F���������,����i����7���uI^��'���?�%��mC�h��b������[�N***Bm�Hav�scU����e�L+3V�ZeZ�ZX��?^
�8���R�x�T]>��Ex��L��TN�{����X��b8������ �7o6������d=�r��|����\u�d���%YYd@z�/���Ro}�^��G�9.20~�H���I��������6����5���*����,p[�Cj
�5�tnii�i�O����F�K?������'�~���l�<�q�u���#�_?E��k����1f�!�t.�	Y�#��U:������&����:����q�F���p��6�=�v�^l��l�|�b��@dd�@l���:���V����m��f$�93'�]7�!���`Fx��6Z	�Z=7��K�������Pn��E��d���J�J�L8�]ou���m����YY��]f��������:I~t��r����($�,���ir$z���� ��s#�2�-f*K�D���[n�s�����f$�c���[�?M.��1f�#��#�����F-��E��[�Hly�9��s���/�$���4���BH���������kM��H�qu���%K��Vzl���P@���A���]r��>#���jF����K.-��n�)s>F�[���Y���,��.�k���1�A�:>:����P8���H=�5cL�tg����_�+7,� ���6#��<q���h��(kF >�e��_@���{U���9yv�[f$�O�4N��i�|�S��^�3�;��XNN�i�[�8��Tx���mJ������*�ZY�{
����f���3g�iy���s�<�D-^�X.\hz��M�fZ$�o��1�1��g��k�����I�]\\,����6�w�}�Ad]�EEE���j��~�z���y��{���8s��yrY�	�w���Hvjmm5-����B��t�H��A�2������LD{��scm@����wa��Un��I^�2�tm�^���g��,,U�f8�\�j�444��� �����v���=���D���jJ~~~�
�2�?���)�>����v����7?����g� ���e"U��ewO&�%b�������P{���2r$w]����T]]]���j��Q�Bm��X�H��o�������o����zQ�\~�w�r�O�Yb�Qh A,�����ZA� V^��;�P;������LoPcccR���P;���A��G��D��y��P�����##���$��O���d��Gmbsnpec�������L/�����+���^ �Zts�L�r�6�B�D������md��E��"��@������G�����L/��~� �w�
�g� �.�e"�f�n��$�D\#�X�*.�F��������;�!{����)�|�\q��2u���I"KL��#�yZU�0X��.�����������M�qr�����~(2��1���,,������J/N��������ssn�����|_��"y�9��/���"��2@XO�~���"�v��"��=Q������ ��Z�J6o�<��l��M{��;_m�m���u���G�O��1Y|i�3����*�b�
����5�����Mk�nF���Y������rsnd�=���j�����eB�V��|�h�m��3s��F~	�to�5�<'xr��������)r��B3 [Qh�HCC����9*++�g��x���p�K����~�Q����d}�^3��Y�����.�pe�?��Ra��uC���??�������W�@�972��g�1���������m�M]��Ht�y���49i��fd�%����v��vj!U'������r�)���lF��"��/����!��%#������H�-��/un�%���'^�mF�9�����M���)r��m@���������j�r������Ebqsn��5��g��<3�lR���5���^����5#�U�:>�yqX��b�.�H�%�l�i�>YT��<�����v��y���i������Q�f@���2���N***Rr�iH\cc�i@]����=r�#�������y��:3�����+K�����9��7��9~�2�u�X���D���/�f�2�����KKKM+27�F����4��O�nZ�9w5v���i3���]P(/)2=H=�e�A~	 �<�z�,�_/���cFb;�����o���<���

 �4���������&�T---fv�I���/��+�����W��3�����-��}�c2��G�Q��g�����5�������?��\�o����H�������l��<���D7��\����,���hF ���	��������$��?]f$�oT���r�s�(3�O(4���0[h��Z��y�L��c;������+�I��3��Q���O���x�T�{����{��w6��������M�/6��M�fZ�^m;��o� �����DW<�0�_4CJO;��@j�/�<�K����v���>'�����6�h��u�L��yP��BH�l 6
�[[[c��,X� ���Bl �z����=��i���G�'K��/�����C���kF�����B.�7IF���Q�� d�����5�������/_1{��D:/�Q__o
����7��e���w�Ht�N?*Td`�q����2���/`�������o������7��]u�d����B�T�[���L�D���[�L�����VJKKM/y�f�2-��5kLKd��9��
s�������9��������={v��
����&�����Knnn�
�kA��GY�}QQ������H��]����v�S/�	�q�N��O'��2^��1�����7�W������7����s�����,��eF�M�=VRRbZ�v���2���,s��uRVVfz"���w\h���N���LOd���	�&�{�����KM������YfXyy��^���������@��52��joo����P���D��;X����P���Cm (�@������5����w�^l��{�|�3SM/��=�G���L�l���C��Y�K�Q���<�l�;���e&�'����������P{���2r��P�3�=����K���C���5��K�?�}�������H���w���x�h���������E>�Od��G����c� �x��
^���L��L�K�,������P;���A��G��Dp�=�n�Vn5���8�|��)�7�_Z��_5o��6�5#���9A.+�$�'P���@��2�%�e&'Y��.g�\�o(4��5O�����+��R�x�	�s&��S7o�������>[|�A�C��/�S\\� �B��'����~O�����_xl��������s�����@�������e�������~>�3����V~�2��e4Z<���:��P�������ksg�W�ef����/���y\p� b�#���A��.-0��u��^|�=N^R,~�����Od��G����c���������J&�V��/��^z9��@��Y\t��b�#�X�"

���o�]���i�>3���S�������G��:�������������
�(�Ch��s�S�L�����MMM���g��&N�hZ���/~Q�z�)�s.S?[�����b;�����{�5=p�����/W���������CG��������@'����	L�A�����e:-2`�4C�Beee��������LvOf6�����<.�F��T\p� b���m{CE:^}���v�#��K���G�`_�?�%���H+��9
�SMCm�����r��
IP�8}�O����Y��O�����1�/_9Yn��)����e��c���r�r�n�u��s#�Le���RG��}r���(:._n�K���"Z8A7���8_�Z9��������<�07R��@����-��;_p\d���G�������p.g@S,8F����+���!U�Z9W%[=W-^�X���M�}�7oN�2�s3��[#�X�"�=�������$///�F�^�x;T$� �m����S�-��� �y�A,T��'�����e�7�&����O"w�����~�57��k�v������\x��I�~�w��i�L�}K�,�����������+W��m��-��?e��}����u�������'O����d����������zE�;�����G��.+����`���0-���B�J���|��p�ef'�d��lO/�ojj2���3�xY��}�5����$�����L������<��� b�#���;��u���+w<�.���6#���!�]R(��������/���S�B	b&���!U�h����J�
,\���E
��H@26n���L�\B����SkA��GQh u���M[��^4����"�H�|�$�x��fNh��s����L����a7����bn��^�2����e�����8��ga��4�y��U���F��Y���]`���"�����7���� A����������V�E�����&��*����=�Yfv�K�i��I.h_3�^{��h"�Z��;���ewO&�%b��k�A��"���=�v����E?��������e����"��'���a>����p�M�f�%�z466�����<'�H������-C���������40���#B�i��>�"����L����^T>\---������R�������H9d$���w���d6�@"nyh��"���D���d����b}'�G���U���L&Z]]mZ��s��97�)w�$�y��K��ko�|��*2��O��_?��E8G���5����1-�J���+����l���y���9!T`@�����LU��f�����5(�;�;�������rsn�y}O��t�Y���f$�c���]�����f2�Y�����HEqT�d2Q{�XUUeZC�97����.�������4#��<�piX<C.�7��� 

 m4D�j�J+�VTT���[�j�l��y�VO�����Wq�����~B����2��|3
�/Yf��%��y9�������s#3�y&�&/z�m���l��o{��D7���r��rz�f2�Y��=B�����'a�����27�F��_�6����G�~E���Vy��>3����$�/9C��1#p��V����u �^�n�OCC����9*++�g@������KD��q��y�r��O��Gb>���,���5���/z���;�3��s#=�y��%MO��o����z���Dw�����q�L;��@�q_��H�������{?���"��M���G������S;�Hl'"?��S��?S`F�`@Zi�X�:k��[VV&999�p;����������]�sI��?\u�L�t��[�YWWgZ�����~W������~N�;��97v�<�En}������.�o~�D����L}nV����<���R�������g��)����d������h�O��19k�83�Qhi�!���Ra:�����bf�m����c�������������1-�7?g����Z[[kZ���}s���}�w�17�����L=�m�����Z���[�Hty�9��/�$���dF�{��e*��_��j�}na�F+<j���D�s�97���@�m��'o{^~�z��m��<���$T4uT>����H�ha6����l�J]__:,X��1o�<s� =�������@�
@&�-�lnn6�Az����o���J�3���t�o�������"ssnxC8�g��<S;=�
�Re�����ed����Ht��9Tn[t���9�����-����z���IUU��K���K^���m��a����3������~�cr�'�6#��6����a6�	Z(@��p������e��i�������1#M�]G�oZ�Od���]����{�~��u���}�?&7�F��f[�t�y�f�N���c��P0Oo�#7��^6u�3#��5}��/�!'McF�{��eZ����?_JKKMH
�K^��R���/��o��M��\wq����O�	c����Phic�&�4�����t8��W��i�����V��h�b������:�)~�2�����Y�B���|z����q�����C&R05l��y�5HsQ6�H���_����K���>3]�������gF����/3\����"fA�d��5�cz�%���'_��oyV�y�-3��c�����K�&�H����\
����Ba���9� 	H�
���7B�'fz������_�5�t��f������H7/��75�9��~7�Fzi��L��h�� Qw7v�����^l�.(����xW�e���G��0\p��b�K^�Ro��Un��6��0��}q�������xw�����M��XNN�i�[_]]�����z��-���5kLKd��9��
s����R%��9���RYYj�*R�d�ihh0��f����k�3mmm���jKnnn�
�kA��GY�}QQ���q��D��i�������zW^0Y�\1���*�p%%%��l��,�,39d�p�������rY�z�����������W�M��7oN��L2��joo�������{>����Cz{{C���B�������{A����������Vy���f$�����w�(�O�:��`��=�Yfv
r�YQQ1���%�V��G$R@��.����s{�5G+((0�����+��'�4���J~y��g��>hz��/��h~��&�oX�*�>��u�}i�@~����������L:*O����r�	��"	����|~���2v�X�C�D�F�8<�~��\ 9N��j@�����lv��q������SR;?xY&0<��O7����3u�.wK��w���78*2P<�0�_4�"�Z�����j��E��[gz��������
zQ�W�����g�:^�/���H��#}����/�9~;X�A=X�A<X��=zzz���{��Gw;.20�����'E���8'���*��Sq~@�`��2�V��J(�C��s�������������}�H���z����Q�,Y�3��q��,Z���.��3�`�\q�d�3�Q���P���)cL 6�X��yi��yf��z��;���gZ0�����m��W�5#����*20���f�u�f����Z����bZ�3��2�uG������������<l�|��#�s���H��kI����oa|z�zYY����=�����L/����KSS������v���J�555�
���s����{��5kLKd��9��
s�����K��F*4��vee��
��,0��?�����
L/���g���!1mmm��?X����XrssCm��X�"�=�������$///�F����M[����4��?�����Kf�R�����$��%d�Lg�d���c�	o�r�V^^.�W�6�����"m���|��E��i.�����M2��joo�C�{>�������YJ~~~�
�2�f<�������b�������jzH5r��A�����e�_�}����k��u�2�s{�5G;��sM+�.��y���Lo���_�w�y�r�J�C&l������'M�����=�j������j�p�	2r��P�3���������������%��M�����'B3�0��S���2�,1�F�����������"�bzQ�>&��u�"J��s���*�E��bb�QA���I���	dh�N�v�m���L�k�0j2�-[fZD� �n�m��"K.-����F�9Tss�iE���bZ���---5����;���	��.$H�L����"�f������R#������u�������u��c�����&�E�+�������N��c�8'G��*���{~A���V]
�;�g+-`�P__�V=	Z���z��b��{����y��g��������YY+�"�"H6�V7n4-~���mily��V�:��'�o�A_�h��Zx����i���Y&�i�6�&S8Ui�T�f����?}Q��D7n�H���O���gF {e{���t_�z�K*�%�1$�^��>��s#}�/��o��C���9������������f�EgkF�@�,]����s��Di�m/2��[_���W���}�������:�sF�qz�
���Z��>���&����1{��D�3 s����7m�.`/��.��S��mO�+��lF 4k��,�����O�4��g^~��Dw��1r����'iF ��!�����h��,�s���M�k!���*�J�}?e��s���������(?Y�aF���g�����8�3����J/�����@�6{�_}=���VWW���x�r80W���dY�n�"V�b���p����Mi�����3�0� ������n_2CN-<��@�d{�	x����M�������ed�����}�h�m��x�s�����D�*677�����R�����b�!��}�n���!��J|�u����J���Hl����m~~�	f��3��\�c99o��-t&�j�Ha�^��lh�q��S����b}����!�9Z�}����L�Y!'�__+�Z+Gb.N�	k��1-�9s���7��;w��������L���%�y���RYYiz�4���7��,Y"


�����S������6�������%777����� b�#������H���Bm8��S;�G�~��"+;c�������x��P<�09���r�)���)c�(������DJJJL�.�Y"Yf���e�{��e���^�z��
_��9��J�y�~~�����M3�A�7ov��&�ff���K___����������
�%???������]��q���i���v���KSL�B��=�2����e*'�&��c�����d��j�����;���e�zOf����2�:;;���'��:u��92���u���������P���@F�j~��O��{�������w���.+�$_���T ��'�����@�X���_��j��������tZh�~^��V���}�{q�{ysn�Cm���F;)4)�V�\t��b�#�X�"

$�k�{��=/�}����>7{�\��0���v���=�zd���2~L����}�G��O���2���,���,3,���O�����M��_�
��Q7{PhA�E�:�d�����fY������/�&�y��!��=�Yfv�K�i/�bH�|��� �}�e��n��I^�2�u�'��`��k�A��"�}z=���P���v`Fb�t�!r��E������
��?�%����4��`W�477��`��*�p�)��x��vZ0���O,h�_���('ExS�o�b8u�PZT��q���)cB)2Rm��U�5�n����	�>#m�U7n4-A���n������G��N��8-`�����"~���m�X�^d@����K���:�un�Z�B*^!7����/�����������'w�c�Q� � Z�]=�B�l�����*Y���-x���.��W��>}�G��h���F��m��]���m�;���\����@��*l�Ye8�������E+��*++M@<��WnX�A��1��M;Rn[4CN/:���L�e��
TUU��
�]�����b}}�����]kZ�t�����s���s�]��R���}r������l7#�yx����%r���Hn �r"% �JC?;��������K�����i3�^�6��������P1�f���|P���w�����V�
�����n{�}^�f�i���3���a���C�_�hH+�NV��o���=���g������?�...����P�;�>��u� �����"����]������}7�%�+��|�8������JJJL�.�Y"Yfr��e�=^�2��P����M/5��_�
�&K�&�Q�L3�������/���|QGG������������jA������:MO�.�>���b���c���?���I����,3;�1����za����T�~�����N�TWW'����;��e��'��a������jO�:UF�j~��GPuuuIwww�]PP �F�
�?c����U����2����q�\wI�u���|���S�B	b"Q��;V]����I`/x��D��
$����7��Uh��`{��%1���jg�"�X�"�=��B����G���������7��<Z�^N@�u����\V^�2��(4�R]<u���	�}I��Y@�q�)����?5�yt�<�r�����O���&�2��3{�e�K��7h���J3���c�o����{y�t�j��-7"��N\p� b�#���A��w������������eFb1"G���H��M���|���So���%v[iQ�H� ���"J�t-�5�Aha�TI�������1�L:�PY|i��d��g�n�M�d6��N��������@^���N�����HI/��:�&���}=�{�~x~�����<7�C~	 Q���kr���9.20���r�M3)2��(4��������$;�����	���]��b-��w�n�c)2d��^�]����"�"���5=������-�����&]  �����p�zY��f$���*��N�93'����_pb���Pq����������T1E~���d�q��x����(�JMss�i
�"����+�j�=������R#����p��H�
��1�X�����}���w�^d_��"�6��2/\@U3J�4����q���f��=�i���l�l���������~�)�|�H�%�X{�
����GU��[�?M��`�o���M/nO�����������������"�h}�UUU��[���nk�
��������\8�����������L/��O'WN�A�2o��2�g��4��>��G�����~���}���DW9�x����"cF����L s�/X��H�o����^�=�������M��n�)3��Y����i�
����jP��+���Kw�����������V�.]j>��hE�z_�����N��}.�5���0k��1-������+��B�|�I�����>[|�A�C����}�wO^^^���?��=�����|���u� ��{~�����o��^|��v�!#�/;Z��kF�
���?~�x;���H�Lg�d�#�D&Y��9s�����������M�_f��=�������5UQQQ�w} H:::>����������������;���v�^l��,m���������.��w�ezY&2��Y���s}���_f^gg������S�N��#G������T]]]���j�5(�F�
�?c�'�/�o�����-;����:v�(���")=u�A&�O�Yb�Qh A,���/dW���AvX:�tP������2��?�455���>�<��^/40q����_��<�����/g�u��{����C�����A�����k��^d_=��D1�������	L���\�!�D&yus���

 ���A�������{��R��fy��7�Ht����\Q"�b�nV`#o� ���Ld�W�L

�M\p� b�#���A��O�=�n�Vn5��.8����B9�P�?�+�'��,1�F��@�4$���J/N����B�t� [���A(2������v��/�{��"�{����2�������68*2p�	�K��d�d��m��
�68.20zT�|��i���i��r(���]Dg���������\t�Jn����������#��t|]+����I��K�����^��k�s�=�����.��{����|�����>F�y�f��qc�]YY�����;OV�\izH�-[�H�=e����
��c�#�X�"���<y2w:����,�����v�������bz�6zw�0��]�q����.`��,�����Y�������M�=�/�y������e��if�`������V�����P;Y�,�������/�.**��|w�F��=� c������R�`���=�	�R6�(��|�����d��=�2��,���,s���i���/��;�#�X�*��� b�;����r��;M/�sN'�]\$�u����O�Yb�Qh A,��4`������m�l�+��0����������_���.�_8����)H/o�MG��b�&\t �����������w���\t��`�#�X�"����~��.yp�6����g��I���
��?�9����e"���e��]h@7�.\�0���ht��p6��if�t\d���{A������v����o����'��?3���M�=�Y���e"���e��'��2���A��GPq�5��u�k�?�;�!O�����w�����L4=x��?�%���"tF/n����8�i��tc4^+���#�v�����
���2�s�~>�BC�s/�u/o�u3��@���������E�P;���A��G��D8����������I�������?e:K$�t&Y&���Yf������d6��-Y�$���d��E��"��@���#k�M����5��m�������L���3{�ef�L��W�L7�d�_��D�{\#�X���xzg���{��{W�9e����")�|��W�O�Yb�Qh A,��$l�����i��~����
�]�����?6R!�9N�����W�����s�
�SUd ,�b����E�*�>��u� ���P{���o��y���`���O-���4�h��s��_�Lx����0�

�j�nX2�u�43�B:.2E��=� c���;�R�@�<��[f$�qc�C���K�4#�F����,3��e�m^�2���I~	��"�=���D������/T`��������y����.0=x��?�%���p���zq�^8���A���u����*--5�A---�_��p���@$���s����1�+�E���_�x���+V��&��`���e^�|���n�U��jAV�cc�>�������R0Fn[4�"Y&�:��@0����\s�s��L9v��^s*E���4��<7������N.�O%{�}�)2��A|UUUR��}���i5�x����}Fdj�A�r������i����7M/�E�-�CG�� ��e^�`��J�Td�������m��o|`F�����CE&u�DC�	�%<?Y�!�|�&��'�M��>��������0*���D���������_L������*|'}���+�VTTta�p������
Xi��5�{g/z�|�������3���YcZ"s��1-o�;w����
�~�466�>�Z�*�1l��y2}����w7o�,%%%�����S���\[[�������������e�>��u� �����"��������}��������O��.�lz�v����%��m��t�H��Z��eF�9�>wk��4�����h������n��N^�2�t
�^����O����������C�H�f�M�K�,������L3�������/����������
�%???�����_��?n�_4m1��4����A��=�2��/Y&2��Yf��d�_����SzzzB��S����#Cm��X����.�������@F�j~�����m��w���w�Hl����u�����
��?�%���"6���������U�����sR����1����m������7��:�^�xq�*�Z`�����b�_��pljg�"�X�"�=��B����9i������')7�T��h�S��D�LXE*\M���v:oMM�A���=�t���3��YfX�
D�"u���e�e��rQ��f

 ���A���,�����^�O�4���}�4���cL~A��=�2Xy5�L��L�KXq�5��u����k�^��?n�_:,��f�1A���P�����lF>�Od��G����+���������t����"*���a�l*�/o�MW��D��
w.B����SkA��GQh@��7m����L�`��'?��r��C���@�����H&���aZ��7�efF�
D����S<���L3�(4���"k����������6����f$:�9�^>MN/:���O�=�Y&+�f����I~	+.�F��T\p� 
���|�=��w�����Hly�9r��E�7���d3�I"KL��#�M�Z0@7�:��g�P:����	��s��1�-2��1Z-������cP�/-���1�`�^d@�G�����9�=����
=6-�j���}^{��x��s#���i2�,YbZ�����r���9m��x�i�A~	����_�kn}�q��O�)w�c8J��_��w
o��o�U:��h!�p1�H��Uxn=�"��l��6n�hZ�`����m7��.�u����� >����j�h�9`8���_�oUWW7$3�\�^5�E�3N}l,n���K�p�jhh0-^��S;�[w� ���;Z,�}�����d�X�����~��������d�o�e�~gw9��ES���9U�s��������nY�=�y#	o���������9b	�����^�V�2-����6y����v�����K�L��***Lk��\0����}����u�7TUU�iE.`e/`}l$n�
��y�i�����K~��WL/��~�@���M���/��[��.����d��7�Hl�&.�-�!W�?��@�Ph�D��rx�/���_[�2��]J�@b�P@Xmmm�"a�����z���~���:�T^^nZ���
�����H���F��W}����^�Wm3#����H�O���M2#�C~	�Go�~���_���k�������>7{��y�r��1f��B�E�n�O�nZ�[�`�i���`Z����{��?n5�����HN�L�
p�~����KM+6-F000 MMM�#Rq���f��dn�<UUU�5��s#�V�ZeZ,^����|�r�:`���� ������l��y�
3��c��E3d��@f�_����{��[���}r��������~m�\����@�Qh�HII�i
�6mZ��:���V�:`��%� ]���'
�i3���?~�\<�x�������7��X�b�i%6wmm�iE���H?-lj�+X}}�i9��{��S�(kee��H��7���m�M[�1#��5}\��@����@��_��?��K���/���:q�'���~k��s�x3��d{A
�#�Dm=WXi���9�������sO��L+�.�l���������r�J����n�:������>��V�G����w�B��6�.[����[�����~���}���D�ET������yf�;�/�����wdq�z�U�63�a����/�&��l�:*��T���!p,''���-2g��5�%2g�����s�y~�h0=o�<��m���Pxo��QV�ZeF���W�U�����sf���	�6�V[[������������?r�}�Ad]�EEE���������!w����"�����'O>���g�r��
���,�,3���z���K���H]]�477)F�������Y��HdzYY��<w,���]�v��ssn/�r���V�^mz��&Z�"e�J�-����j1�D��ifV{{������~�=����Cz{/�,,,����P
�d~\�w7v���m7���QY(�=o��!��=�Y&+�f����$�D":;;���'��:u��92���u������������Q�Bm������z���:M/������..�c���4���D��zH��/o�M&��V����Z�7B����SkA��G����[������^dW]8Y�4�����������h�WTT).�������:�E�Z��������?^!7��/g�a�����H?�"!��,

 ���A����O����~�}`�<���f$�����+���NoFT����,��W��D�d�_"\p� b�#�(4� ������r�#����=f$�k���)�T���D��z#�G�I�o�L+�O�t$E)�����s��}�p���]Yr�zGEN<�0�m���2�>�S���9�EN/:B~���(4�F���M6o{��v��\Y|i����������E������Vqk��
�[Ql���3Td�����Hte3��������6#��w����U~��W���~3���O�]z�(* >

��������_3�������1=RG�	455���K���Y�f����jjjL+5������ ��{�v��_n�z����>?���/�,����`����\}�s��/��HlS�-u��&_�������������:����5k�����9sL����;����|�rY�`��eFcc�TVV�^t�g�N��!�������[qq����������D�{�u�I^^^��;��@�������W��<�xY��"�C������HII�i!�e:K$����:���2�A�"������k�\�o����x�����)z���^`��

����W^y�<����~�D�Y��g�->���!�z{{MK$??���������g��;���.�/�@�e����[����^lW�9R�;m����o�������cM���S:�����`(���LtO�~�8���=�����)===����Se����6�g�{UWW�tww������F�
�?��u��������^|J���?[,#�/X�/���S�B	b���@[Qhn��SkA��G����?��%y��=�w�i��;o�izmbs.2%R����[�N���L/���Th`�����-_������2����3%���H�:�,���{M@4���/�l~[6n;p�x4G�!_�;F�O�bd��0a��a8�2Xyu_&��&.�F��T@e��_��W�x�]�_uVu�#�����3��p0�e�Yb��0���_t�<����Z�%f����b� ��^to��������O�����]��1������+�.�Q���c����GRdxV�<1������r��/8.20�cG�O�5�"0L9�jHH����p�thKKK���Z^^~����v*d�W+��D��f��fw�FP��D�{�u�I^^^�����i�|�g/�^d��,�����;#=���O���2��
Y�>���2��?�455�^|��c]7�����LOd���'����jkkz�����y����kZ�r���6�;���d�����t��e�G��O�2��"p�n�*}}}�����}�o] | ��i��y�M���������c���}��G3D���aZ"�����:������"��'�e"Y^����L��;�#�X��l��;�*^_���'w<�.���5#�����u�g>u�"c_�?�%���"L-��Q+�FP�&Q/nE�x5�V��p�"�X�"�=��o����_������7?0#�3s�|�'���@�����/��e�/�O��@EE��X��������,���^��,3L7��^�����L3����?���/��D�v���w���� A���,[���+��/�2����p�|i��b#��d����L�W�L�d�M\p� b�#�(4� ������W���u8��<i�\wI�L>�P3DG>�Od��7�|�Jj���f������:^�����E�7J_Zlz/"��-|G4'������Vdn�
t�C�8.2����Qd<�,�-���#���Mr�#��/������>�"�b@�im��T2�nZ��7-_��4�u��E���E2f4ws���,s��Y�5H�C����oZ��s'�������rsn�P���-��c��xz���������?].��1f�5~��YWW*|��������)�`����_��h�;��~������[��u�4#��4�p�_<C.�7��R�BH�Xavmm�i,���4��kZ��#�i7����`��}�x�xM�g�����&�x�	�s'sw4e/`����A���W�4��
�o���f�8Vn[t��^t�x���e��za�f��rJ����M�y&R, �g��'���>�D�Z�������_�"�~��{������s&����S
��@��|�����v���D
�u�guu�Gw����p/]���i�ja#���X�f�i���3����� �w�6����/)�����/��/���c����x�b����6�������%777����� b�#������H����n�o{^^�����c��J���� ���jZ"%%%��l��,�,3y~�2������
���#=�~���k?z_�q�|��;��f�ad��E��Y�����7�Q$������!����vaa������@P�=� ���������N\t������hz@��=�Yf�����H�#}�MMM��=wLD��"��A<Nr�Lr�e�_��e������	��N�*#G��?c�#�������;����G�j~��u��M{B7o���f$��G"�]R$gOgF���O�Yb�Qh A,�����Ho�@[��d�A1�)����������o6=d��|�;�o��o�.:EP��D�{�

���yd��;�!#s�'�<C&}���*�Y"Yf���e��������D[3����.�{k-�9w&%Zh@�if/2��(4���"k�2������K\���b����*���`z@r�=��=Rd���/Y���+�s��[ZZ*�h��������N�����U��w��kjjz}^^�N�L���E~y0.�F��T@ez����t�C���^|Z�������#���I"KL=~�"m��n�!�>N��0{�
D���@�������6���gw�,2�_ZL��8�d���J7����htC����V�����?�*�F�X��;��if'2MxM_���|�������H�O��d	?d��Y���M�D���uL7����\H�����Ud@��=��������J�9{�ev"�@�l��O�?�������I������?�"�F��EZ�7��������l�����vv!�@6���[~�fz�}�S��gmz/�S���`�n���BX��C��.���Gc��>V��7���������fv!���l}�}�q�Y��f$���*��f��3&����%���VZd ���������.�YsD�����Td��^l �s��������P�vYT�^6my���v��G�O�5S�?�3H

 -ZZZLkPii�i%����>?�vv �@��"���oz+>�0Y|i����oYf�;e�V-8���:�o�hoCl�n���9b����������fv ����y��P��sX<g�2>Td�d��f�u~�2��d�"J��V�V�>>��)L��Z9)``/�`�#[�_f�K���7>����%����4#�]WY(����r���� �(4��pn"��6md�{�o�<�i��E�ErrL�5��ej��w3sZd@�{���D����N�O7��Vd��F�	�i|�����������.�u��|m���gF�������e/����������5Db��ZP�/�/������'w��?|6T���c���f�%�M4#�L����"��&md����%��b��Ev��B9�`���~��U/��������X�i��0���	�m�������^7��Vd��D�	����Sn�m������B���"� ���[�Q>�kA�T~�+V�Vb�F�:������/��������U~���HO�~3�.�"�^w�M<��2�B�J.�@��-��6�u�K�o�L/���� ��M�]*/e�z1���>:�?����s[�
���N^��9w6"��2Mx�{��?��Q~l����C�����t�,w��25�����ek�W^^nZ���~X�Ko!�����S�������]f$���G��q�|q�d3�4

 -JKKMkP]]�i%�������A��
��FzW�o|`z;z�HY�Y���,��L��4�%m����m��v���N<�0�_t�|���f����e677���h�TW�XaZ
�{UQQ!999Cwr��p�!#&���K�i��c�#����r���v��`�:^���c2���f�@Z�\{�����*�- �"��,md���_����nz�-��X�<<����,��L3��4�%�6�)7,[/��gF�+�q���h�7���U��L-`- 0�|��O
�k�>>L����BEbHeQ�\~A~�Y��p�s������,_���m����O_:Y�������A�gPhic
r5�M������3�@��������m7��.�7I��������������a�,	��U�b����U��a��X��S���k�?���G�?����m��j���Z��aH �)CF�Y����\�|sssor������}����=I�'��s���� *�e"Z�i��&"�������39ZL�����FXz��2Naj�i/��h�"�:�*H��^L�-��T:�z��ex�_ �~�b�����R�y�EO���(O-�l@D&�j����	aU�m�"[RR�[@�l��6���/W��g�N�#��$Y���,��L3��4ITN��_�u���W��M��Y����e�?_Ula��i��>u}~~�477�P_��S�':��2��/,���K���76������k���2�w�8=
�D@�� ��z��BX�����S��zLLL�0�����vhh Z=�j���9�{�%����W�� �e"��i��&"���
�_O����~�G���W�<p�8��kC��IL�2=E(,,������t������999z���z��*8`=���abNL~���WJ�������Z=��Y����N�K��I@4��BJ��V�����V`��5�Bo���^����"�@�z�����w���g����A�t��2m�4��L�BA��x��g�^=��q)�e���rfF_=p"S�LOETao���zm�[oE	U�����If@��_�%�a��z��_&��W���������ce�@�P@��i>N��{�����S��/�X�'+..�-�����V��s�=r�����@��***����j���Kll�������a"�u���&qqqV;��['?zx��j�#������:]�������HFF�n!��;K$��LJ��L2��#�����Jill�9:�~�B���J�vjj����[m�|�d�^�E���6��c��~��%���� T�=�Y�389�l��@�>�����
1���KNN���~��������	�����d�
�����e�t�p�-�D��F*�t����K~��	X�0k&������#�����p�1=�]���fVoI��G��s��J0�>���$}���=������%v���"����������\(�������aCn`p��b��D�{�(�
,��Zy]�w	K�]��c�t��?������%�eY&!T�2��!�

�t�����������f�m�&���[��g��= |�=�Y�s81�u�e�������{-�Oaa��u���}�H/40l�0�
��~X�|�I�Cg��3G.\�{@��54��yP�[T����I�e��=t0�*40`��C��%[�6*tU��*$U!l{�u*�U�cc.B��������[��Ql�@4{��-^�(�_�N�p(�LD2�� �@����|.2��o����iYf8�(��;R�m[�`�u~tEh�7�����s���������Pd�\L3��B���R�Z���o�Xt`Cn`qwk�����a"�u)w:�x�>�yr��y6��T�fz���spg/g�.`�F�	�2�t!��82��������F�)?�����a:�`����{�$��r)���#m��(w]�!�S{� ��=�Y��Ec�i����
',Z�(d��}]��I���O��sU�������Y���Su+����^y��t�Pk&//O�[�l��g�#F��+������n�*���V{������`�'�e�?�������u�}3�(7_�,����	�Ke�.*�4~~
�������X�@d	��\6���
����S�����a�H+4P�pL~��*����im������{����[gbs.w�(4��i��L3�(4�q���{&���_�q�<_.���l�f�)}��2�o6�#��{F�LD����^h��K���L��������=���
7��l���Q]]���������{����F����vrr�$&&Zm�����5���ly��o>�G���#^~tE���@=D.�Ig"K<J�����>����u�!�@�{�O^���� �_��{��L�?d�����C~��j��\2e���G�)2p�`Ps�G u�0@QQ�n���"�F~��K�om�����\d kbyr�$���Ph�D��mD����\���N��l��i��w���L�7d����^#��Q��������
��!�E


�;��9���b������u����|�����-������"�@�l�yX�zz�<��=�]���[�6;M���T�����41���6|�BG;^��T�X�+�����X�D�O��[�q�=������{pG�\���d����%66�jN����X�0���OKK���8�j�����D�y�����r����tNyy�n�ddd��]��D���"�Dg�3�t!�l�fpUVVJcc�����@�TUUICC��NMM��x
��,|�d�����f)xa����.=�������9w�=D&r��A��,��e���b�S����=�f��)+V��=��Q#p/0PRR��k���)���e�_���2��������j���HB��|�{����F����vrr�$&&Zm������=����Xy�/&��G��J���=�=�'��,1�(4�'a��������\=���������h�Na*�>L����"�����T�V���&����n��{@�h;�s�Y&)

(d���i�`:Y�t|�d������H����Mmg�.�w���FK���z�\����,3�E{�i_����y;R�[���})��K�H�
�"�ehp�&b��T�������������g�Q=��g��o]4R���C>�Ld���E�B��2����������[�@�
'x���^�����~U����,NE�y22M�������~�S������%�����$��e�������@8P���I�^m�P-Z�[����
��w�"��Dk��'#�@ |P~X���^���
�!�O�0�2�X�	�[h�	��d���x��y�����{'"����i� �@���r�����R{�Q��m���������{��Y���{%;;[


�~x* �
�����������T��6m���f/��*6�>��X�"Jaa�n�����%:��X�<��Jy�p��9�G���5L�Z8I&���#��i>N������:���7�^;%UYU�Xoa.����X�D�O��[�q�=������{f!����
ijj�����k��c��D�{��}����I\\��������W��C
z����,w|�����\�D222t�.�Y"Yf��e"")�t!�$����Jill9<����HPUU%

-�k���J|<Da�`2o���W���w���w?�"U�<g�����3z�eF�h�2=������V�u�?//����^C_�t��CIIID����e�_�_�Ruu����[���IHH������a��������������h����|�,[^%5;���K����d��$=D?�Ig"K<

��E�1����B�h����
�U��*�fffv8nkn��uf^%�s�R�m�51�&�=��T�}��u��������������R�t�'�8]�b�� p��������Y&�+
(d�

�t������<���G�$��r)]�v���k��uC�L��^D'r��A����e������3����{��[��bj_i4��P�E~���X�0�`�_�^#���E��w���"���w\8��3�%;������B����1W���J*x��1�>��0�*tV�ikn�y��������������=�#��S����E���J����Y&�
�&:[���?����)�{�#�&Rd�&'d���$���� �z����z����o!u}aa������V�D�M�"�%��
[����|.2�-1V^;Zr�������U�5����J���9�P���A_tu�������'�lh�)>��/���F�<���d���[�&��,��&|�k������j�a=��sN K�M��!�����,S����"����N��?_�u�P�k�`�m2�K�}zg��}�Y]U�G�;��~���d�Y���T@XDsEVU0�~P�U����T�V=��byyy��BU���m���
�"�s�dN�	���
����+u��������#t`�.S�i���'�%��6���&=��k�!?��I�g[�?d�p2�K�����{�]'O��Z��o��)�w�X6�����H~Pw��P����f�����W�-((�������W�U�l������ ��y�`�
��lh�I�s�l����������J�=g#����������w?�:]n�,Y����8����\%����G�K/�=M��>\�@���� ~��������@{U�-Z�[-rssu�5��y*��^���J0�F��lh�I���R�����*2��;^�&pJ�	t�&�6�#�#%k����%�J�n'�}m��}d�0�% r�h�,~q����
V�3&w������=�-(4��q?��w�_�b�n�|=�P�5�����b����u��5�����������sn��)�6�6�������������;mB���	Y&�d�@���9 ?~�SY���i������m����z��e�D��0�{k�����x�����A���Y��gQ`��2����=��&���GQQ�n��������z]=	���M���pUd��X���6>����<E��qZ�	t�&�1o}��*2�mw�E�?s�,�7A�
��G�Y&LE~	=�j����:�|O���r����SO�����Qh!c��VVVT���sU��+��+t��*��qM�����h
�	��4��[��U��^k�]b����u`�h�2�bbb:���h���3gz����]0��wd�����fy�����v��/}���r���@�8-��A~	S�����"/��M�x��g����12��t����(�����t>P��x	��6��_|������L=�?���#��k.))i�9��-������w��;����uKd������������u/�hG���
ijj�����k��c��D�{��}����I\\����}!����t�3��J�@(�����HF��"�Y"Yf�9!��
��%KT�����N*v��z��������9w8Dc��B���������F�����HWUU%


V;55U����6`
�`�_��A��`��y�s]�\����8�g� ��~N�29�-�$�D TWWK}}��NII����
8��������:����,���������,�)��{�;���r��4I�}����{��|����B~bv���C�����pPw�r�z�kF�&����?_����b��;D���h�	�#�Na*�>L�����Yh`Om����Od���?�z2���r����:��������,3�����/|��9w�Ds��L�A���C�0�p�{�$��r)���#m��(w]�!�S{����=�Yf�sB����
����2�q�&b��T�F���qX�-�������b����Yi����o������'��,1����@H�6�2��tj#����*�����V��T�����q��w��}���y��D��\�����A�����t`:�d�EEE���M����>����ewj~�G�C��������2M��U�����|*2pzzoyd�D���}���%��/��.7/^�s������SwN�Xd��Ph!��;P9��F^e��E��Ej�M�
'���[������g�_�.	qD%gg��d�>�M������������B=_����z���9d��	���C~��j����N`�L=����O����;$ �L��	xB~	�h�n����}&���R���vw���%���������iV����bbbt�^B���9��������R���mz
���iC��6���D����IDAT����Xw�������@�iLmv	������X�D�O��[���{�����_���@;:TTTHSS��NOO���X�
8k&b��D��>--M����vg|Z�_.]�{�}��d�����^yy�n�ddd��]��D���sR��������V�����z[���;���e��i�_������h��s>M�������j���J||��L���������on�=��8��\4�;�F"��d����}��h�2�/����R__o�SRR$!�"ap>�=LUSS#uu-�#���%11�j�����e��J�w�%Oo��=e��T��[���uS�O:Yb�q�>��:�n�U����ob�Q�3�h(2���5�����G����*��p���fy��J��l��$����,S}-�233u�sTqSw�����6�)��}�`���!���T�x�s�>���"sf���&��|A~	�h�2�%����[�s����&���S���L��P��c�w���.T��S�������������ZO�����d��;�W�UU������e��e�Z��rK���E����\���;y��	p"�����S��a"�u��u~W�OV�=�{����E~z�@��3V�����'%%I�>}t��,1�?Z9)��g����o�?}���&==/�s�[����p��M�������5���4�g}�$����{N�y�a�~���|P��m����{�h�o������cX� ��N���D�8%�$��?��;L������;"�������UV���!I]e��T���$=��=LE>�Ld��G�?�;�������E

t�{�=l�0��^?��<����s�����9����~��{�����Z������I�����

0@�*���2;�IY�{V�M��k��y})|�������pl�e�n���L�!k���8����������p��4��}Z?�����-�����7z�eF''e��,N�2�/�+\�D�{�������F^xk�������[g�I����T�uS�O:Yb�u�o��rj���"���K0��g,�[n�E���"p��{��������Qd�.6�z����[�k/������O���n�>6� �^]�]���Z����6T�nk ��2���/i�7�����\d@��7G�O���"�'��R
~��E�D���:�3E:SYXm�����=���|�������	�+�N�:U������+<�����o����{�&�6m���&�=j�(����E�����a"�u?r����4��uRV�v���F�����= ����\�������w�NN�2���+�S__QQQ�,S�?33����3������2�sG'���P��M�����R[����u��| Zq7k���D�'_������{����&����^�?L���Yft2a_&���Y&�%����a"�=L���N,�*O��Z����qIr��4���u�����'��,1�(4�'a���vNz��X�3f��E�:|�?���9w$p���`���������w���:�1X�0�&r_�=���k5�bQ��~���O,�$��{� ��������)Y�����=�M�mi/��hj�9���B���Ph���)L������M��B�����G���k��u}�L��GN`��t����,3z9%�Ddqj�I~	o8p
��a*\#�qT��R)����G�w��S��s��^���a*�Ig"K�.�-tj����0������:}��Iii�n��~����7���u�}���w��{�C�
'S�Pn�*�"���e�?o_�(+V��2O�}�"��Tl;$�/����)�{��y= ��/�K�C��w�����������l����=@���`��M���Q�[�u�(AQQ�n�����|��	��d�6��/W��g~e�\��!��gN�2]T~����*8�%%%���=�|E�	�h�����X�Tm?�G�v���")C��B��Y&,����G%��ry��H]����F���M��#�� t(4��Q�����F�]�Yd��}3��w�s}�sn_��mm8�*2�Em���6b`7���4��mN�2�ED]�eNN�i��V5�
�3�@�-��L)�Y�4��X�U��7�����7�^{�p��w�Hb<[����}�@��_ ���z����*y��]z�����%��q��������nY������	����9s��X�B�Z6�fggKfffTl
F�Em�u/�6���z�?UP��QX	���V\\�["��O�-g���{�����=�h;KEE�45�lnIOO���X�
8k&b��D��>--M����v{�&���Z�{��2^&���{@d)//�-����B�w�H��y��e�|///���}�/=e��,�#Y�;�ui��9w$p�2�����x�bY�l�����[n��L����["����������������~N���D����wV�=�n��G����X�0]�HJJ�>}�������2�K���Dd1e_&{2����Z��[nl���"			Vp2�=LUSS#uuuV[��611�j��ly��R�M��wy��uv��v	^v�������Ld��G�?�;�����=��Pn���@~l��}��k����������BJG�mm���)L����X�0QG

���������g��9Jn�p����@����%�ev�������e1�

6L������'�|R�|3g�Y�p���S{�I~W|P�mn9X�M�n]�;�����"�*40`��Cg�eF'��L7���=����k��uSq��RVY+��R)��|+h��+A��J�s'?�`��T��t&������AUZZj���G�R�������U0��^'��s���7���s�}���w��{�#���;�,��\�{��=6�"�9%��(u�3wEEE��yj�s�snt����[n���Qd�R�y�<���>H'g��� ���e���L�sol�;���\d�����;'�����i�T�_�v�1�;QZ�*���������������C�_�h�����Rm���a��u��������|��K��\���b��D�{��}����I\\��n��?n����C�Z��=N�\8I��/"�s�)�Y"Yf�8%����q_7���������N�������E0���Y���Su���{�<������o�~>8��M���9��Q�~�l��Y����#��]p���^��,�C���w���M�/?�6]���������tK$55U�'>>^��Yd���,�b��L�d�;��D�{��;�#��??,K_��U����bc��YirY�=�{��}��D�x���c�h�����>_��X_���6�s����o�6���q��b��D�{���Bo|�S~������o���T�E �v&6�F'6��^;����1�@0��&g�.d���������/��Q���:`��8���D����Y~[�I����E��;3F���X�0�g� ��Nd�n����q�&b��T�F���r�,[^%�|����1}�"#u�#������'��,1����@P��Y}����0[m�Uhg���G����g�m0�F��w�}r��w��	�0�������+t��+�F����2O�^�XZZ�[��_����[�sn��&�M.���@�u�;Ud�`#�������^~�����W*}.2������9��Rd��Ph���+t+������B����
l�6��qu����97B�lh����J���^k���%?�"U�0����zRg2���"�ja�+�s#r�i v����KW���#m�/Q�m�\��Az� ��7E������O��#�e��)K�M��/�G�<|��boG�V�U�a=UVE:[-8�s#�\�6�6L���k��
�t����J�-�SPP 111���e=�)6���.;;[�Np/`����[�QO=	���d��U���������z�m�G��G�M�����LC~	���c��7����\�j���]}�0Y:�t��K��(4��mm��_k��om�=�n�2MF���{�'33S�Z����V����t��/EI})d��Y�b��y.`�I0�Fx�i 
��!?yb���W�G�v���Rp�8��'A��T��p���}2�����C�x7�W��N�����G�lQAU~��
����3�S��f�6m�n��������3f��������t�m�k�*`���������/m�=�~pi�,����_�V#w�r�l�}T�x7������I2el� �QhDUd�������*��J�=�����[-����P�S��[�h�n��>�*N0s�L�kM���.����54���o����=����.���c���#��}����{�S�CQ����[��\7Z^{�tK���D

��


$&&���s���@>��,/��M����������;!=���LuW��{UH@}|{�uX�^d@=w��i������� ����c�(�����Y0��l�w���I���z�m�w�G�M�s'
�#DO9�=[�tM ��&x���2��O��M��wS����N��2H�](4"������U����3F��c��P-Z���@nn�UT�}�����z�/�KJJt����}n���s����`�
����n���SY���i�����G�M��#z�&�����������U������T����*��%���@D{��
�������o]4R����i������z�}���/E5���^��u��V=�������r�����R{�Q��mv�0��i����G@�)((��3g~Yx��Pc�}+W��W����;;��������.�X>�l��nBZoy��Ir�9����B b�"��^kj3��W���$''�:��
x:���������>���W�	\����V�D������T���T��wsg�Z���|u0?77WV�X�GOPc�}YYY��~����9m��>��E�97��9p�Q�}�<��F�o8�G����#��['H��z����v�g*���%����X�D�O��[��UTTHSS��NOO���X�
8k&b��D���ro/y�%�����9o���Wyy�n�ddd��]��D�L ��e�d������rw���4�����0KUU�444X���T������)�@�>�$�/�K��=z�m=���]�g��qIz$4X�0�g� �D$P��=��F#U�H�������PP})p����,&������z����"			Vp2�=LUSS#uuuV;99Y�6����Y�J����������.��J�I����������'��,1����cWm�,]^�{���6�"��*������Td`����d�����Td ??��$�z���j��z�zn{���t��*P`�[����x@0�x��tU`�~���E��6T��srT�=����r�����6fT/�uv��0������T���#m;gbY2o�u�1�V�\����:�����{-�M�f��CA���s

t�5�>���su���[��x�W	�����Z���U�|�v=�]��	������f���h�@TS��p��}pH>���R��WRd]�2��^z{�u��������|�����O��8�G��Pg�yyy��BP���Y�h�n������������;{Ao�*��@`�n�f�sY�Tm?�G��>c�<u�$9��zg�/i	8��9��Gg��bbb$++�z���������:y�#�A����$cdO� ��2 2=��
����u��_�.?�,E�p&�d�+V�������P�5�����B}�v��";;[��f/b`��%�s����e����ol�#������N���!C�����8�*pVa�����#��������y���iCu��#�����@������{����%����[��e_�Gp.'f����GQQ�n���������������97��x�d����c��b����������$��/B�}�R�8��{�
I�*�_��{�nm��������}z�mcSz��y���}��t+W��3fX�`p/����������sn��k_���o>�e�+�H�n�4E���X1���@T����-��R�M��x��yv�Ui��[���2�c��h�,x�L��>�G�v�W�#�&���]��W8�Lu����B����,999�=�����[�S�	�������d��U���/��w���)��>Q���G0KL�J������:������3u�5���Jg����RJJJ���)..�-������l���r����t���������a"�=L����2��Ot��o]4Rn�1J��)//�-����B�w�H��6�L�Y&LVYY)���V;--M��( �TUUICC��NMM���x�
���t�s��,�-��{�}��Q��#u/���a:r��A���2��^����f����,������������"�sG�L����Z����vJJ�$$$Xm��X�0UMM����Y���dILL���~G��d��*y��z�}��7Bn�,Y���uS�O:Yb�Qh�O,���C�P�����a"��T�}��uS�}�������v������q�8��3�97r�e"�2a2

�t2�����~��y�����]�u������������=�Yf�"�����@rssu���Baa����_�����
	s�H@�	q�&b��T�v������l����n���r��49��~z��X�0��3�%^���E��Vh����s��"�c����u�� ����co�,\���"C�����M��"Y�gEEE��"\�������e�3�|.2p��������)2@{(4��Q�XUU�P��J�����.��?���g�����]u�� ������}2��O����z�m�G��G�M������E�����3e���'���^'fY[}@n[�����V=�]��qr�
r�7N���T��|�n�111�u/��V�\�['���������V�
����X�D�O��[��UTTHSS��NOO���X�
8k&b�����>*?zx�>���=��k����O�=�����uK$##C����%�e��,�D�	�UVVJcc��NKK���8�
����J�vjj����[m�|�=���Czi��yw��������Y�0�g� ��|d�-�E���JAA�����^�������.���9w$ ����������j���HBB����uS���H]]��NNN���D����b�Vy��j�k��	�e��T������{��|����B~bv�=T�5C�h�D:��X�0�Nv��k����t��Q���;���A��L���2;�,�B�	�Qh���)L���y��5��[[t��\�,�<��E�?LG�=�2��iY��"��Sh���2a"\�D�{����m��#�ly��g�^=����[�5m�1��"�t&�������X���\�6L��S������T�[�I�������Of���)c8�ch;S��D���!�D��e�d��8d
��=O��I���x�n=����.r��r����H�`��t����,3:��e�[d@��@��g��`�_��?��KO�6N����X�������%���e[m��O�k�z��dr�{����+�������O�>��P���0'�b�@�?�;n����%~��7l���8t
S��a"�=��?���=�F�<�fj/�>�+�`6�:S��D���#�D0�e�d��8d
��=;u�����e���z�m�w����S�D�?LG�=�2��	YfG�(�)�^���,�l������Y��a�t��G��K�6�#���+=��3���*40`��C��%^�:���:�(��\�{�}���V���,���������|*20el�,�71j�
N�2;Zd�3|\Y'��i��E�'�����Rd?�4S��/T�"w����5L����X�p���}�}�K�Z�7Qr��#]�[~��N�0	w�r&��Y&LVYY)���V���a"�f
��=�WWn��_��=�fg
���Ru/z��a:r��A��Hc���2c�Y�h��E�s�S������|�������	����S���l[�l��g�#F��;������n�*���V{������`�Y�k����I���=��+���/�{p����TF��2�@���� K<

��ED6��D:��X�0�N��&���&�~��9���������Y&LF���C�0�P��K����6��NP������{F�LD�����o:s����@rssu�u!�`�	�2a����/����p�F`��T555RWWg����%11�j#r|�a�,[^%���#�
��(��J�����X�0��3�%^� (�7l���
�����Nr�h����:����'?��i�)2��"Jg��V�\��0�o7I��k|.2p������(2@'Ph��/W��g�G�����{�`������>�����#m=��,�7Q�6�
��*�E\���uK$77W���>O�97`��[�K���l�#�uM��;�q��u}����G@GPh���U�~�A�kM��_��{����/d�ce>�y����e��	�<���	F�O��i��f���'����[�sn��^~g����URVY�G�;��~���������A�������m����W����l �������~���5�#m��y���7�*	qla�d�"���)2����[-���t�m�k�s�sn�;������g��_��H�~xY��w�X>�=�
��cO�<�r��yv�����_�{��#��_��Z����)��``��3g�V�3f��*Z��b��V��z���E}.�sn����`����c)]������^������� �b���m� &&F�N�%����X�D�O��[��UTTHSS��NOO���X�
8k&b�#����������^k��{�&�=[h��>--M����6�t����%����[�v���2��B�	�UVVJcc����|����J�vjj����[m�|�a����\>\�O��-�W��^�!gd��#������=�Y&��������?233e��i�w2��P��-Z���RTTtR!��u����,&������z����"			Vp2�=LUSS#uuuV;99Y�6B�p]�,[^%+��C�����#��&�:�uS�O:Yb�Qh�O,B �h�D:��X�0������$�[�Y�<{���26����@���@����%�e��,&��L�!S����[[s@��� �v�#m��[r�-C�w�#������=�Y&��~���P
rrrt�d+W����,��MIII���s�p!���8p
��a*\��{k�XE>�sT�x7rP7�;+M�2��K��uS�O:Yb�u�o:���}�������.o}�K<V�S���2H��6��" r�C��p��3�H��5����S<��*���u>�t�y��I (4�H�1y��
��,kb���a�"AAA�U�Y=��:B�������P���^���t�s��,��\��p��o_4Rr��{-V�X�[���Z������	��W��[ �s�lMU��}�y��mz��>=���o�����KB<�����l��v���}��W��{ ��������?�|U@��iS��������(@0��9�xa���p��y�s�h���Q�pBsss�999zV��Au������s�%�s���77����d���z��s&���N��&�# (4:�/��.o�g��yv��i��G���H����[�S���A_�:��Q��Z �s�����;����d������d�D��+��������������*��%F�]�&?��TI��G@�Pht������?W��g��?B��K�=	f����A������1���t���Xc�TA_sn�j�~Y�x��U����M�G�7A����#�������������&��G�^8I�>u����v��g111�u/a����JKKu/�233����M�������%2}�t�����B����vzz����Zm��X�0����G?�u5t�5���_�h��y������$..�jNW^^�["��h�,�,�cL�2����`�:�_XX�{m+((���\���<{A���|�������9w8�e�d������h��9&����������*����f�{�^�<��F����)�e�5����X�0�g� ��N��D��e�D���R__o�SRR$!!�jN����jjj����j'''Kbb��F��=� ��WJ���z�}7�%��p��!�X�0��3�%���"����`R�N���#v3)�@&��)L����X��O�Z%~w�����E�\8IF��G���LE��Ll��N�d�����k�����{Aok1�s�Y&LF���C�0�������om�=�n�4E���{`��t����,3:�/�B�	q�&b��T�<U\@P�|q���2wV�LH��Gl�{��|�����~8��P��s��<�|��k�����|.2�i��e�<�AX=��60�S���h�"�ja��%�s�OcS����}*2#?����P��`���G�Tae�����a���Sd�B�8�
�	� 8v���G_��=�.���q�`�m	e��r�J���������t��/w1�6m�n�h�Nk����6�<"����Y{�w�G�M�sN�G�S�/��a�>���U���>�#�
��(�����GW��)(4��P�AKJJ�G[w�������C������1��\�p��j�nLL��Pm������;u�k�k�i��|�����R{��*��C���W�����YfVV�n��yw�{�O_K[�k��`�
�7�^�W�?^&�7�#m�26I�m���S��P1m_&�����k���������w~e�<u�d���G@$��B�uW)����
������[]��Pc������s��<�����\5�}C��.�����t����Z��_�����BQ�j�����<y��-��������W�K�.1�@tqj����W}~���3���u��}����g���.��j��"�.�����p����G@��2>�e�"��-��/��E�x�=1V��v��\7Zzt��� �Ph!�Be��e�pZ����=�:Z�������\���:s��_j��=�����|~�u-���xLv����6���������_��=��\�"�S{����Y�*p�����#:��
��sn�{��*y��J��n��2wV���pqb�	 ���x��}�YSU�G��2��<u�d�q� ="�2�������Y����]�\T��~M���7�vDQQ�n��������6���}�\{ub��
8���V�[\%���'�����{�S�����_��Z��A?��i��5�����iY��l����{-w3�(�k������97��:�(�>�N���6=�������?+���G@89-�Y>�sT~��Zy���z�}7_�be�C���d@H�7��C������[m����(���/T���1PT%`_���� ����GO���\����7���~*���E*���'S�x{���W��2h��;^n�*]��NN�2�����[��,������{k������C���`����	�2�����������G���[�-8]��FED

 ,:s'*E�w�� ���`��JU�
T�����w�������;{��`P~k�je�#���f=�������~|�)��W�����e����9��0K��_��������z�mY���y%ypw="���e�CG����
���p]�����G���&��=��@H��d��1KU�u���Y����
�]���/-Z�[-<��@�R��&��6�#�R�N��6{�d�K��D3'e�*ot�.��_���tJ���Koo����3��?�G����{o<U��f@$q��L�S�z����c��v��F
�.�<N�I�����
dee��QW����D�������H�Z��
{b������ �����_��I������?b$}Xw��R�P@�Q��K~~>�Xy�B~��j��n�5�d��x�J�������uz����6D�Z8I����G@4�i>N����.x	�����~P������}�����������#�_��
������5�U��U�@-(,,�����:�3o���Cqq�n�L�>]�g������&����.���Vp:�>L��G��{�S�l����+����C{��wN���q_�iiig��+//�-����B�w�H��1N�2���������*���`�	����dj�,


�%�[�9���B�����4���������o������(��G�|���r��D=��`��t��] ))I����{�O�
e������e"��/&������z����"			Vp2�=LUSS#uu-����mb"�WYe�,[^%��#���/sg���I�"��b_�3�%^����L�j�6�v������z��tM ����D�r7���l����5}~�����td_��b��W���5/N��,Sm0vPU�����/F[v�Z��a������������u���5<x8���@d<�o9,/����@��x���}��!]<��������w���`2'd����l�;���\d`��������A(4����W�\;��S=��
�����=�4W��@n�u���*�6�?�J+�5��k��
��p�u5��77��������yO*`@���,��1�������C������z�mg�N��W���c��TN��	 4j>?,?yb����������W��=���~���(p��f���h�:�m�K�uX�� �RRR�1�n�
����t���(����{'��[�����s��kt����e�=Q-Z���P	������-m���T\\�["��O�-��***����j���Kl,�|`�>L��G����y��-�x7\8R�;s��u���OKK���8�
8]yy�n�ddd��]��D�����,��v�����v&�l/o����=��:u�nf��i��?��5��oa���7[w�VF����0�����[���_�p�p���a��tUUU�%����[����@!��^���Dh�/&������z����"			Vp2�=LUSS#uuuV;99Y��I�����ty�;����gf�����d��nz��uS�/����B~bv�����Q�d�J��6��Cqum^^�I�y(7�vt3lg7��q(48�Na*�>L��G����*��vH�/}Xyr�$��

�T���,�,�s�5�u�>����`�	�2a����/��s>L��544Xmu��q0
����6����{��^�!�9P�(����3z�eF�h�2��2a"\�D�{����{j�e�+����_���}��Qr�#u��B0��3�%^�	>���Y}s����k�q����0[]k������[�E��*�W��s=������7��]C���P���M5g ���3�����t��}�`�
���=G���e>��U��6�"D1�L�y���r���|.20zDOYr��`

 �T��)�vQA���{��u(?6��7��*
�+���E���_]�N��+���u��=Z��l{�T: �Ek��6�������������g��P=mD��=�t	���	Vm�//���Z=������#�&���^zD�h�2_Cc�,�c����z�w��=�W�;L�-8]����#��(4��s��������k�m?��-Z��k��z���N}��9���[U=�BU T
�h�2����a�����_2Iu�����l��.�sN���;�'O������/��)�e�$�N�# ��,�}���g�>���#y�_����$u��}�4����z� ��8��bbbt�^��Sw�Ww��V!�E���n\��l�|j#i{��������{�W��(�5�������������#�����%����[��566~�oO\\���'jh8qp5>>^�gc�#��8�$����<�{�`��+V������Y&	�������O�>��P	w�H�XN�2]f������6�WUq__�<��c(��b0�'�,s����������5���4�g}�$UUU_�����J���=���F^xk��yw��)rm�p�C0��a���r�����-D"�Lgqr��� ����������h[JJ�$$P�
�����jjj����j�3(���V�i�y�F^,�-/Tf�5X��J�������e����'��,1�(4�'a�������wB���.4`�x����)4`�;�
6L��565���[�����H\l���;B�����p�S��{6�:�S�L�������g/��������\��B0�La:��K�~���K�������.�{�h9�t2�Pa��tl��d����,�A�	q�&b��TN?p�~�AY�J���9�G���5V��J���:H���(4S�O:Yb�Qh�O,B�'����t_�����+[�h9�#���]�O�:O�n���p���@x�;K$���:Rh@��1��E�}�?��tf�s�/�0�s��sa2

�t2�����;����K���z�m���K���%cDO=�P`��tl��d���e�D���X�0��\���V����u�}����"��q���(4S�O:Yb�Qh�O,B�'�

t����BS�N�-��6m�$MMMV{��Qk��c��D�{�}��(�,��{�w��������q_�#G����6���M���&��`s.�EG
��C_��Os�pas.LF���C�0�����=���9p����S�&I����s���a:6�F�L��2a"\�D�{�����}qT��R)�^�W�������U�r�OSPh�"�t&�������X�hO�
�7w�X��+Xnn��E�F]m�������w���:�1X�0�����l�!�o�}�GC�6��[�c?�(����{���9��h;�s-:Zh@Q�c^^���o��w��E~����;�2a2

�t2�����+�[y�/fg
��J��`��t����,�;�L���0��r������!��W����=w������O�C�����'��,1����"��8������EEE��"�7�h����������+����?��Fv���>*3T�	T�RU��NP����`�
D�'^������0L��yn��/��^��s��o]8R�uE�I(4��Qw�Ww�RUE�35W4S���:�����?��_{vv�n���	777W���6��.��[������y�=����n%l��P�w�(��y��e������z�vG���999_��>���`�
D�CG��g������G���[���cev�P=L��,��?��������w��R�t��[���3��Z��B�`��+V����������������"�j���Z_
�k���x*` 2������|*x{��S�����x�������B�	 l�zH�?V&����G�6zxY2o�L�O��efX��R��7�d��z=��S��S'���}���(4��Ra6vk��\�T���b3f����������V���pg0 :<��k�����H[��.�����K��x��K��{8Y&�HP��2��2����i�9��%�&J���z��,p�O+j����+%���wI����#�]�&]��w��@��LAAa������"++�k��>u��E��Vk������m�����"��h��'V��_��#���+��SG���v����-�m�\�H=�s�e�/��U~������I���������x�`�L�������pY�Tno��r������IVaR���4���ALL�*N����T5W`������L=�1��M����())��������q�������P�<��_�U��5���CQQQ��#y�������up���
ijj�L���.���Vp:�>L�������.��W��]�c|jo���T�������M������/Tq��]<J��}����I\\������\�D222t�.�Y"Yf���e"��2a���Jill����UUUICC��NMM���x�
�����y��
�[������k���)Ct�����{F���E��` ����������j���HBB����uS���H]]��NNN���D��*��e������z�����u��
��E4�{ ��'��,1�(4�'a��_;�G��P����k����"��s�E��w"���8t
S��a"�=��s_�,{�R��z�i��.)7�l]$��M�_k�������m���	��z��qI2el?9uT/=x����������,�E�	�Qh���)L����h��/�������%�N���F�}�"
��#��d���,�@�	q�&b��T�r�Z�|h�+�������+sg���A��p�`*�Ig"K�.�-T�N����x����k�
�������?�Rd@Q������u
t"���9�X�s����=d����(�x�w���������_��}E��D����q��` �e�5��2��O}*20.��<2o"E0�iY�����H���/�s�}�_go��3g��8GG>g�g��:���|�W��\�,�;gE@�Pha���xS���X��U�OE��z���������w�o��[]�j �<�$/l�_��Am�#h{�<g�<u�$9=���.>����hPoUS�e�7?�%+�m_�#m�������	2$��N���e�������)����*0���++V���'����,�
�-o}�Kn~p��,�B�x7fdO��7�G��qR0u8_p=:zX_=OP���|�����:�50��s,���N=����D���O�]��GD����,���K��{���FJ���`u@���H�
�"�|~�u-�����Izi�<��r9p���}�������elJ/=���9�o�4nYm�U} �Qh�w�X�<�J���o>�]����w3�,O�9Y�6.I��4�xq��v�&��N���Q�`����N(**�-�3fH~~�����L�L����t������O%%%��;UlP��n��yp����=����]�����/K�#�Ph�\&��Z*��]'{��l����R��w����������6@H��V���o>*�'s,����G���#��p���S�{b��"�
;���K���v���
IJ�%�M���G��2U����\�������M��j�^A(,,����j�^l@=f������_��m����.>{�<u�$9��~z
G�Z*���Q����4}�^��L�}��:u=M(4����<����$��5R����������;'���N �l�6����*e��z�mgd��G�M�q���@�g�3g�X��b�����y����"����Q@�a]����'������zv������;�q�tM��C*��VI����#�<�GZ���#��e���[�w��W�S���2����%����[��6���T�s��..6F�]�&?��T��;A���"�,���C~��j���^����)C$��q�D����,s���V������s����[�SPP�[-|)`�h�"�ja��������@�G�S'��|�|n>!e����q�j����P)0M���yc�y'}���|��V���z��NQ�S��)"�2999��B����X���V�����1}��;'����S���2�����^��{��ti���&]�<sZ��>U0�����3���D��WZZ�[-�M��[�STT�[-��-<����\���m�uD���Z��k�z�}sg���T�7Q�B�q�*9��R��00K��|�T�"����q�~u���E���"�R�a�
�U(l�&�������y��=���.M��������(d�������n����=����.�����k�G����e����P��VXX�{�^@-{1_����L��{����W����#�ML�-O.�$�����������{�
���o���g[}_�������|@��i>N������:���7��ZU��T�U�����������U�Et)..�-������l���d����%66�jN����X����?����U�t���/OK�%sg����z��p_�iiig��+//�-����B�w�H��qd��L����R�6?��DUUU���`�SSS%>>�j�0�{@�a��o>�G��<�����!����#�v���{F�����,Sp}
��v/0�����wT|��T�WQ�����nU���������T��ZB�,&������z����"			Vp2�=LUSS#uuuV;99Y�v0�?�`�,�h�i��/)��1J�����"IG��#o-�#�<c�0�Da��+�
���e��I��}�v������QtYb�Qh�O,������OHg!���8t
S��a"�����_/O�Z%�~�[����F��.N�=���L��[g
w�H��1d��L��B0�La:���_��*2p�p����L�Or������w�I�7�#��d���iY��z���<�L������\�U��E���������3_K��e�D���X�0U�\�=�����-�g�I�]n��&����#@�Ph��7�l�\&���Q�v.�'_�G[���S����k^�~G�J�9�����(:�,1���� ���p��Y���ER�t����9����Wn�E��������������0�:����@�����@������U��3gZ�RZZ�[-Tq�������:>�J���w�}.20k�Pyj�d�@����k��lP��{-2���kX�2?i(4@�k��/n��/�i��r�����������z@�z��*Y�J��y7wV��@��VUH���*8���`�j�~��x���r��n@���=Un��&�8�
���+e��,��m�	5���~}�"KL�*��y�<�K�>�����;m�4��I���uKd�����'������&����.���Vp:�>L��w�������+e��:=�����2��T�6��q>�u���&qq��
f(//�-����B�w�H��1d��L����R[
��s>LTUU%


V;55U����6`
��m������==��J��2el���oLG�=�2��IY��Z������>7Uh�^\`�����-999z��k�����h����G�-���'}.��,&��������N���HBB����uS���H]]�����dILL�����������{�;���2wV���A^����"�?�dsS�T��%��GZ2O�UG}����������g�3��B~b��@&��)L����X���ly��R�M��w�WYH����0�B0n������e�d��8d
���=�q�!��@����H�F��)����������`:r��A��H��B3g��+V�^K����B�kM}��������@g

(�k�B@d��5L����}��b�!��Ce��z����X�uV�\|�`=�`*���c�R��X>z�/R9`�l��<�V��??���W�s�t����A�x���,�0�Na*�>L��w�U���VI��Cz��]ce��4��+���Y(4S������%�e��,&��L�!S������O���6H]}K���9����FKB|='�����3z�e"�u���{������?�b�x&P������o��/=��8
��
��s�Ayi�o���]��s���>�Eh��|����+��|������c���>�8���7���~Gj���w�N�y��7|�AGx�Yb�Ph�O,B ��9&��)L����X��������l���M�$sg���~�V���L��[g
w�H�	D�L��B0�La:����-������w�f��.M�=��`:r��A��H��Be/$`/`R��a���\�h�?��O�[���YSz���w�=@��4�I�����F�m��
���>������c�^�|c���4\��d��Gytl������2�������i���;��"@4Y��
����:�"Q"33S�Z�V���	�^����}y��ER��Of����H����XQ,=����{�����7J�7.�?~A6���@}lW���T�k���u��t�1�<��@� �p0���[�T�}��u��X�U��k���o���2wV����G������)L�������pG�	�UVVJcc����|���Y�tN��s�^
�� ����f��;A��>C&���G`�
���=�Y&"�:�����{"%%%2m�4���t_�����{�}>��%??_rrrt/2�g�S�N�-���l�����#F��gw�uSm��U��������%!!�j��H]�<��M��v���]3}�|o�H������t�Gj�i�i��F�m[{��Z����m���Cd�!���P��6T&$����3������ K<

��ED6��D:��X�0�>�l�}D��R%|�W���{���/�	�(4S�������Y&LF���C�0����T�ZE�}qT��m\j/������z������3z�e"�EZ���|>�Vh�,�������]JJ
��`�=LUSS#uuuV;99Y��7�Z�G�-���>d�������}��~z���{ 5}^.�[VK����;6����p|��mhKq����v"�b����rO�����\q���G�CG�%^��0U�U}s�?��;O��a�xJ��k����*����S�}"E1�L�����d��e>��������B�Y���)--���������-D���Z-?}f��E.�2X��sE����B��+G�Z*~�#��7M�/���K�����"�]���R��|4b��}�-�n���a�Iu�Wdo��>PT6�y�u��[@�����@����e�7J}�1=��7�.��?]�&��#��6��9��������]�:#Z�|����������|g�(���;wD�!:G�6m�n����H�Ztf.���r��O���[��w}z����!�9E�8��j��F���9�����Gf���������w�����E��+O��:X6��(�����~W���]>y���&;{�"uq=����_�~���F ��*���?�%s,��v��F�&�;g����=�C�����b�
=z�������w���^=/�����7�oWl�=�T��o_4R�.�P��EU���d�������u����|�j�*;b����H����e�c�J���z�������;'I����c�?���o��7��gn��?���>�m9�Z���zM����W�p4�����!�����G~C������|[��H��� �+�����~>|�n��B�����<���r�s���`��������O&�W���#���{:������
��:u�/�G����v���;�����#m���,�m�\p&��"Aff�n�(--����V����j�/����=��T���y����5���}+:�%&Fn��&��x�����G'9�$��V������Kw���.�}^"_��]�[i��H����-�u��=�GJe�We����;��,��7����jlo�Q�5��
��b�]V��<�������b��>}�n�VQQ!MMMV;==]bcc�6�t�}��uy�_�W�-��m_�#�%���[g��9��J�S�����4������������HFF�n!��;K$���"�.[��b������V���V����o�^��.d�0Yee�466Zm~����������Xajj����I��{`����������z=��32�J�u�%���������3z�e"��<Pu)))�i���^��k����?�=�t�>���\��g��s�L����Z��[~�KII��~�����a�����k9����,���V�o�����������h�N�#��N��!����Z�@0��U��������n]s��c���H ������!�����:X�'tz��!����2�������S���������'�:�,1��������Z-��Y�s����(O�9�" ,�fX�A}������Z��U��]AA�ny������s����>��<���"�N"�7���@R�W��*;m�����h�"�j�}~�i�Bm�T\���`���\�<�r��En�1J~���`�����P��]�9��B�������������=o����!;{�"���#��7G���~W������NI�Ux���2v�X+�={�\q��~���;�*8��"j>� ��_��j��.��B�\���[�T�}��u�*ke��J������[b����T����W�t�u��Na���L���2�f�������k��zq��S!;������,&������F����0w�����{���j���-���M�&���#th�`:r��A��Hg/�K��b�U  33��9<]�K>i_��9�8�knU����������2a"����a*�;�o��K~���s����J��r��4���[���}�'''Kbb���ulw�4l)�����i�ji��N���c1������U=�o���}�{C�[�n2|�p��RR����O���]�v�?���;A�.�)�j��^(�=tYb�u�o�����I�XZ�s��)����;'Sd����U����M���{�ug/�1�]vv�n��~�/�.���������n�OE����7�J��(��C�������
����GG�(���;5����c���?�u�Y�����b��Efg
�'��D��h>z@6�'G�)����} [�=z�������������$��g��|��&� o�Y ���N>t�l�}jH�t��]F�)��~��3F�8�7n��u�Y����+2���'N�����.����pW�Pd��B�Tn;$�������G�w��S���?M�$Q���*�*��a}��o������������sn�-�v�����;���#mK�]���(Y��D2����B���B�P���z{1O�5�Z_
� 8�n:*��q���=�G��/Q��{���Yiz��i�����������������+~w�)zB�K���>}e������G�l�)��J�F�&%i��O�^,��M��nC���+�
��N;��]��+��Y�fYUq�^�zI�.;B=~�x�����rp/>�N=O=_�D:

���w�����O+���&����N����G�O�WU5�����������[�l�u�e#o0�<y�^��@���z�mS���%�&��=����I�!�%z���z��T�-��W�	\����V�Pd |~�z�<�|�l���G����������):
�Y��#����#o-���+{����K��C�����K������#��
�MIg��C/�w��/E����F^i��#U�c�����O�>���&_��We���r����\ �'O�Q�FI�����������-��W�S���6����CUn��C������r������T����8c�<8w��
cc1NfJ�����[������-��\�R�<����|�vY���r�p��g�3L~�����[��<N�2��|U����a}�<Up@�w�O=�XG���������
[������7����%���kO���3�8B���r������?���^)��/����/G���4T�K���+[��+�z�&��'�'_/o�Y �%� ke��>c�pB��2x�u�&����'�y��'�\s�\z��2e�=z�$%�spQ����@�~�����������&@��l�iU����U���C�,�X�[�G�xw������[��#��L�2�����C��9�����[�sn���W�d�+�����Yi2��T��<��<��;[e���duU��n��$yj�$�y�`=��X�iX[$��xDj����������o�����O^������
�]����R��|4b���#y7�&�t��R�t���6L�c�{Ye�SO=U�N�*�_~���=[�=�\�����C%>>^_����
\|����o~S���
���z��j\�_]D

 �T�M�
 R�?� ��\zi��?�G��z�py��I2!��NdB�������?�����`m�]���T�Zt���}�`�
�:�$�>�N���6=��^����?8Mfg
�#��}�@k;��Y9�S9�@�7s.O��������p���q�'r���r���%��\�-�X�x�]�[i��P����-j��M�&I����2��������W����dg�t��������{KJJ��y���>����N.��"9��3$99Yz�����L]�t�=zXE�[��+!�6�f��~�[�,^%o}�K�x7l@7���ir��)z8���LUX@=����;��������B��e��C���>�����#m=��<|��f��}�@ko|�S�<��O9�2.��,��\3}�����g�������/��_~W���YR������������[��-�����{����������1f���|[��@��/�+�'!!A�
&&L������W^)�]v�u�1c�X���WL�q�
���v����T5WU���������i���LS\\�[b������B����vzz����Zm��X�0�>8��e�+����>�#��d�`�;+M���l��>--M����6�t����%����[�v���2;��Y���+����q��E^?G�q��8AII��_��c��999���#Y&LVYY)���V���a���*ihh�����o�SD���*<Z���k8�G�v�����3$>���U�?�7�#��d���}��L����Z��������:8�Nt�h��7�����#������K�up�����k����[|bb��F�k���-e��e�4m]m�=V������$��nCd_���C��x�h\/���Q���{���	=�Ig"K<

��E�q����M�@[�a"��T�}��ux�Y�O�-���;�������[g���I��������������Y��B.�
t���������m�H@�	�Qh���)L	�x{���o5��������KSt��
���=�Yf�rr���!���8p
���4���#��W��{�������f������\��v�|YX�����m��{<�M(�����CZ���Z��=[��%rn�F>�Ld����j��;�Q�0@(��o��_O�����*.����)2�aL�2�V\��X�w�������@�{�O>Xp�)Q�}�@�'_��{�]�s��ic��=�$������6�5=(
�����J����7�|���l9��{���/�*2P�Kv�-����#�!���]JSo�5C/��}&����*�;d�?~��s�92{�l����.S�N�SO=UQE�.��R
~��E���D�k�@�r.L���a*�>L���5�����*Y����.!�����&�N�GJ���;��$T�u&��L�2g��ipQ�u������i��R8�������	��Lu��$���*��&jll��g
�{����'�����M������[���M�������#�s�`�`�.�����7�B����B�����G+�e"X��	qgw��u'X]U+��W��-��
��3^�1�������;��555RW�������i�Fi�Rv��Z������7���v,&V�u*�����o�u,G�����NRR��P���O�����Lg"K<

��E�1�m��C�0k&b�w��on�_�������N�'s�H������`*mgbsnt29�\�r�dee�^�a�����B��	���a�����rG����Vv���L��
�����[��"�h�6:�M��<�����L�2a"\�D�{D���c���p�����Id��T9�g;�a

���C{O8�Vh>�vA���d�!�C����u�����TQWq'��e_�3�%^�������<���"s.O��nK�`,u�_�wWZZ�[@t�`C�<��^�����U��G� J��8"9O����@l��we�,��I�EA
���m���Kr��������}������X��R6�wR������g�l8M�3�jyk�m�2��R6t�l�7)$ET����i��f��c��Y�C�Rc�}�$0KL3��B�����M��;Q�P9&���0k&b�w����e��������v�O�mU*=��A8�����4�J�����]����Y���W�rrrt�s�O8���Y���Su0��M���9��Q�~�l��Y����#�]�	����on���?|q���L��UUU��Hjj�nN||�n���2�Sg�4��e�D��&b�#���jo`�4����32�Z{�w�#��fb�������u�4n)��-��qs�Hc�kkwLbd��R{�����w����������o_������~����8�2��,1�(4�'a���v�m��C�0k&b��g��:���?W��#����#����t��B0��3�97z��e�7'��1C
u�s�

$77W�Z����;�e�d���_���|�H�khh�����`�P}���
��;u����F�_�{@��oLG�=�2���Y&��,&��5L��G4�{�A�B���=����%7\8R�N��5L�����c���	l)k).�����m;>��$��
�}�Ct��!�,�3�`����IE���\�|�����~�����Y:k�;d��U>H�C��@��Q�g������3������Jg6*�V�\����������2����U��6�"�1��	�+�x��Y���EF��)�6�c��F�_��.|Pj�=��?g�]9���R�i�IE�c����R1 S>y��2W�M��|:���t�Uh �E�
�(��z�L�:U���
�={��s�92n�82d�o��B����&W���G����
����r�h��1�3;k�<u�$9=��8���+VX����z�������u������\����3t�d������}2��2Y]U�G�vFF_Y2o��K%���L8UC�1Y��
������`����s�����x2@�i����������"�_,��\�t�-��4n�D��5��E�w"��M�O�^,%i�������#��
2eg�4��������{KZZ�|��_���\{��r���g�!�����G}%��B����j���B�@�M�9V�}aU*����S�28)Q~���d��4=�6'e���/**�������6_^u��������'�������\r�\#_�����]:e���<N�zq��<����|�~����'��{���\{o�z���5����O^�C�= �������9�����H����X�}����>�����~�y������1���o�����>��PB��2x�v�*��
�	&�y��'W_}�\v�e2e�=z����__	��|�n�111�u/�o\���#������NG7������M���S_kVV��V!~G?���kc��&YE����_s0����b��>}�n�VQQ!MM-�����%66�jN����X��;�,�^��WWn�#��y�`���T���k������g��+//�-����B�w�H��qN�2���}�)�sME}�����w2���]��67�g���b0�'�L����R�6?��DUUU���r����T����3����g^������w?�,E�q�p�B�`:r��A��L����!�����������]JJ�$$$Xm��X��T���F^x���O������Yi���o{kjj����j�;�'&&Zm��L[��u��qK���ji:�h8�h>�G��d
]���P��u���6�z[�C�7t`P���$����~:�|����B~bv�:���]��������^��ol�H������*`���h�"�����@�
q��b��D���>*�'��WJ��#z��^���?"��@=�HG���@�������e��(��J���N�
��W< �s�Y&LF���C�0] ��I���U��H���H�
r�D���������3z�eF'��e"t�2a"\�D�{D���e�+������N�;+Uf|u��
�`"�����-�6����������]m�������8�*0p q�~O����������l�G>�Ld���E� ����rm��eu��V=����_��Ir�Z�s��sN O�9�"�H�@�:t�N�}j��{���*�T�s�����}����<�s���)/m�@0�@���������"�����y(2D�?o�[�|�s����K��N������|x�������'���n����+��F-���}������%;zeH��s�����72~,�)��5�/��}'����*�3t�P?~��{��r��W���_.���2f�0`E���R�[�[�����[-��^UQ��Pw��A�����6���T�s��..6F�]�&?����j��\���=�Sw8S���#�z��=�T��������y��V�>��9m	���>���+�����Y{�&�%�&��=��H�m�QY�����_��H�n�"U~���dHRW=����&u���������]%{�����X�?-
����-J�u��=�GJe�W���_�wN��z��S�S�[RR�u��)S����^*W_}��w�y2q�D>|�t����D��fu�>S�x	���4UZZ�{����x��V(�"js�������f����^������[���]0����b��>}�n�VQQ!MMMV;==]bcc�6�t�}��u/��[[�����^���Wn��&#u�#�6��>--M����6�t��'*)�?4���%�ev�����KO�����
�����<r��E~}���;�2a���Jill����UUUICC��NMM����ob"Ig����.K_��=�fg
���Ru?�
���=�Yft2a_&��,&������z����b�yp:�=����;d��*9R��W�=�S{[�_g����H]]��NNN��Dnf���u��.i�\&M[�H������L���~��$��]�Jm�����`�=��=zH�������"�q�,���D�x���������������a���m-s�p"���8t
S��a"�����Gd��J�p�>=��\�,�<��!ZQh�"�v�pg�d�p��t\TTtR���B%;;�S�F]����#y�P"���(4�q����������J�v��Nm2V��H��0�g� ���,&��5L��G�8�h�~o~�K����G�wg������L����*&�
��M[�?�l��;Y]\����nC�,.��%��n��q�b���*(��v��]���|�����~��WoE���*�Nm�m��K�;�.�����s��Y���ENK�%���t�x����������������������v��s 2<�(?}f�OEzu�����4�������~�s�����%��q+2 |��m�������I���/{��,�}�F9��b�/+������X��}�T�;S>v����Cy���G#fIe�)�E�������u����>[.����7�a��;���e����(:H���ju����������;_6��
���w�r	��@���_'?��zy������[U��/!��>QN�S��f����������#m=��,�7Q��M�#"���U���^'������>u�<u�$9#��5�����#9���r�9����e�C����r�h�s��i���&��C	I���8Y;�y/�[�������d���d{�S�p|��`RET��I�&����o�����"��@�����Qh�uw��*0����Q���������sn ��p��Y�J��d��.eHK���]��GD;�.x�Lj>?�G�v�����yd��nz@�)����\%��l�#�%����~�T�we��v���"Y������r��|����e��L��go�#o<"�k��c�wX���u��=�d��L�p�UR4�6)I��|:�b��o���:��.�bcce��Ar�i�Y7����+d��Y�����+����8}5��B@'������������[�[�r�ny���k���Qx�\n���]1m�<}�d*�������^�����}���f��8�����76�K��r�!=��y��SwN����L�u����}9��3r�����B���l9��"9���i����$F�u*�����a���i���S��G#������G���v��
�>}�HZZ�|��_��/�X���Z���d���2j�(��������NH�g11��a�v���zii����233z��=RTT$���z��5�*F.%%%�~n�����KNN��y���������X�D�O��[��UTTHSS��NOO�*�&`��DN_����#K�W��=uz���}e��T�6�?"8���W�.�aa���r�����-D�pg�d����2%��LD�L����R[���s>LTUU%


V;55U����6`�������U�imy�%����I��aI���
Z�,���M��I�i��L��v�@K����N��n�-P�	!��:N�x�/�-������:�y��]��=���s%�����:�����}��F��M�Kw-���f�Ql��R��������Z&��LRQCCFFF���y��������=����A����8|�O�Z�.	�Q������a���,��;���Z�(�3����`k9���j{{�X���Pj�,��Q�~�wm���N(**Baa!����7��>��XK=
��0x��-
�[�n�G����p���|\��|������}.�y�X��6����IU���D�?x���i����p�,-d '��Q�HU,h'&N���^���b-�T��R����z
�����L-����G&g�K���[���#D���Hu�{��2�k�J�e�����T�qO���{�������/]\�n���YY�Hx1h�T���w��Z��8����1V]�(�m���u�GR�������<&�	�������>��XK�d�'����P������(~�[�����a�C�3R���.����"""�$�2�������D�	_z��_!+�����V1d����h�eQ,���?���?[�w��'n���|fy�B��B��X���~}��	�w���?��W��}�!����_��������xe��x��N�.��s�#2������^v�ex�{��{��G�_z��ZxC��f�A1�b6�������E}��<2��+��WW�=���G����"��L""""��z��N<���u
�#����Y��W���!��k�DDDDv��g���9�-�LmQEyh%��n�<BD��l�EZ�AX_{���,��������?�3,����������h�]�S�����{���/a���������i����)==eeeX�r%���|��-������kW�/**��$"�PHr��6�!))I���)����;'M����ekf�m��+��B����p�g��i�n���?���=.w���-[��^x�^{�5�����Huuu���Z���:�Nk%:�}RQ,�{����F���i0��"5e���-��sgq��;�t:������W���R��}UURRR�6Q�����-h_�Pb�v-����S��I��Z&����6�Mk�}>������Z�Z[\i%55Uk���5��W������t>rc>��J�#�?�@�c�3~���X��p`-�T�������=o�<���im�D�qO�`�:������������uM9>s�<�����FX,�-��.4%����z�����,�����.ocKNCf	z3J��Y��������[#�����-//O�B�'k�����q���s'���X�f�@d��M*�bkR�>�(V���F�����'��k�G�T�e��ez�[f����Z+~����������c^I�<B�a������Z"k�3�J-�"��LR�Hu\dJ��~
��A'^9�����������Y�G��;�T��g�`-3~��I��Z&���IE��'�Z�@k��<2���<pG._��G��A������h>�:��~�[z1��P��g�j�H���AQQ�* ���B�����Zb�1h @�3���^H���A��]���T &�z~��	��PE��L�8	�T����&��-��M���rq��\����7���X�_"���s=>G�HU����uH%��������v-1�~�R��I�����2
�����T'^=���n�:�>�NFL8��}��l^���Dj�@��D���Zf|b-����LR\��8�)?�s#�kw��Mo�����2���1
P��[ak9���G����7�N�Ef	z3���'G�g����( �����V��c}21��z����=�D:h���e��t������2�""""�`�wX��o����}���7h��!Lcnq
�������`I��'�ND�_��/k(�897>�T�����\R�Hu\dJ�{~O
�z�������,��������	%� �q"o�`-3>��I��Z&���IE�45�&<��Y�4�w���L���_���O�Ao��M��@,�|��c��1�$z3K�'�]����_��*���[�������Zb�%�=QD��MDDDD�v�w��wF!B��20���/�����2@DDD1��L""""�����s��s�~����;>��!DDDD3�Z&��o_m�C�?�w�����+�T�Q�s��Z�����3_F��������?`x�oFC�����'f_�}�>�	*���YW�#wqDB���QQQ��/v����w�}76o��5k�����!DDq$�����0�bf|�s�9��w/���J��/%x&���}��;�e����c�wr���e�(�555�n�k���J�t:�M��8�IE��'L���'e���,���s�3��[���E������Eh^��T!�n�!�nj�Z^t�*`�I�Z&E�F*�����f��UUU|�O����IUO�����"{S��-�p����G�8�;�T�+���2��j�L�������%����w��^�G����
6��sGk��"^��T�qO�j�4������<2�/�>w\Y&{�����Ek��;���Z�(���`k=[�Q�Z���qJ�2f$%�%��,Eo�l���V]��52�������f0PTT���,y+Q�b}21��z��0h`��p0�}�[�?/���,h�����F���7.�&Up����1�z����	��L��w@�2�wT!E7������$R	����s��J�L�,�2Ie �q�)��jsb��k���]����R����E�jU�<B�X�;�T��g�`-3>�V�W��,���\F1�r����B|�x����m[@������LR\��8����yO<[��!�LmeU��
Ue��H�a�E�����	xo��7gR�;T���@�^�6�Z o������`���zyQ|a}21��z�rOV��EQ��S4���|��sQby���C�����~��������,��wV3d����bk�DDDDD3�t��/>~����y%Yx��� """�!�j��!3!�q�&�!�#�����sEK��
��U-���3~�|�=��+c:d�(���`y��1��7����0���0��!�_����OJ3�-N���7���}	o��5�7�-oiDB2331g�\t�E��i���.�|�������`��)�A1"��C$���v��![� e8����p��������F�y�Y�����{�k���_]��.*������bk�DDDDD�{��?v������-7���Va��y�����fB�Z���;�
|����+�y�={���#)��sE��#]��w��+�:����@���
|��Jy�H=��n�����O����q��������ay��w�cD�����8S��*��W>���>�#�7��p	�2K�e���C\���K�.���q�m���;��UW]������������DD�

P�l��M����Ld��?����w]$G�#��&"""����D�l����\�g��HDDD���L""""��<��
�~zf�<2�;�,��>��:y�����f*�k�"d`�W��
*��w��%{nW\q�v�7@<v*�<7Q�9������~~
]}#����wE)~���X� _!R���������=z;zw���g���O�Z��!�g��I�GJo���O`���v������U��2���'//���Guu5�-[�K/�W_}5.��b��;99&""
P�b�w�T$�r���-[����?�����=�G8�MDDDD���F�
����d@DDDJ�e��������7���Z�/�>_�����(X�\���ys�!��9&
��0���sE��3}��w��s{��������}t	��
�a�;Q�9�:0r�%�����$z�y9���e'F�<{w�R����5���������_������������iy�����@yy9V�Z�k��w�y'n���_�%%%�����$""��(�|���-��"��u�;v��;]X<7���&�N%��&"""��f�9P�6({�!�o�;e����(���IDDDD4��
_��I<�g����iI��M�x��y�����B%�j����;�q��s�m��M�5�m������9<�yn"""�H��������!ydj�.)����b\��P!J lM�1����mE����������nu�l����dte�E}�:�s;^]�y�^�i�[v���UG�N�0<���PTT���c��
���[����W_}5V�X���2����{M�A����|�b�Hf�m�Fg&[�[�l�"[n�@�b�����>U��sQ|�1Ye+����""""�]�eM�t����Q�?�#�L��8_�]����HDDDJ�X��/���=�Q�(���G��{�n�r��G9�+��B����8�p�����(������c���M����S�������_���y�(��{�1r�y��_������k���O`��������mG�l4�/����w�G�����V�]�-��s���-�>����7o.��R��=��=����E_���("����~=M�� .�/�b�(,�>����yn""""�?����'l�W~pT	�H��L""""���~�[h���
gW�.���,B�>�Wo""""RQ��2E������������'��c���\8�MDDDn��z>���8R�'�Lm�������u���8d5�Z����_����=�>��0������9%��P;�j�� ^\�0���0����+1�^,O>iii(++��+p��W��n��Vl���/FQQ�����(T4@cDb�(��Sx�$����N&��&"""�����^<�l=����6�����\�E�^9��EDDDDDDDDD������[?�����G&w��9����H�q�#F�_t:�!���i�&�
���{ekb�<7Q(u�Z��������<2�O�w.����Q^�!����z���G�����������0��ga~�q��|��>�+QoX�w�o�k���_|�]mq�'�����{5X�p!��_��������;q�5�`��U(//Gzz��'Qx0h�(���H��'J�Etq\�'�Bz8�MDDDD�����X7��_gp��;����8�wo;�����H0�2h���������(^=��:��O
�7�/���|�\�#""""��'`@�_V0���B��s���o��g��0��h�<2���9x��U���9�Q�r��a=��W�����s-����#,{{
L�Eh�_�%���y�K���w�v��8������l������s����/���_�{���7o��5k0�|����{EN�3R+JDR���}>�D���k����."����n�k���j�t:�M��8�IE3�f�o���F�;�
���W��X��U�e�����=��y����*���hm�DW[[+[��E�d��]�k��e��2Ie�����lZ���IEg����j��b�Xj*�)������L-����G&W�����.����Hm��:�=�k�����+��R��]hi�)���[�n�=�������7m��E��{�X�Z&����###Z{��yHKK��D���^�#���z<��9ydzw_[�O�2O�Kcc#,�����o^->>��O��z���������-n���e��7���/E_f)l���9'j��EEE(,,��YYY�����'U�>��XK�d�'"""""�8�;`���?;���~?��W�x��N�B����Cj�r�lQ�8~�_|��_!+�����V]2@DDDDDDDDD����|����;d��8�����
�����H�_1���0���0~�
���^�a;,�������`���n������>�CsnG}�:tg��H��^����v�Z�|������p�u����.��9s�2@DD4
��s=<��
[tw��|��g���ykt�f�[��-"""""""""�/�u_|�(�������p�,���+1[�� �������:�6?�{�M������k�p�E���8���/��6�}�����|	�����7arf�-9N��o��^\�%�Yy/jf]���%J��g
PQQ���Wkaw�}7n��&-d@�
0�������������H��!���|���������;�C~\M��9��%%��x?���+��2W�����(Zv�����7#))��M����W�3p��;��
�<7M��/6a��O���>rc���P��������W\![��sQ���8g������E:'Lx��w��W[����e��k�/���^��T.3��r������a~�Q���S���:����������{;��S��E���`�����8Rz�����Q"�>:��f����K����{��p�����+���e�0{�l����{��$��l���E_|
�����^�-��k��-��VWW���������D*��'y�}S�
-�������Aykh,�����zlXn@UY6z�����(�(��<"`���W�����WUU�O�����-`��E�E�.��D�2�C,���u��M��o��]�dozb�������/�#���m[@�h�y�h`-�TV__�����>�Tt��YX�V�=�|����=����>���:/{S��}�p����71�He��:�=�k��D�P,����g���@���?���=.w���-[��^x��k�s���-��&����=�~9��&�D�q��;Fp�aG,h�r��U�b��t������4y��������7���2p�U����3'�c?�v�t���Sl�H�<����Z�I��,A_F)�3Ka����4��5r���������l���j�x�qO�����p�����'{)�c-18al����wc��M1Wh����\R[��8�I5G�����u8�`��>���U��X�L��+
Q^�!��?��/>~T�#>�~bz���X>���HU�p���]Kd-3�D���y��i���7l�wr�?��p�sGk��2
������EG�0v<s��N?��0[�[���r����5@*��'���?X�$o�8/�A��]�,++�-"""��?��C�_�h�%��uM���������y�����;7�����Q�%��i���u���,��i�fN���!�\[�;\�1njx��-E�����2��_�EEE�G��Zb�1h @����U�&���V�N�%q�5��c�Tp���7��`�	#:{-�hh�]���e�_n@Q��)���y�O?�${~�&�X���*q���G�
��8�61E���Zfh�s-s���?_�������I��?a���}�����;�3�y�ha-�T��R�R<x�tv<S����'"_��[�[C��uF�He��:�=�k��!��e���H
��y��E8�4@DDy�V���h8��#�0W��A;�y`KSp�y(3p.�N���
��N#��R][��(o����@��um#)�����=E���D��HOO���A��Zb�1h @��������V�N�%q�5��c����t7b��n����.B!E�4,�a�9��-�8~�O��5M&y�K*s����a��<y�� Uq�mb������L����t�]}�����N��*� ��
�<w4��I*c����L)��e�9�������w��<|g�����R�?��u���Zf�K�y��,�Z���e�(������w�3g���8�c�ov��/��^0�{������}�k�#{jjmm���;p���ii����]
p����8l-���8%oq����^* ��
�-�����-�^�Gaa������T%j��Fj|��%���A�p��X�<J�����".�&Uq�S��c�8��7��`��g���3R�n�����B��L���?�~�	���,{����
|��J�#
�HU�p��897��{-�����D���	&3��k� ���Tc1���&�2Ie �q�)���������"{S��-���k�e�|
��8�Iu�{��2��
�2g40��xL�s��2IE


��������w���� .�������9����������cUu�<����FX,�=w�\^���a�&`k9
{�8
�P��0���/���%��,�I���-�E�����1��*�'k���,�Da'\CY�L���""""�u=&+���_�$������Z��pWHByix������ex����k�-�U�$d@��=�
��P�}�0q\�.���"""J�P�����?��I��>���MLz�%�o�6m���m��M��|���s����o��������d|�#�g2@DDDD����H��o�lM���6l������DDD����C�l��w���i���
�qe)���j���l����?|�����]0���0���w?��3o�y�/�����V��4�����5��������"m�����q�-������r�J���q!=Q���j�.fn��\Q��.����zn����w��;���(�19�T�����8�)��w���=x���O�%��BYa�-7`�k[� �����'M#��������f�Q�x����NI%L�ML�
X�J�Z��� ������[����W��}~�W�cr�������Z&����6�Mk�}>��W��X�xn;�9��-�����da�}���|�R�5@*��'���?X��_��������-'2�:�?u�p�;�X�$����"���{��&<�r�����3yk0�q���x�������}�4������=�����(�-����2(oL���^����g��?�X�9999(,,����NN���@p���X�LL�%���A8s���D�^��Lt�L����X�&q�5��c��AC�����=8�h�GCCL��V��2W%J< U�����]Kd-s����)�����km1a5�A����L<�=�D�1���6�2Ie �q�)�Q�!f��������������� �q���X����%F���g��]��2�5���L���a��)�y�hc-�T���"�������Gd/��,)��h1r2���/�\��N��r�������(o�SrG�2\����
�g��"X@�322��4S U�>��XK=��PD�"�7Q���+�r}���I���c""""�h�i���7���=�O��;Z;T!�f���5Y���x�������2d����(���)�l19Ul�KCfb��M�8���W8�MDDD��g����O��+d��+K��O,
:d��������2�O�P�� �[������(��@�pZT������FN�������O�������a�O����]�2�q����o�_�?��|���u������H��^�����n�:�|�����;q������.Byy9C���"�A���m���������I��|'��TX""""�Hz��O��Y|�_��C���g^nA}���58���s������R|���|I6���KDDD)��2w��-[n����,;�
6����yn""""�����������n��=""""�U��9=���������|�3Y�k8�MDDD���F�
����d@�������.���
�����V���M�������0�7�5{N����}/.�T��S��FG�b�S����';;���X�z5���z|���M7��5k����
��DDDI���.5w:�������B��I#�}���A|��������kX����������\���
|��2�2�����(�T������%����w�r0�}��s�j�l��v��i�G&'�p��S�p������������n��Y���MW#��sQ�������&#�o�;e����
��]z������`������1���0���vkN_��*����������h��F�ly��III�����l�2\y����������O��"���5:/�EDD4@1�w��dW���~DDDDD3%������gjq��7�?=�?�����y��KMI�Ua�}����a�'����%0���{Q�K�Z����e�m�+�Q|8�2�/=~����-�������K��%
��en��E��<���"��7@��k�lM,��&""��U�>$[�e4?��b��:���0��?`��W��o�����b����_���
��n�{�z�����/���;PW�]��`����W������kq�M7�����u�]���W���YYY��DDDk4@1��D�`�w�����&2`������[�8�[��
|��5x��N��_�W{��Y�]�?��:|�#�q������DV"""�x�h������:,Q�!�_z�:���|��"<��JT��DO"""�D�y���={d�M�B�UU����m�����|3�p�����������f<��<����hx����E�����;������'���[���g0����[�.Z��pr�uxc��xa�����^�������J-�g	 �.��"-L������7���/�,�^�@[""�x�����[���O����������|u���O�:�?=�;����_����v�fw�{�\Q~nY_�oz��~9�����rU!RtI�DDDDkT�eF�jZ����P"""�1�}�UQ�X�����4�>��)�>BDDDo8/��~(�����������sQ�r8�����w>��������	'L��W��*[���X���k?���F�������c��D��@{�0�m��s��W>�=U��������}��@Rx��&''���K�.���q�m����o����/_���g#5�c���(���b���{��Q��u
��m�Wx�|� ��:8i����8�����
������wVc�����D�eN2��i��G~w?�S��M�KwU����+{DDDD�8/sjbq�X��;v��cD�8.�h@8�MDDD���������7��$n�����S'��}����{�9��_��\.��E������,��[�{�n�}L�x��?Dg�4�������@������:s�`�e���O~~>���p�e�i�	����p�
����1w�\����{Q�Hr�A��;��4A��B�x?w�`�e���������_�G����^�-��k��-��VWW�����������6Q����`����c=�������W�ec�2=��(����Q9�IE��^|a������]mm�l�-�-�w��%��9s��2'�j����;�u�V������z.�y�X�]�0�Tb�Ze��	)�f���O�s'z�F��A;~��'[F���d�������<M	/�He���p(,,D^^��Q0&���Zb���x���%r-�"��2IE


q��7o��"���(�8�C�T��4�@�'��(����L �4>����G�a���������Z;��F�Z���r�����Q8����R���Y����Y������9��gk��gA����'�%����XK=
��p���$���|�����X�&q�)��c�fB��8��m
C�hh,���u�X���y%Y�hhq���4@�bA;1E���Z���P��I����s3qE�+��R��?�<w,��e����Q��;����&t��k*S�.I��6����,��/�"
k��K�Z&E�e�����T�q?36�k�8p�����wc��7T�c�+e���s�������F�]����t����d��%%[�-R��.O��g�����R�c����2k���o�)b6l� [nb��(L�	��|��}�v�r��.f��!""""u>��<W����������_i	Y��������/��R<��*�{����Q�$z-s�!DDDD{��w�����������+`�QI�Z&Q,8o��Oot��>�[��
�������2���n�2�lQ�8��c��n��(LO�o�G��>��?}�5�h/�����;��xy�Cx��.�.��sD$d //O���K������s��}q�!DDD���dTC@�v��&�
���=��>�o1[������K*���IU���js��d��4}n��HM�����'z����?nt��)���~�A+��_^���J�G#���T�=�����������sS�k��e'k�b���U��o���m�;d����>�����c�w-s����E������������|K�inn��f����K!���[�������h�k�T��O�;{��l������IM��ww������y�j��I*���IE�S�i4�@�Q�N5
���#�W����mPqo�B����+���*e�|����l��`k�Qm�0�k���\�e��/���wm����������EEE0Z;##C�J4�qO�8/31��zap&��*�N��~��	�9,h�����T���N6��E�o�0����-U�e��ez�[f����K?�9��1w���?h�=6B!=5�.���>+=zc���T��R����s�[��2'�{���]�v��|�������������P�;��I*���]`����"���j�jm����(v��4^z���M�k�-�u��^��5@*��'���?X��o�V���c-�T���"���-�P��'����������<k���v�+��i�z�j�LZ[#��VxIe.{x���D�Yp-B�PO�@�Q�����T�g��7�D��2J1�����A"P�,���/o!��HU�O&&�C/2�%�D�Z������MwA���l"""��x��&�������[&�qq����0LC6�x�<��'q�����_��+�:C2�������7>���u��/�u�G5d������+�j��
�'&j������{�p�����(Qt�X�����2PV��GZ��""""
?Q��L"""��u��������N�����o<]�?�?���N�
<xG~�w���_^�O�<w4d@��m�dK
`��Dww>���j���m��9~�U���M���[1���C��/���������?��=�7+�����p.wQDB�������\r	���z�s�=�{�5k�h�� ""�`%9��]�����o�>�*S�=��7o�/� {��DXJ,L�%����*���q�l?��C��)�~i������P>����;���'�8Xc�GC�� ��n��-�������T�=�y�SR	�s��8�������;wj�������9����sGk�����z�l6�����"^��B���}���Zt�M?���E�r�Br�%:�He��:�=�k����2)X�$����"U����6��k�m�GC�0/MX�D��K
��6���g^n�BN���o�>~S%���B�h2�G���v��g�mj����v�������e���R������.D�����EEE�>++K�J4s����X,Z[W���km�D��dbb-1�4 ������H�i�&&���mR���8�cC��ILU�n�4���=���#�}�hhT���z.����U��D�=��A�*�'��'�j��|����������w��c���K8�m�e��4@��"S
���?������-�K�����}|
��8�Iu�{��2��y�4�2IE �2�-V�;X���bG��G��pN�.�c��,�7�y����J��A�Z���c���r��8�t
����9�@>*r�z�,��

"�w 50h�T��dbb-1�4 B����6���NIU��'
�_|���y��g����W��������q�'�)����`�
+ZA?�p���4@�bA;1qr.E���|�s�^�{����b8�M�e��4@��"S
�O������*{S��-���k�e/6�5@*��'���?X�$"o�e��4@*J�q��=�5F<i���~k�����K
�f�k��Qb���O\J\�_��O��O�f?w�V*���������i��Pw�@�v<����.0�Q�0h�T��dbb-1�4 B����6���NIU�����Gf��;J���z�<wv�-�c�q#�
���!�/�c�r*]N���'1h�T��vb��\�&����,����;�u�V������]�y�hb-�T��R��L�X���Z��n�<2���dl�w!���H�|
��8�Iu�{��2��k��"
��m��)y�N����S���5K�X�D���&�[BO�������{��8��.�����[����\yT=��^�Z��C��6aD�5(��^����L�����y�����}vv���(�4@�b}21��z!QlaA�T�E��*�����+���m�����D�]��@.X�����Q��'1h�T��vb��\�&��;v�V`6l��+��B�.��g�E���m����{�A�?�0����2Ie �q�)��g���i�n�G&7�$_�o���DU�He��:�=�k�D���LR�HE�>��G�8p���5F-`�����;�W���,���V�=&+���s�`�MEjJ�Bb���$���`k�Ga�j�IZ�@_F)�3K��j���#"'??4P�0@K4@�b}21��z!QlaA�T�E��*���9�h��|������|���k��>?'�&�q���4@�bA;1qr.E��U��!
�l�"{��w/���J����={&
.��sGk��2
�������D�20`v������~��E�����!_�2�R�����L"��Z&��A��x�m]f-T���^-` �RtIX��, ��G��a���E�
����,��c(M��,��2K����'�)33S�l"T��7(�1h�T��dbb-1�4 �����}���^tLuU-Jl,h�����T��=O?��g^n����HK�����M��d�%�8��'1h�T��vb�����J-3RA�xN�o�>��w��7b��m�������Z&��A�:.2�@<��?x�^�������������������T��g�`-3�p^&Ek��"
���e����B�4��mP
�Y�t�p�����-1N�;T�U����p[a�e\(��1#)��A����|A�@QQrrr��D��A�*�'k�����q��s�Nl��U��c�����X�&q�)��c?z>���a)��o��������
�
X�L������qO*b���������G�Z�D��3����3����!�6m
��9���$�2Ie �q�)������{�doj�Q���(���������T��g�`-3�p^&Ek��"
��bu�Y�8x��5F-`�hr��%��X��@m�,Go�,`wmV0�r�u�����2T�.0�V(9yyy���p�P~�N- U�>��XK=Fm���6GxC�	j�����$��|v9~�������a�Z!DDDDD3%�8	���Dbqe3q_�����+����<7Q,0���~v������Tl����	 """"""""�NK���z�����������<��Y�@jJ26�0��wU���_��^����!��Y`k<���?��o�����������;�c�|�9�.���Kxc��qb��h�_��YRR��+Wj�J��p�-�`����bT4��"""R������hR=aH�L��,|p�<��J�����+r]��@�JDDDDDDDDD�xN��K���{���-���#������Q|:R��������w������,�:�+o
�lC:n�P���R��_��_���+AqA|]��i����[�1m/�������?c��;�����O�������Y�����������������[�P���9p$���G,^�.��+p�Ea���Z�@YY��NDDD�Jr�����&J�R�)��w/���'{��a���V���^{M��������v�]kWWWC�a�(p�GGM�	=zD��g��q�%�d�<8�IE�����
)))Z�(������TtJ��%��9k�M�e�����a���6������=��=�s����U��������gNc���G&w��"|��EH����� �q���X���e��2)�X�$544`ddDk��7iiiZ�(�Ez��p��k��}/�C�`~��\�Y���%z-�3^���b��uXO����XR�1���l�Hwm�� u�H[|R*V�GE��2� ������c���Y�M/A_��JaJ/������\i{2����Vocc#,���;w.H	��*�'k�����q��IE\tJ������'z���N�^�����0[���/�{R�HU,h'&N�%"o�e��4@��"S���n�O��({S�g�|��se/��5@*��'���?X�$"o�e��4@*���o>?��
�1�Pm�<:���X�D��K��}������'�~�/8�]���*�����S��<�L��5X���7#���-�e?w�*���w�b8%�(�Q��
�f��=9�?CE�E
�0O�@FF��u<.�&q���X�LL�%���AH[X�&q�)��c?rDR���ta��N�h���=p��e�z����bi�=��A��p��v+��.-1>9�I�EH�qB{�pr.yc-�T��R���G~w�N������q����O|
��8�Iu��?X�$"o�e��4@*
��?|�O�p���!y4tJ3��K
�f����	�����=���QWx�<:���7�4����_FJ�jy4xNsl�G��-�}�q�-C����
����<���a"T�,PPP o�\��8�IU�O&&�C�A� $�-,h�����T��~o���^;�%�H��nk��e�x�+�+p'�{R�HU�,h�/zGj_���^�;N��ct%����
�-�
)+�Q
N�%"o�e��4@��"S�����_����m�yi�����zA�<�� �q���8�7~��ID�X�$1h�T�qo��`�;X@�����|~�;X`�������������9���}��������Xr�5�m��WR
����V*��w��@z�2J����W�ly�����
�l�����kR�=������Zb�1h @�D��mR���8����m�����t���]@���o���6W�y��'1h�T�����'�/�=I:XR�1���l�Hwm�N�kL_0g^���Q�qr.yc-�T��R��p�l�2��=,�Ln��<|��E�mH��~|
��8�Iu��?X�$"o�e��4@*
f�7��BD��;������H�����]{=y����5�;������G����{f�~2;���;>����/��4u��rT�����0m������'��G���A5.�&q���X�LL�%���A��{�b��}��{�<l��	[�l�=����M*��SR�~��mZ���C�8v�_�����]�c����\�#o��"
D��n��8tt#9�I�EH�q�s�[�_<�����oof)�gW�+�
����=��Y���T�Y�u(0�#e�
dm���_4S��]Kd-30�eR���I*c����L���;�����}Sy��Y��{�^b�k�T��O��D���Zf|a-����LR�HE���wN���I#��x�,��NYQ�*�	PE�S�}��d�M���=�?6��c�'�/{.�sy�����(��6-H�7}�h��Pj�|@�h�EEEZ��^�w.�&q���X�LL�%���A8=O{������n��Fl��
W\q�<B8�IE\tJ����(��p��V��z�Ow������R���=��A�ek>����a=���S��]�b�.�i��BJ�Jy�"!�����O���Z[$��^���Q����h/d^�2�����������Z&Ek��2
����Tm�|��x�I����+���T�^��k�T��O��D���Zf�c-�"��LR�HE����A+������.pj+���f�;X��<�W��ub����am�����quv;��`k=�����Q�{��z.�����"\@l���p�5����T��dbb-1�4 ���b��W^){���]�v�Q`X�&q�)��cf:�F�����������t��2�=�J�h"��"
���rR,H�p$�`I���kK�
"��%;��� ����3-h�������1�5�������K�%�_�����O�)���-,N��m�eR���I*c����L�������[�eor����{��K����������T���������Z&Ek��"
��&�b�����8|�O�-���uX��,�fI�������B�~��]L�_��S��t����2�-�!�9�@, D;''G�}\pM*��'U�>��XK=
��pr3)f{��M3��6���NIU��3[�x��.-\�������yi�tI1��������<������#����y�G��'1h ��B��]����z3Kq>�]�U�O�%�1&�rE�z��C��)sV k����<fZ����Gp�������dD�J��<��,���������/�
V�k��eN��L��2Ie �q��z:z,���N�x�I�\Ya�����%n���R�?��y�k����L��2IE y������f����h>o������L�Y�����l�^��]O������ts:bY~~�h���X���"�{R�������K�{��MW�E�������s����a7�-��P�w����C|9ADDD���S���������&��wg��jU!���%��?��gn��/����T)o�����QN��!DD�g��S���E��X�����/Z7a�� ������������y(��_y���q!�S�y�{?qq>q^�D�Z&����^<���B.]T�GZ��!DDD���[��m�j�b/�D���L"""
��+^z�<~��_}����������������;?��j<��K����3d�G[[�l�F��edd����V����^�;���}�{�n�:mg���x�����g����D2���c�y�1[�n�-����/BD��x��	��q�������b�(����&|�_���~|/<�}���9�����w�\��t	6�,����w}yp%�T��#����������Qx��k����xc���+�\��q�8�xqq>����ZD���W��.P`*�|��b�.Q�b-��������ot`������}���w}	���������DDD�5��W�@��E�?m@�c����[�����C/?���(Q��IDDD�R�6��������(�������8x��Hh�gg�����������7���_X�n����ly�&���'��[0s=�`?���da�����an��V������W_�+V���iii��DDDD�����H~���7�����H����E��i����e��S��r`�����������/%���
��=����l�\3~qQ"�����n�������tZ�(�q���:���N��N�vu�`��b�%��&�����Mx��f�����M��G���'y����*���hm
\��?�S��qW��&j�3�g?���{�8sy���<B�V[[+[���#&�=Qp��?�����e��y����h�Y��k�-�e�����a���6������=��}�����#5���O������*{S��-s��k��^��k�T��O���9��~�/8�]���*���������G����P��fd^������Zflb-����LRQCCFF��������YJo�2��I#�����,��N��L�]����z\���E�4	�
�����s���`������
�m��������Ek��;����oAj���P�')���z��|���E���������/hm�|l��Ik�C��M
�}n���_2l������<�Y�&q�)��c8|��uj����Ggn�
�.p�EE�H�j�L�����{P�6(���.�����[����\y���qO*b�@h�I�mv�����#n���L�����([�91'g���N��?�p�?mo��������'i�����}���IZ�}�v|�sz������n����lw�u�����1�������s����a����8�s?�����v-������I��Z&��A�:.2Ml#6v<s����\7���dl�o�\U(����R�?�.��������=���
]a�K�����e�T��Gi*�e�&�2)ZX�$1h���4�5F<������%tV/���%z�]����,y�&������������;�1�1]Y�/\'��~�7�{�p��E��w�@FF��5�q�5����T�����Zb�1h @���&��~;�bx��
U�?�/|	b9��mR���T�m�������O^P�����0/�_�Ym������4rS���,o����|R��gk>���|o��}���h����X��+�|�	��,�����-~[@ou/��9����m��r��o�k���Xh/���M�P�z�}/x������;�s����GD��t��/
����������_��t|
#��D�2�c-����LR�Hu\d?�q6��BN��#��[�����H�����R�?�.�����>��}��f�53�u�������l�������(M�����Z&Ek��"
P��kt�������h��d�h�Z����e�s�D�2P��(`o�E�*��^�kor������'���	��������IE��*
$&�C�A� ����g��)����wQ���������`/�.,h�����T����;�Z����ti_+7+e4\`��\y�����4��W����]��_�*P;��M����Y��;��t�E_�b���C�]�������>�����j������������b��=���}��g9���/��g�#4��%��9k�M�e��4@��"��v����'z��	#����1�e��|���������u�����i���m*��������P��_�2�R�L'�����g���cu_�.#�_��dDM��;>�����M�����Z&Ek��"
P<9p�, �������;;k���.^X �����
{G-�Z��)�:N��-H@
�����`�e�GLo��5��=��eKQZV�$'��Q\pM*��'U1h 1��z�x�W�����I�_L������`�#�mR���T�"QX�m���
���z��E��X�x����"
��G_�������_"k}���+AE�{��\���Y�/����������'y�`����~q�c\�����z���z���W�B���G3�Z"k����I��Z&��A�:.2�]O?��g^n����w}>~S%����<W/�N��W�����/{j�k�T��O���D�����2���[0��
�������M�����Z&Ek��"
P,���B�t��8�-�sQu��)D���;�ak�����0|�,��Z�@�*P���Yp���J��s�=�\��8�IUHL�%���A8^��`�H|��P�������X�,h�����T��c���2.��1$�����,l���..�,=�@��?�IE�9��:�ZO��=/��hl��,7Q=�_����=�9��j��=�G�'��)����U�]�k}�@_��+����r�X��^����8������>�Y
��x2�m����y�G3�Zb���X�[c-�"��LR�Hu\d{�����h@M�I��!7
=&����<pGn��T��������T7����W����a�e��w���4/��= ��/�2ck�M�e��4@��t��q����aM'/;K��biy
�W�c���j�{�C
�ro��0u��?9&( �C���/?�����a�#o\pM*��'U1h 1��z��"�!�w��-��o���y�r�;v�������������42����-J��V��_��l���"�'��-����?�D�S��q�F�=~��Gt�V�{�MTP����V��y��s���w���;qh�8\~�������q�����	���Bm��+Z����h�_�&�j���D[�2��.���������J����Q�%��`N+�pJFt��%�A.����d�]�;Q�y��f|���3
�B&��������\��""�����������|����c�Qlq8�x�D��:|������]<�|SHC��d��k����_���������fa���3� V9-��5�������s�D��C�?�����6��:��;�B��V�Qm>����8��(�!�^W^V&[DDDDDSc��b6m�$[���w�l�L�h0��������&��W�x��Q�23k�������o]��?P��Uy�""JN�m'ay�0��A�O>
�?�C����s�������x��vwq����YZ���/�}����#[DDDDDD����~m��83
p��1�*r��C�p�2�<BDDD��8��+�\�B��F����}�GD*��3�v��)�0=q���7Ox��$""_]}<�������w��������u��{X�#x�.*��n���m�?�����[�a��|yk�s��z�
��9~�w�y�n�����q��a�����s7���G�����5����]�<C����T�t�lM�AD1��^���@�`���7l� [DDD4S]}#���������������!��5p��2�������_�oj�����j%
%,�U�Z����a����GB�7�����1������v����r����(Ak�J�(�Auu�9��P}��{���L�""""""JO��A�|���y���\}Q�2P1������f���C�B��F���.[D��@�fB���<�n�z�<Pq��+�����""��6�?_j���?�{��-|�wu�{��C�#8�9����Y��-�s�����r|��2%�U��vXk^�������q��;q�����?�o��+���Ux;��{/N������Y
Gr�<Cd�������V����s����~�Y�b
e�����hj �ab����}�.L�&<��\DDD4����?>}���A��O
�k��.=57]>���xj�%���
�f�[��(.Y�am|�����������o^���|C�X���ZO��H6���|N5������[����xq������h��h�_!��������L$'�tHDDDDD�����P�d�=��|d�u/x���{6�c�G#E���DDDr:��?[�IXk�����0�����w`��W�����|R���[�5n���K���>�Px"�����b.�D�<}����2l��Hv�o�������[x��w��]M8��U�
RUi6>�i�����7�b��q��E�����H�w5�rd�^|�_<���~'������{�/��������9�Gm��h�]��4�|td�9�������Kq�����;������+��B��q#���@����>�|"�������_I�7�~F��0���?�"�U,z���g����_�>>Q�>���y����=v���-[��^lx���d���kd�(�����n�kmQ|��tZ�(����?�4�W����C�����3w��l��X�89UM��O*��UUUHI��/=�������~
���v���ub��4����6�m�4��e�L��<*_�M����=}����s�w``@;&��������onnFSS��SC�o$��������$���h�����L�&�2Ie�������y�}>Q ��=��]/�?>RSS�6E��F���Gd/���~>~Sh�>��� �q��
�#Cp�w�9�
��LcmSG����l�E�p?FR�aqm��l�=�*�9�cY�~��e���s�M�"E_*{��Zf�a-3���<A7�x#6m����!.�4�s�}nA���]�����}��qA�<�Y�$544`ddDk��7iiiZ�h&:{-8p���5F����v�E&B���X�T�5K��S<����1������6�m��4z{��K��@�,����Y�'G��vZZ*�z


�9b�����S����K/�${c�{������P\\,{4���FX,�=w�\���km�D�qO�����-`��E�E�����c�@�8�cA;4|��S��^�/�����w_a���������E���X�F��e�@m��<:s��/��E��L��������xp���*`m;	GW��ubCiz(P���b��i�hrb��g�����{Q����������������13hA���/�������I��T�k��e��Z&Ek��2
����4��~����"{�w�
���J�#_|
��8�)����'4���@��m���v���Y����,:w����@-L�}[ Dk��V�nY�yU�e�<X��=�e��o��w@0|�����H��1=X�$1h��U�dr�������M�B���5K��J~Y��{���@�Z��Oa��}}��<��O#����h���� L@�srr��3s��192yP����t���%��%hj\pM*��'U1h 1��z�x,h��t���&J�������=�4@���Tkc��n-\`��nyd�RtIZ���.]T ��g>�)������@���:N�&Bz&_�`MN���P�4�^�m��5AA������g����Y`��{6���}�{�D���'���������`�~-�t_>S���W�H�����"�2)�X�$�1h�T�E�����F]����^uY6��
,N��R�?���G���B&k�`���������������0{���o�p)��-�=�`-3���Z�c,�� ���O���<�X��e��4@���8x��5�Z��y�{�f(U�g��\���<y4t�9�C��"��P������)c�v��7���{G�'H�{������8t�����*.$q�%���8�A���IE��*
$&�C�A� �����71W<��+��=7��/��7\�P�.h�7�D*r=�{����~7%"�$,!Z��;�8Pk���A�"�`,�����2�--��e/�=Q�y��Xz��4����$w�C�s:�>y�S�:�@�A�
x��`N�N��x���q��9����=
����?z�(d/p��C|�,���"YK����X��h��\R�Hu\d]V�7o}C������PY��R�?E�s�v���6u������Z8����
���9�q���(��CU��=�m��9y��G�e��2C�w�e����������X��)��I*b����qO��@�Q��C�~EX�w�PV�)��G�������5������}�h�@�,�t�]����C��0(`0��� F�z�	^L"p\pM*��'U1h 1��z�x,hG�t��`�_�1�Aeee�EDD:�'��[g,8{nl�L���fa.�NC�������">oO�9<�t�]Ha=
H1�u��[/4������>( �u$'����K�";;[�(�D�@MM��������n`�}E���Z�x�e��=�5����������q	���a�����p�;�89�T��R�F���u��8|��|�Y
�g�~�81o"|
��8�)N�E
p�\�@�k�����"H�s�#�Nq�����,w���j�@[2��s�=QY,�X��=�e���s���y��j����w���Xg-�T�����F�t�j��E&c�Ks�,)���zd���-��q��n��D����:�{��LG�6�f����/Cy������B<a"\ 77W��������mF�Q#������&.(A���kR�=��A�����c�@�8��-�FC���F�o������]�d/�/�!=�ADD.GG������E�`����-H��W�K(""���}%��R�
�u��BR��H2��[/�	�c0��2P`856�h������Lddd`pp��������7����+����i�d<�����D��H����v-1�~,b-s<���3�l,j���o�K�-�m����y�h��\R�Hu\d~����i�u�8�6���aykd<�����"G��_�2���s�v ���]�����p�a]�  ���-L@Ou���9E���������?,{������Zf�x�@}�W�{�r�p�k��"
�����5�`0�����pN�;X`���������������hDwoL�<-T` c�R��=�'?/zC�h�����5���p�l6cxxX�$��O\��8�IUHL�%���A8���;.���JA7n�-���������+++��E.��(�"1��D��w��w��������sq��B\sQ!�2�Z���g>��{�WTT�t���	�g{
�j��c�O�:FL������0�!��-����I'�(���C~~��&�_$�z��W���#{��-�?o�$����N�(���s1�<P]]]x��wg�o.��/���#*��%��9k����V�A��C��g��������2
����4����q�u��`�S���`����0[��y�k�T�������B�����k}y����� �*D���!6����j|���ZV9��`�{"�e��2C'A�<W�����2IEP[G��*���������d��-X`������06���;��V�snN��|;z���VS�,�����.�O�%��@��0* 6����p�5����T�����Zb�1h @����}���{L�N�M���Iuuu������Y#e�k����P��;�/���,}:6]R�kW��,[%����hq���WLJ�)DRn�t���=��Y�d?_[[
����l�5p�[�H�����P-P������ ++K���w�Wgg'^z�%�#>3NT���
7����a����#Gp��1����+�j�*��P�v-����X�3�s��V���I����}���#�3�y�ha-�T��R�����t	�
����������k���������+�M��R��s��d`@�`7uj{��?��Ya�]��x�Gd��#Bm�ELb�\�Sl���W���o��w����^��X��=�e������?��w��������
6L['
fne0s:#��LR��s��O��N�\8�$����PO�@zj�}�h���@��Z�P�a���y}f�(��V�����g���T���94��b/.�A�/.�&q���4��XK=
��p<���{\N4�v�m�[��b���B=�E�0�N����<�kVi������g>E���(Fj_���^��B_���H]t�_�����h��$h��Q;*`k��5p����n�)�0e� �",�����$ �����(���b��dDMC|���'#���>�,hwww���U��F�<:F����rm�����v-����X�t��y���{����0�s�sGk��2
����tz�!������L�;X d����?���,O~e���/�He�1�n��24����	x�G���0���{�E[��b�����������\�����m��m��e�}�h��d|��]�k�������_�����U�$�m�6i������"�H|�;�q��W�;������E9X��,�l^�<���
�����4 �����^��$i����J��\���L�gi�i�\��=�����"�{R�k�����q�'���L���?	��@<�B��/��r�I���r����tcq���M*��SRU(�~C��h���w�$���j�b���y
=���H1���}J�G�N�$)���fD�kKv����y�'�y��ZS
8�@���:����{�� ��"@` ��i�@,0�_2���\& ��NU�����C�������/���F(C"WA��phW����d^
�h�Y�����5?o�L���&�+�Zc8�M�e��4@��"�u�ZP�6��F���������*�����G�� �q�G�s����=�.8�����{��o3�k�o0,C|��W[�
G���]��:����7N51}�u�]|=��!j��Cn&6�z���DX��=�e�F��
��L4@�x4�����q��(������%'a�R�(@�!v�=��-z�5����~dj�"X@��ES����>?�������'PXp�5����T�����Zb�1h @���};Q���>��Y�	��L���IE\tJ����q���{�O������p����(Qx�g>����0�v}��c��,���jt�V�?}����<�y��1k��v��Y���_FJeh����I*3F��t�w���!�	��k!"T@
�`�����:������.�<�hN$�*P��Sd����89�b����}��IC����$}�;y6���6�2Ie �������t�P��Mc���J���~�������2��R-\hM*���C�Zx�m��C�����T  ���������'W���pOH��=Q�@(���i�S����K�7F��&�yM��n`��4X��D�[o�\�M�6]PC�;��W�h,���AD��A��x�	o���������S\��XZ��SSb/�����kci�E��v�`���/�*�@���=
E�\O�Q�p�5����T�y������c�@�8)�)�s����99�(vq�)�*�����^-\@�����_���"l��W��VR�S8�����<��O_������?������=Z;���y�'��L9G�� ��S�=}�]�H16�[{rL�Er�����UI��������8��������mF�Q#��-//���B���$�'�R���!
b2��m�.��;����D|��D������LR�Hu*,25X-P@�	����,0,V|F���\UY����x����������!x�p����M��G�BkR�����A-�.C����Ny��2j"(@x�"L��	Hu��I���ck��~$S���"@w2����x����"X���Zb��|J\����.�$�M��N4w2���{�3h�(60h ~�-v-T��k;x���0�\���5K��K���|�m��Z-T`��z���o���T�(0��y�i�����|1/%;�x����kR�=���2k�����qR������LU�>�t�ra�z#�mR�R<��Z�h�>7
�����|�������m]����-����K��� ��(��3����t�?������u
�2K�-��7�c����7�"�S?CJ�jy���������~
����\��u�V`(M/�1�Q���"�X,	<A��������H��p�l6cxxxt�irr�%������Z"k�$Lt�.O������d�@k�����c8�m�e��4@�K�E��!+���.�o��F���,,(�q�g��4�>u��=��&�����A�m��{x���d���T���w}st��tM�v��"@�j�k��)Y��������l�Ep���hkmu�����p������wp�g�����{vvv���CZ�n�D��%�\���by���Z&%*�����uS��v����������iv��j!���K���X�D����G1�s�����u����,����g�c c���]���� ~��dC_<{t>����D���IE��*��LL�%���AH�2�����S���{�������F��".:�xq����'z��	�6���������X����~��N6�G��z�K8T��������h��|
�����8]|�g
o������Ofa�,�D�g~!��q��.Cl�'���F�6kr�$��
��CD��_0� �0�����
�S�k��e��]����"h �I��L�
�����LR�Hu���������
�������#-%	��9�a�����s�l?���Q��x'��'<��J,��'{4
��e�;G��p�
mk�����XtY�����l�-BD��j����c�
bxB&���]���G���c�doz+V���U
k�D��7��*��G��)��L��H�~���#���n���m��X�kk�����9+�
suX1/+*��bntQ�NC�����H�i�PO'�f8��P���"�u�]*�1�����|���&;;[��uC��?�IE��*O}^G}^�������H��gk��a�@�8)�|����a�������0��+�m���s��������;��8'����������7����eoz�]_���T){��G�h�b�X��`l\Y�M���������|
�+O���.�9�^y�M���i����_�t��H]�����\[{
=-��i$P���"��C���m�&�yE������6_oiHL��%��I���7Q�p&A��	d�t^8�X�$�1h�T/�L�
ia�mC8�6��{B?i;"��,[��*�����+������]������	�F����m��4@*��������.W������������BDp��
�����*W|C����*�e��������f4��j������k���K�a-��B��F����
S��ZfYY�lE������8�4����d~�����S��"
�*�0�8:uk��y����-����b�)9[�c#�hK���3�������CDDDD��`@Q��Dk�����qR8������-���?!�o���8!�'������b����h@M�I����\|��y�^�I����:c���4�7�[fnAy66]R�m�y�^5�b�S������'�s�G_f�<�|s;�7�
#�L�/:�P�"��\���H��ED"H@�����-6�%(Q,`�@b��\�u3Y��	�*M�e-�T��R]�-2���8�:�:&���$�5%�T�e��<��e��4K;��e�x �Ec�;��ZX�����G�&��k/�DMW��&
Nq�%��=�x�*�x�L 0Q-Fu��{:��f���$'G���	��L��|����d�Qx��s�x�E8��B-5%	�*������Dv.���)�F���0�c�b�@j�6����JMBVN2�sGCX�!"""�������c�@�8)|��S	4@��	3���m�������".:�X�U�&�{(�����:����8�/r�RF�����G�b�S�
�����E]���H��8Fbf���T�	���3=Q�`�@b�v-��L��"��LR�Hu�\d�?hE}�D]����o��F���,T�ec�k�UeY�����eQ��m����aS`4@*��w�p��S�ka^mO���2����`89����
����T�f�	�
��sQM�X���e��;.���Lj��V���q�l%�������s�����:R���j����^4t�!_���X���.����HN�� ����Zm�im@_���v���aJ��.<������������l��6*�p�B����{%���V����#���#-�*���qO�5zQ�5~n��C�A� �H��}��a����BD��M��*.{��h�sG'������b����?*{^�{��Wz���p�&�6dq��`�_n���Y��8���O����/`_���yv����B\��;H@��^L�$�7�p���]Kd-��3����9�2Ie �Ej��9��&�6�3m�`����5:�R���<�e�0O[\=.V�4��������n����l�[n��ez,�dx�L0h�T6��w�,Z8�;(����r�M�X��.�$�����HJ�$ BD[	��@w���u?U��Q�C�b������G�����L������W�k�.��L4RX�$544�.��7o�������5F8it�{�7���J���i�k���pN�<N� ���`n;cG3��>��8��1�����e�$A����r���b^M^^�v�=������v>w�\�l�8�IU�O&&�C�A� $�-,h�����b�C��&c��x����*=U�5�pqE�kWi��,�P�����N�NmRc7�s
��[�$'�
���JV�=����:q�'	�E�����g'����`����� ��(Q�����=T��IDAT��]Kd-�����{\�2h@������LR�Hu�Xd��1�-������@��j��������h�+��������F���i0��"5�Q���R�s�Oh;}�]O7�Os}&2^ `�E��E�
���b/���#I��Gb��d�}.��=�{��2)��:��b~�q�4 ���y��.>�c��LR\�W�y3�<�����GC'=-k������}a~x����7�h�EOG+��A��Sa�������a�M�i�0�������T���'Uq�5����T��dbb-1�4 B����6��Ak�~�	���,{.���L��{��N��������p��U��]�x+?�m�G1R�:�����qJ�+Y��EW m�UH�X)�R�8�p�h�6�T��	h�A�����U�+Zy&����v{��b�jV�*R�+KUTThi�"`@L%Jd,h'�h�Y���0h �X�$�1h�T�"k������i���ow�-V��Gt�f��,�rF�f3��&��JZ�U��v�n�r�hE0��-dP�tIq��uY�m(0�s�SE�@���|$:O-X�~�������g�`-�Q0�F��NTo����c�;h`��X�Z&�(�\�B��;�{�`��5F-43��
3�f�;X`���	�O���[[
z[���y}C��O�FZ���]t�5u�����4�0�|4T ��;�HU\pM*��'U�>��XK=
���(���M*b�����&�����^t�/D���B��Q����|�+O����dp$����bBd�m��-���;
�W��= {4O`�DP�'@��t���������^8�y������\� ( ���x����}�)�������9:���T��vb��\�u�,�Z����H%������D���}���;��4a`���.�;�h�����c��h�S��9E)�(NCeq*�
:�d��	�����hI� ���dm3j��!�7j���\}��� �^���_E[l�$uN��{b�w�HEl�c�������������c�|(D���?��o�3���w�0�x�I=3��S$q^&�(\��|�O���F��
��c���q�2=�-3`��\y4t�6�y�����@��!w�l(�����K���s������c��=�g����>�}�|����H����t�����L�g��0�T�_#����HU\pM*��'Uq^fbb-1�4 B����6��AK�~����"{�7wv���HW�"J4���ok:��]����z3Kq>�]�U�O�%�1&�rE�z��C��)sV k���R�Z�CN�h`�s�G�<aZ`�h�&�z��q.��T�����TW[p	����g�HM��F=����N��D_Pz�{
�JX�NL��K�.�A�M�
��c�w-���L���HU�&;Z�E��k/����1���%-%	s
u�S�����xi"�GN������:�X;�u�"`�
:wX�N����D��X�V
��FoD=��	���^�E;9��8(r���d���Z&%*��5�0�o��v���y���y�`8/�T���~�	���,{����
|��J�����!�1��I#�q}�
��t�,�c���Y��!7��gE[�)�����	��}��������''5	�y9(,�}�,mnMvv�.8��R\��8�IU����XK=
���(���M*b����}�����!#�f����]E��u�����I�%�U���o��S0����>]|%�
/�����~;�h���@�����q�a
pvZX���F�\�����G�F'��mX��]�J����U����Daa�h���rs�O�g������Z"k�4
D�����n������.+Z{D����}_���C�0�}
���T���D��F�dH�Q
���\m* �D��s���*��Y!��-���Z�v���SF�����	
���^l�aD��A��Z&%*���0U����(Lu��O6�2�O0A�p^&�(^\?��'����&�<��%����m��|~�<��C���p��t�,��Nyq�;X`�^�f����ho�Eo�)��k������k�	�I��)�H�.��/���}�h��_���R\��8�IU����XK=
���(���M*b��
������!{���"�V�a��,Z��Oj���|[�a���0f��f�5��,���/���%�_�����O�)���-1�n
A"0@������0��^����2�I��D�Tw����\{����������,;�g�@���}�{_Pi� U�����]Kd-��3����<�c�0�p�;x�27n�([Djhjj}�_YY��%,�����C�oB�k�i����QjH���,T�eaAy��d�����)r���a���vEE�=4����99��@7��Nw�$j�]�1Q�u���nks8e��'( K��@�dW[�����J��D���Ldddh�h{�y��z�p;{��l��������B�D�Z&%2����n������D��'�w���l��m��"�`������c��2IE����������M�7s���]_!{���p�D�,p��f�g�PZU��5K���e��9qai�AO�i���wp}�t�2f������Id�A_XC�<w��k� Uq�5����T�y������c�@�8�b��"
P�x��_��q��;�.�{�������O���������8]<�H���$>y��Y���3���_�#ab�"<�=��3&�	p�����������;��nO��#�?�r�=� 99Y���R��)��D�2i:�dA����|��p�;��I*���]`����(��Z�@]��kt����G���-��rP]���g!7��(��"S�����"S.
U��2��f����s�-��u\W���@�V���`����W;%�}\������h�����.����(V��?X��D&����o��:���uM�b��k���x[p}�l?���Q��"~���w�#����y���,�>�8��w�&�k3SY:�]�X�T���?�9�:0�t=mga��F���.��"y����9\�/Y0�*���������
�����T�qO�b}21��z!QlaA�T�������j��&����6�`
}2�D��EX\���OJ���7������s��G��g��>����_�l�fd^��<��ux40���W{�8v������s��G�`5"���:�j{n��r����#�)������!{3��R����s)��$h@�[���^l���sGk��2
P���v�	�
�6w��9�{2V�d�%��<<���H���A��A�����N����o]}���	���A��s\�mE[e�!�}O��CQ<b�3~��I*�
+�J�A�����m���t���Z&�(�\?���4M>_&5����>�
�k$�#��:�>��_�^�j�T��c�+������Y�:�2�����������
�m<cG3z{��kq��ZsJ��G���:���������0q�P�A�*.�&q���X�LL�%���AH[X�&q�5������&�;X@��������.�l}:�>))����|�?�(��{?�2K���������W���/��U50�3:��&�A��wkW���#I�5!5�"<@�=V�1U'��"��JV�I�b;�<�������g'��;S+V���U�dof4@�bA;1qr.���
�N��w\y�Lv5�p�;�X�$�1h����vw����A��`���G(tu2��4�0��,�����D��A��9bv�fEh�pa��Sm�."�u$u�;Z��e�v6l��.P�wD}V|6m1�������;.@�(���?X�$U����}��{��q����i�&��O'�9�o�A(�I�e���i����7�����~xB.�m���N���b8C���K���>+N���i�����;�����f�(������X�N�����Z�;0�>����t�]���@Az2


�/�����Z;99Y�#~1h�T���"�{R�������c�@�8�b��".��P�kD�(�h�!�M��(���c=RS�9�II��C�<��'jQWx�<:��obQ�~8����S�'��'�jT��#�"H�3aU��9��78����&�2���/������� SXX({3��R����s)��*h�����Y;v��pRm8�m�e��4@��2bG�h����Xm�}�TV�9&�	(.�d/�o��B_�;���N& �]��X���������Tw�,����->Q�oH���{�3�9�Iu�{��2��k���xYp}������������R\,{��uv��M��^��d�`�=�.-�����?9��j:c����8`F�#����.3����T
�`����P��%*
�����T�qO�b}21��z!QlaA�T�������j�D�@Mc?�#y���Aa�K&&�>���Z�c�T�q���/`_��.�9g��?3���wH����������*�|��&�Y��L@�h�����#G����c�7s�cd��X�j����HU,h'�h�Y����4h@�_"lh��]�7���7_p����b8�M�e��4@��;`u�	�
i������cH�=����.�r+�AN&���x��:l#�AS�� �4��N�qKR�h-V�e=���5Z���"�I��E6++K���x�6�g��p���X���e�7�2IE���������-�\�wk��b8� ��/����-x��<23����v�;X��E���s������RcO7��m�C�R��=�'OgCAN
�g��b


���*a����IE��*�'k�����q��IE\lM��k���M�����A����
|ls����'�r�;����8�S�^�d���:q�'	�E�����s�0�-��'��T�dT��T�M�ib!^ww��������P[
 U�����]Kd-��L����;�u�V��:�7`���p�;�X�$�1h�"��{X��V�&X�)�5������^\�EyZ��.Y��	��������Dh��_�E������"@���j���N����f��#����a��mgR�<�z�dY�����{���\H��O�c�3~��ID�X�$�����}��V��)�%�Z{"����d���M�e�����G�j��|�]�����di����S=�M��5�����1����o:8����>?�Ye(���B8��A�..�&q���X�LL�%���AH[X�&q�5M�t��0i�g��E�@�~O������2Wks��>�����6�{;�YP���2�RY�
�t��1h�&�b���W����'L�����������=��X�����b������^zI��L���dn��O��u 4@�bA;1E���Z&M'���w��E���m=���{�A�?�0����2Ie�p�ow	��
�L���6
M]	7C^������,�s`��"SR����V8L���.�]mw�uL�M��8fA������&`I�q����5��4����]���-����Y`���u���Z&yc-�T��6n�����,�,V.��3���9��T=��[��eo���w��R��O35��������?�>k2L�8�v��dC^z��(�3��J����[��HU\pM*��'U�>��XK=
���(���M*�bk�U�D���5�����C�bn���<�xtj������=���
|��J����U�����3���oE�.O�g�GIn**�������4�����nt��h!��^��:��capN����21"��LW;��[&,���:��S����E��LL��LF��|'�&��j����#Gdo<����dV�Z�+V�^�4@�bA;1E���Z&M'�������?#����2Ie�`
���a���k�����}oS^����,T����lT����'fq�)�.Q_s�V�AvS�h��7L�n�B���k!b/�#^�$u��uW���D{��[}��Hu�{��2��k���X_pm��q���yJ�~�;�4h�P�0���'.�=�y%YX�D�����������C�;�0�����I#(�J�����*f�#++K�J�`����IE��*�'k�����q��IE\l�q��@.���.�FR��L,�����\,������F=z5���(q8��H��x��U����{��g����IKr�q�m��=�8Z8�6�tP�������t�#�~������p��Dp�8f����w� )\|����O�Q��������<;;;q��!tww�#�+,,�%�\�����T0h�T��vb�v-��L�N�A�8������/�#����m�����u�h`-�T��
��d� L@��
�[�C����������.sdg�W��"SR]\�v8L]�f���&�u�znF��ZX�Hj6�Eh�w���k������'������������}��X���e�7�2IE����fw��y�[P��N��l����p�[H	�\
��y�v���U�0dc�jG����.�O����gg�P\���0�f�94@���kR�=������Zb�1h @�D��mR['6��}Em�;L����t�,o�Cn�0-X������0�l?���Q��x'����Wb������;:����w�E�.�d�{.��"��� ��)1�T8�^���$ &�����d�����,����fi{ �D��q��1���}���TC���#8v���Mo��X���@�Pa���������B4�!��o�>l��Uq!�6m��-[��������Z&��A4���a����mg\���7�'9EKf�n4L��,K����<�������T��e@pj!"0`,L��w8��0��dsF���lXR]�\mw�@�V�U�Xx�	
W���=5YO��U�;��u���Z&yc-�T������2X`@�2�%�
�h���M��r����*�iE~�y90�.G���(((��R�1h�T���"�{R�������c�@�8�b��".�N,-��Z�@m�jMZ������)I�a�*�����������x��&���x[5����o��}�W�����������'�y����'�4���o�����"8@�L:(C\m �	�������=)E
H�t�� �1k�����������L������yOT[(��4�������f4��1z�����VXX(���HU,h'��~WE���?��.�Z&��A$ha"T�}P��{\D�!/�rP]���2w�@��Lyk�p�)�.l��C�z�X���6���0��	�N�70,��=����d(�n������N�}������Hu�{��2��k���H-�>�>��-c�������������|��f�#�c��W��F,�OK��������E�����R$0h�T���"�{R�������c�@�8�b��".��_�V-H�T�;P@��c�6�$�,P���
,��#o	����x�
�q�?J��>w�<,��'�\�c?�:����#
����M�V�C��
�&���R�
���S��0$�2X��w����������[J�;0@\�������Y��\���ir��@���M�N��p8`6�1<<<�o���,o
/
��X�NL��%��I[X�$�1h@-f���C�kp�
������}2�8UeYXP��
�vQ~d&Qq�)�.���sdS'�",@�o]m�w�wE���
����"����}��a�qG���;�|{�e}����S��w��u���Z&yc-�T�����Z��(�"��Y0>��0�3��/���0
�RQ`(BaE�U\�4@���kR�=������Zb�1h @�D��mR[��2|�i�, ��N��5r�����(����Y�3O?��g^n����w}>~S��M�c?�����W�/{�+����^�`�D��K=a�m�pm�������pw0�%Y�]���1�u\�Ig�R����o�C��&��R����s��k��2
$����0�!�i��M�����!��0���,T�g�D�.:.2�v�C]m0�v"��YEe��r�]�<��ds/�����B�"L@�uM�����wi}�����,XRe��N���q{W_U���wH�wh@VV����nt�w��u���Z&yc-�T���sF�v��2P������5�PIM��C���^lJuZ��<}n
�f�P�������c����IE��*�'k�����q��IE\l���q���S��MDZf�N�.P�����j�L�����{�+���{�-7`�2��0���:����f �W3Z�3�Kn��v�*-0`� ��2���	pj��E�'�{sO �hp�
���Py����w&E����@|�	��|+V���U�d�hr U�����]Kd-�(���I*c�@bh����������kBg�{BR���Qg����F�!,i����s��D��c����_�'������$7W`����Qe5���p������:6�{�C�Z�v8%Q�M�90'��Z�@�]�E��=Y�I��w�T����;�T��g�`-�����I*
d�uw�j��a�]{. 9�!7���L*��(�BJ�X�"�s{��F~��90�.�a�R�����4@���kR�=������Zb�1h @�D��mRQ".����1Ya4�$�4rS���������aZ�@�	f��I$�I�K����T���*���1���w�d/�_Rmh�=�L
p&��� BF�\�<!6���	��=MOLL�NEQ�{/���

a��=�Q�w��7���P��&��R��)��D�2�bk��2
�Z���F�Q��z+�KCu��Fu�;X`Nq|����4v��	NU�~����",@k�����6�,�lXR]�k/�]��mI���%>����G�`=�{O[l"T��#��X���e�7�2IE�-����t�j�M�m�������N�� ����^�d ?�]F�6�*�:P��CAA>�sQ�`(&
�����T�qO�b}21��z!QlaA�T�(��O���H��>=�>�{fzfp	�C"	
L"
P�a��'=����
��,��D�2v#6�b�x~�dY��E[���M��@��L
<) GC�q�������{�������������"�����*������/����k���7�wb�_��������^n�����w���RRP i/�	��Mn�8�]��\L��k�U
\�5l+�/�+4�=����O�]������+������F�S=b'���4-��-;��}�������C�T������{��}��)X�B���`�v.Q.z�\&E��@�:7��K�	<S��K��=g�z�dx�[������o'W����O<��prd{2�~;�N��o{G�|����>�8����,g���ai:����r��X,`~tki<-����c������v@��g1[����Z����|Pt���C.��������g���G������+3�����W�������r������p����kr���q8���s���9���g����s�X�y��a�U��]o�%��bQ(4@Q���"����`�Kl?�$��HhSD�p���y1���^J�j��\~�C�$S�������/��/�XX���A������b7&����������BX����[w���K��Hc�h���Sa���r�N��s���d�Ji�Ji�_l�#Lo����v�M�?i�0_$����X�Vo���r��N���+_�J8v�X2���?w������d
jSh����L��%�eBo���������r8~z!�8=vN��K����how�=>=_�w>�j��@,��3���1<<T.:{�����Wm-�����+��"����}!<uz�� �o��\y�L-��>��d��b.�R0 -Pj�G�b�"�x���L�8����r�8�
(���J�G���u�}���l���������gE'��?�2�,�L�`~q9���3������|&|�������f;�/����B�=9.�<�&���X��F������t�a(4@Q���"����`�Kl?�$��HhSD�|��w��������u�5S���.�����9�{��\��@lO�Z|^3s��v#�T��XL -.p����[���K������������$�����aS��]vC���0~���W�=����e�|���X������b�r�a,,�n�#����pex~�4���"��E�"k��_O7����#��_�j2�*~�Y�c�Z>�������Sh����L:�Yr���x�G��_~�x���N�/�����q����[/	�\;�����Gg���������0X=r��y�[�L���	�Bo�"),Po<��\d�;^;t <v��d����Wt�����{�L��lq�R0�\�7S@��j�XX`i�h�;_,�\8`,c!��,����d1w���ll�h6����g�����24����&b?�8���3����a;&��b�3y6�,Mo���EI��mi:|��N�t

PT.����=E%?9���O��	B�-�Q�^l�{_{)|���������	?��������]
?x�t��������X{�����pBZ``t����~7�g�^����7�\�N�%Lo����j�������=g�
W�����O�rrKuE+4���P.�-r������xi���X�R(���x2o!�.�V�5��x�[�N�1�����N����N���C���&SK��V<���/���7���(4@QIh�n��2���eRd�Ph �6c��^1�s��;I?�)&����������U�r���r��la��vO&���L{��|�7�����T�v,�
?��_J���23�)���X�;W)�)��e�����M�zE��^��t\Gy��gE'��?�2�,�L��s��
O�\)(P�t&,-����}b�|Q��������~^����������?��d�A��E��k�H�ST���I.��h� ��"�M�����}~:�����TF�\m�s�_������y[2U��
?�QRX�����W�%�l��[���n�fky����N�.���p�������uG2����}#���D�|�����3�{�~/4��0{Q���E��8<w"��r��g�B�D:2�q87��t�3ie��-^��V��j������q�����O�c��%s��k��p������w's�~

PT��I�\ K.�"��B1���/�����dN�b��O�����z�b���r���)
�������,�%W��������RV
��re��L{�_�\��������[g����/P) -M��J����Riz!��/ �������8�����������Eb��y���|��,�2���g�����2�'/�~n��@�=]��o������s"�;J�KJ������W�������d�A��E��k�H�ST���I.��h� ��"�M����������{^���|�������%S!�zl6)(Pj�����g��������D����p���m
7���{�u����;�r�?�����������m�y5���	W��w����a2wU������r����p�������$��c��8�����p�\D�7;��s�j���j�zb����iZ$ ��#;��[��^����C�%S����7���z��F)4@QIh�n��2���eRd�Zh����R��#/&S���]~�W'S�96=�ZL������g�Ko�$�v���P���[��n(�Ja�-arS�[��E����w���;�&S��O��x�������B�h@,0��+��������:b~s���q#S( ;�upd�����=��\&�%�I�z��l��@ZP ��T����]
;'cA�R��/���c#��4�������0�m;w]^=~:<�|i�D����r1��~*����\pM�{�J~r0�%��B
��[$�)�~+4;�������f>:s��w���eb�\\��t%���b�X� �)V9M(������o���;�s���p-o=�?���F�yg2�����<s*��p@�H@:%-PjK+Canxb�p@�W�*E�t�����$|=b�*�|������U��1���c�����v�Di����sg�����m��]�\h�B���`�v.Q.z�\&E������t����������������'oo{��d�B/�9_L��8|�\8z����[�N�������nH�i��L�dy�\0vy�tx�k�9<��3�a5_��V�?H�����7o.c�8r�Hy����P4>(:y����\b���\��[X\�O/�������p��X��>8GN���cQ��*���3�s�F��,W

L,$�JQ���J��^0V���caj�X�6>�m�
;/�*l���0r��ahbk�d��|�7�����T����K��O}���)�B���������'�\b�)4� A�EB�"��BO��t���v0��_�L���J-Km����^��L�7����3�w��e���Z2U�P��;	��V�"�@@R(���d�8o>����h@'��t:?�/������.��O�.�	���A��)��c���r���	����;
z�6�B���`�v.Q.z�\&E���~�W���x:�jB��|������+o��r�����	<s�4|�\8����������
Wm	�_�5\eix��p��������L�2w��N������J�t��0Y&�V���<<�����DX������_���z�8�
H��-*[-��L�St���C.���L�/�_~�x���N��_=6�-����0�0�-��7_�5�q����[/	�\;��kc�O�T)&�
��4~�T�?>4<�R."�cr!\�y�|A�������m�?��
����m�'��]��%o�>�\^)*P�`L�\�k�����L�j���=�����3��(��\pM�{�J~r0�%��B
��[$�)�~*4��G~~�k/'S�a|l�|1���*�r����3�g�{a6���"����3��{���?���K�����].02w:\�}2��S�����L

��H�����XD�<�2\r�t�����Zn�2n����)�������E%�=���K����"�I��[���?�b�����L����f>B/�_�+j�k.��r��pC,(��9���N+�E�+��!��)_��^�~z�2}A��d:]&��8<Vj�Jm<,�$�8����i����a��M����up���dok����������=��\&�%�9xb������p���r�d���?7^:5Yn���/|�����:3�T.&��K���g���f�[��}b!�L�
�b;J�q��L,�.��/����}�����7�MW��o
#��)Y�9O���S�������J�k�ej!���O&S�B��)"qOQ�O&���Sh�A�z��6E�O�~��vx����T������u�����_�bq���ikr�X|������6<���dN�\�������saq�LX��F&��he�
'�8�V�q��bG��a4&���Ng����p�����m���n{nyO2E�B�B���`�v.Q.z�\&E�O��/���_;�L�����r�XL [X`b\��n���L���J����.*p~�le�t^y��Y.������C�ai�2���(+*��/"PZ�t2�<������u��Gi��Z��lQ�8�
.����?E'��?�2�,��������g��B���r�w��dnm_����)|������y[2�qs����b1�XX��J����In���M�agRD`��|��@,,0<�;���f����a���rQ��[&��K/�_F.�1�^~S��=Y��^;t <��w������c�T���w�������h��\pM�{�J~r0�%��B
��[$�)�~�����3����'����
-�+�O�K�����[������J�;���>n�E�����L�x�/?����W������Asb�tX#����"iK��������?����!#7o�
��S�y

PD

PT�����D�L�-r�Y?��#?
����������p��t�>t:�Z�
�W�������:9n�*��rEe��;:}������2{&��B�af|>3/�L�@���l�b�rK���,�N��8,���.�_�SZ f�d�
����.����?E'��?�2�,����{_{)������a���Ee�8rvS���;�������>pu2wmK�+��/�
?�E^:�{�\rkm��Ka����cb�\H�RX`!�
W������05w,l��E��m�g��;����
�W�T.*0r��ahd�������g�H���������'�\b�)4� A�EB�"���N��.��Gg��Gf�_~�2>;�~������;���mcavbg2w����p��B��������%s�7�i�|A����e;}!^���|X�9���
!��;�K�af�4}�4�W�-��	Om�~t��%k��U;_�W��4iA��Q4;\k��pg��O���MU�N���X��>�K��(4@)4@QIh�n��2���eRd�Zh����p��|8Zns��>�j�>Wy�yo~!�>z(�>��p���antK�/����aS���xExb�m��7�py��f�(n!��{�x������+W\y�"��n��LcN�|��\z������/�/0�z[:\M
T.�����C�@@R����\PD .?\�_���nv=���kH�c.����?E'��?�2�,�������������cW�J�\,�^[���?�=|���o{����D�>�8~���ra�z���#��"��@RT�4>9�;�V��TRL`k,,�
�N��v_F���RP �]�&��
o<�������+��az���+��4��6�t������$s)2�(*\SD�����Lr����@�!�	m�h�/:�_\�8�H�
�1S����O\��0�{w2U���#��/�+��.�B���V

�����b���tR )��q|u���tX�=�ggJm.�/������4v6�������a�#j&����/�Gfd�1A_m~�y��������U�����{�].�{g2E5

PD

PT�����D�L�-r��FX\Z.
86�p��@ZL -,plz>�/,'��X/�������`xc�����[���=�-����.=�\�s��������8��9�^�9���������=aey��������i���gO)/3�0�V�����2����/�JT���1O[�/�+�4ZZ6�K�-En�������
����"^d�Bk�L�St���C.��������a�������r_�<y������!��������������c")$P.,0_n�^��[��S��bia�mSSa����-�����{�����p��+a���0�cw�|��ad��\H���������'�\b�)4� A�EB�"��E����x%����M�h���=����av��dN�&f���zyG�����9�����������y����\X��
3����J����8,�9fg������0���P��b�"I��te����7����dI�d���J��?�Bx������J����2�n��'�)���E��E%�=���K����"�Y������p��|�95.�c�������B��-�/���
��;y�����4�\��_
+�]��uG2����}#��R�����,�%�[���P�����Bo�"),P�4&v:ae~&�$Eb1�JQ�Jq�Ja�����sN��+��R`�b�r��X0�|�MU�I������#�"��?��d�<ZSd������r�@�\f��#/����*��R���������W����W�	T������x/�\�.������0~�
�b'�v��mo
�;�
��p��)�(*\SD�����Lr����@�!�	m����N�O��/ P��s�����~��������^��0y��d�b�������v$���w%S!��C?���K��<��*
(�Ef����a���0?s6�������0[�"����h�H@�jZ$�4>ZU�-����z�z]����H���N�#�k��'��N89�=�S�K�����#\���d�Qh�"Rh����L��%v����e��~t:�����o|�Dx�����U�_�%�q����[/	�\;��-�z

,-��c��X@�;_H`������07_���>>��p��'�����pj2v�n���W��o����S��/-���-��]�5�{����+7���-�F��_��/���t����)O���������K�� @�0@����X<���Mo/
K����1��8s&�-���;�}���p��>�L��Bk�L�St���C.����_1W����?���d2�}��#<}tK�������p��b�l|�l�:W)&05
T�7m�Z.(0z�M�a��^-��k�H�ST.����=E%?9���O��	B�-�Q��N��.&�f�����	�,~D�>:�������^x1|��l��_�p9|k���N��������8�_�*8Y.�23�J�sg�����07s.,������_��_Z)	H���_��
���_Lz������w!k����������7o�
�Tg�F(4@)4@QIh&�s�,�������R2U��}����&�*�CO=S.p��r��+�8�ta����������F����@�����9Ks��r�o=�?|����K�����3�
��,P����S��.(��@���8�Hn�[-�.�\���/P��(�+���Ja�t~y��7�)�4��fc�<X���r�-�������x~�����F���<�L��n�!���=�y.����?E'��?�2�,����;t(��>�L������|�������T/�Xh`x��IA��V�\~S��'�gu.����=E��k�H�ST���I.��h� ��"�M����/��~r1�L�	��K

�+����k~��'���%�T�&f�����m���������1{y9,��af�\4`���0�T�=w*,�����+}��/���������X�#k,�;��;�J���Sk�'v
����p�����������S�/?}(�R:�L�lK����4��6�t�^m���E��E%�=���K����"�9X���t���_��t2�blh!\1~*\:~6��^��V.����k���?z]x��/��o�K�+��.(�Z<�����|�����M���>��w����_O������?�����<��al�?rW+����������������Ef*�U.�����4����"�J�����,;���E�A���/�O��y�r��7�������k�X���������=�G�s��~|�Br�������i�f��d��|V����axe)l�?V)*����car�T���������q�����������k�H�ST.����=E%?9���O��	B�-��/�|�%�������p��X�t�l��O�����T
	��3��W���S��1w��p�t��p���at��dn�,9���O�;fG+������a���07}<��;]ns3g���L�����bXXZ��+a��.��/;�.���}��/x���\p�����������G����a��W��1��V(4@)4@QIh�n��2��-�9�y����K�����L�p����G����0;�3��jb�D8}j!|���������!����	?������snv)S<�R4 ���@=mY���g7�q���K���+����>�jx�����.���������ZZ+�ia�3�O��U
��Y�����b�/�/M�B�����@r;�!��?�=���^�_��3���L5.�Og��w,�
?��_J�X��)2�O��{��L �h������������3a�����K������?�Ko��g�	?x�L9�������>�e�X����*��Jm����m��w\XP �_���m���k�H�ST.����=E%?9���O��	B�-

��~t:�����o|�Dx�����U�_�%�q����[/	�\[�����:��ZL 3�s��R���K'��vO��Uq|�����������F���<�,yq�V�\<V�W�B,04^.�2�W�7R|7;y&�}t)�����q\SD��"Rh����L:�Y��en��������������bG������t�������>q��*��g~��w%S!������7oK�6N,p��@��@, plz�<��]�X�x�`j:�y�;�=������H�j���}#�v�G��~��������ZR�|��|R �Wu��@��B����bA����J1�r���a2�<���o�1g��������2E������/$S�����=�����3�b-.����?E'��?�2��"��������~!��>���&sW�Y�S��[o�.�k�5���{��s�����g�=���.�oZ	��}�@j/��p�\L �2W),���+9���W�((��W�o�\SD���r�5E$�)*���$��~

4HBoQh`��_���	������}�t8�2��lKa�b���_���z���R8|$)$P."p�<�O��t,�e����7�I
	�
$�vO����;�}�;<}v�Dr��l��^���s����9>6�������r� vp�	���i����av�o?�����H������k.���'��.����=E��E%�=���K������e���;�/�N��z��cGi�P8{�D�����������j>evf����n�����������_������"���+�z0|����]���~��0;yIrK�&f���zyG�������L�_�g��[Z73�tA���h��J�"|���x��R<�:_H [T`<l�����w�����_}_���z��W^qY�@@�]\0�r�������K����2�"���a���A��4��^��T+���a!��}!<uz�}�������B��#�L�X��)2�O��{��n�������_���x�������k�'>~W2�q�-���@��m[;&Kmb���O����a����;���M�g��r!��aj���������17�ZP�����nC�_�������\pM�{�J~r0�%��B
��[���>����t�:�B�}�P�}�����W����0_j��g����O,�����._W�����^��_��;_H��8L�
��7NT�������|!��q�������[���u�D��>fKm���0{�L��=ff����R�[
ane$L���C.���X�alt$l��(���J��R���V/�<z�h��?��d�������k�����)"qO)4@QIh&�s��?�w_Kg����0������&�����BX����&�N��o�+ox[x�����Ll��i^�����e��
����^=�"�?{�����kok<��cp:\��kG�~W��T��^8_@ -������T~�l�������
��R��GG�����aeq6�����P�V_YH�K�O������'wX}?������W���#����������"�j-���a�>q�D9bn'v����,�&��=�:�x�;����dN�v,�
����p��;�9��Bk�L�St�������n?>p�A�����/�o?�d�:6����������n��w]2�}��W2�V,,.���iA���=)*�cr!����cK�a�\����|,&P_:�,Q:�OL%n<_\`����A�Pt�5E$�)*\SD�����Lr����@�!��C��;�F?;������7�\�N�%Lo�S���m��p��������?���`�����3n���i���jA�s�aZP &�{Y�\[.$p���d�f�R�b�l�N�������a��t��9fgf���B�]Zs��a.�����07��/b��h�c|l8���&E6�M��������Q�0~��N������J������Z{��
�������)"qO)4@QIh&�s��#_�l81V��>������n���0>��Ow��������������?��
����������wm{>���v|��4��?\��~s�|���m�X86�p��@��@���b��R�Z	����-+���������sb)l_S�69���=_ ���I��xZL ,������/�#aix�2>4R�������?��M%��A�+1��m1��-��/��7�E����/.|��D2U��[g���T2E�������=��\&�5��2����0t�G����_�\���k����O$S����E~���r��RH`�RX`r!l�4�G�?������gj�h�6�z��@�&��[+��_~AA���JmWo��]pM�{��������'�\b�)4� A��x����c�=����$s*z��p��w����+������?��ny.���/�������#�����}#��R�����,�%����������p�H�x���3��7*�b;7���
�u�lx��������M[g�����}l6������03;f�Jm%�������0?������p4q�F�r���6�bG����ib2�o*�-S���jl�x���������p�t���S���dj2����'S.����=E��E%�=���K���C��s.���?^x��03�3������+G���V6�������/}�m������B[�z���G~~�k/�_����^�����7�w[2��F�R��B�����\/��DH��ph�4�I������B�<R�-�����K�/���R��ayh8,������<\����q��J�/�T��[�=�c�t[y�J1���p�4���H���Ej���m�]��<���7��fx��C����0=r�g����p�������=��'�K#������=��\&����e6mvf&���K����{.{��:LLN&����_�:�LU��Y����w����.�Z���R��@����c'��a��b��i�\X`t��y��[^��O?���J1���^~S����x'���"���)"qOQ�O&���Sh�A��~�����|��Dv����>����|b�h�:�L��_�����W�C�w����{�f85yErK����n~������-<u�O�_�g��������J!�rA�s�"������Y/�6t6L
�.
��o�Gf�����sr9L�/�x��r�3jae8)�eC����R�0=�����0>6V.�l���R0`�T�PL��/��x�9r$|��_M�V5�O�~��aw��]pM�{�H��JB{0u;�(�I���\�#_��p"�U�Z����}��Z�������a���S�Z�_��cc�����K�z���r����p��_��\���	_���J�dFJ�n���xa��"��8\���r[�J���p"/O��'�7Q�74��z��?���W��_����������x�L//�%��J��7��\(�Pc�V$�Z�v[??t�i�XZ����fN	�;v���^F�t�k�}�"���g����'~'�(�~|h�~�������W��n�+��3�v$sWM�����7�-����Uf6�w���a|�}���������������x�������a��B��i�4�;&��P��������a��wV
\vc�~s.����=E��k�H�ST���I.��h� ���d��w��L�g���=������lT2�>���������'�:~�{�����x�M�ky������?����_��5��^=6[."p��L��@RP ��hCC+a[X-0��a8����-�W�������022��F���D�`@�������zb�������K-����0^z7m��&&�h,P.�-�o�^�7YN���B��:t(<x0��X����}����{��U.����=E��E%�=������������V�{.���?�:�Z2u�Z��k�^K+�������������������y�v:3���,��3������+
g�y��K�����0;�~l���;n\}M[}���>�B8zf,��?���r��������@,$P�}.L����*����r�~z�:^�"��x��Hf<��8�x�!�I�(��������U�e���E�"S��>@���N��t;�����V��������|����X�������C��t2U�/~��02�b2�>���p�7�_9�;�����@�M,����cX��������������;�u�5E$�)*\SD�����Lr����@�!�������bn������&SkW����&��{����[v������'�&�-����7*��
O��t���v0|f������?�[c����U/�yx����w��=L�](XX�\�
��C,p:)P��+�����c�abl%�������07�%��LD��^��w�3l��+l�����tnw9r$<�����c������+�~��a����k�H�SD

PT�����D�L�Y��2����3;���R��������������{>��p�5S��U�����+�J��x�@, 0[��:�(M����
�a<,������a�����8,M����x:1�p�B��}SyQ+9�(���<v�y�����q|du�\ s�Z ��~����v�������-�P�)��q���p�)Eg���?E'��?�}g��<K�Z1��2������t��s{��.�������������P��X���g�j*:9{"\w�-������Y����>�x2������3��k��$S�������\pM�{�J~r0�%��B
����~8<��������|����
��2�[��w',��[�����O�&�����BX��_��V��n\�����n��#?
���p�
��S�W$s[�}����3G��8�����X.�
��X8`��j���sarl)l--?6�F6+�%�����?��5��������C�Tm{��
���K��s�5E$�)"�(*	����\�\&���s��|b����j�^K��_�z�����������T�;���z<,�_�g���lX�������n��?H����+���������m�����}VJ�uih$��/�	����G/������W~���K�S��F� ���������t�8N�p�)Eg���?E'��?��K���C��_f��L3a8�U�c��h~r���pnrW2u�4�X+��s�t��'�Q2�����sX\>�L���l���4V���c��u~u�u�xx������#Y�y�����7��[o
�?���9�������\pM�{�J~r0U�n���<�$�W��]/���'�{)�c2��|;�L��W���}�=�Z���b�_wcW.���N�S4!nG�=��I�z�����<���6�-	��������,_[�@�m�l�U
��3aj8)$���[������02:R.�x@m#a��I{bh���
����0Q��9�eK���-LN���fx~v2����|h��[f���T2E�9v�X8|�p��8q"��j�������*�]���'b��������$�S��?��'Z����fec��r�1w���'S��i5o�����L�oy��aqn&,/����x��|8z��pr��gU������+���^���yq<�����HX
�"����(/3J��<t���rx���Q��0�la�t|xx8Y�A�"S��>@���N��T�n��o�n?>4+���/���������6��+;�	q����z�r��5�g���|>����T�+Ka���r�����Be<ohl"M�	��_������@����P?��������7�M�\SD���r�5E$�)*���T-��K9�~��@�!�(_5��J��?�x���������
������wq��%���k-����O~���������Z���z���v��b8yf!L'�SI;y6?���w���0q����Z��j���,'��GG���f���lZ
�FB�	������d��2&�������&&&�J(���o�?����T�}����[��L��������L���-�O��F;L���"��B���`��=7~��(�~|hF��2��w�M89�#��P��W���v������O��/�W��kMw�����������N��M�n��}�5��w����E�"S��>@���N��T���m�n?>4cP�e�>�����r?�z������;�������d�bs���?���#��}#�����<��q���0���0������R��<>|Iiz���Ef��7�+�|4|����<����r��~?\�����O����;\SD���r�5E$�)*���$��~

4H��������n�zLf?��������+>�����^k���e�����_��o�����T�j%�k)mv8uv�|��l���p@����r�8
K��z�����������Q��C+�ae��>E#a%l.���01>Z��{brK�l�&���q^�����?�f"�j^�=�y�l����J�(\SD��"Rh����L�����6������\��>�o�������ba�x�]�����?��t����j����=z4����i2�~��
w���LQ.2��������=��\&4n�e��o�Z8=yi�9��+;^�z���wb;w.L�}�����������~<�:y*,�#a6���>�F��S�����J�	�
,�J���qE��@, pi,(P).���������l	?��Z�"/}Mj�6o=�?�4y6l���M�&\SD���r�5E$�)*���T�;l'�E��@�!�(�����>�L�/_q�[���~+��n"3��o�ej��)�^���>N�mK��'M�G�K+��i�������
�����q|-�a!l:Wj3�aH��6\���4osyz6L�,��bY��+���?���t���������McabbS�m�[���X8`�����*��?��o��#�������K��O}���)���������$�S�<�F�W�����l��[.svf&��/})�*�n�\��]��o�_|��{izz�~�"�j��v�������W]uU9gYm=��z<x0:t(�j^�=��wo��o_2E������Pd�����������4������m?�������0=yi2��|~�U�^���bX���)�/��l�?^j�bq8�p:Y�����
���#�\S."P)(�k������������������pj�����m�y5���	;g�m�����k���2�\pM�{��������'�\b�)4� AH?���C=����dj}�������]w��Lm�'��Xx��k�T�����t�b�j�����{/N���L������k����[����rq�X`k,���������sarx.lY�����RY
���al��:
������84G�ai:�[*M������p���g���o��x�D���%Ln�V. �-�������~����C�c_H�V5����.\���d��p�5E$�)"�(*	��T���F������l��[.������o}+��P�H���V��`H/��^��k]�_m��x:��f=��saqq�<�/���������3�T����%S����|<��h\dJ��(2�O��{��jy���%v�����-B������/[�e6{�V7�_����054l��J��������o���\@ ((��K�b���a�����v����(��p����]w���q��o���_���_������A��k�H�ST.����=E%?9��}�oG���h� ��<�������N�Kh�������>�afbgy�K���x5�n�W\G�}�Y{��:��k��L�/.�?�������_�o�����z���N�����S��hx�����p�8�biX.P��$Q�_�^2Rz�7���������0�e�4�&K����������N���/��N��q&��Z�p��B��#�L�(\SD��"Rh����L�����<������\�����C/��LC��z�ZO?��ps�{_����do��zY?8r�H��W��L�j6^?�����w'S��L):�E&�):y��!�	���\����
�#���^��e�ki6?2�����0<<\."0���0���������K�%�c��o�s�������pj���������o	���$K��6�F�}������a���a�M{����E�����������\pM�{�J~r0�%��B
���V����o�G���pr|���X�m��u�����}���'�	'W�������r����0�����a~ts�$�.�����4\-�/ ���[?_t�����'�N�lO��o���p�m����3�C����"��B���`�v.������\f�b�X�����L]�V�����f�k=�������*�t<�p?{[���G��^86��������[f���T25x���@t���p���d�bi���}����{�&S��L):�E&�):y��Q�;M���Q�����~�e��o�z8=�+���V����E/����'���.���f��7���;�
ayh$��n	��6�x6l*�����������0����L��)"qOQ���"����`�Kl?�$�7����������t8�Z[��u���
�z�ZY���p���&&7_P( ��(��A����/.|��D2U��[g��������������$�S�s��~|hT����	��K_J�h���b�s�����|Q�z
T[&��f��������l2�~�������$S��_
DG�	O<�D8v�X2�~�v�
��~{��{w2��r�)Eg���?E'��?�2�1E����N��^�~�7M?��O��������������?��^�A2w���7����c7��0����t�5E$�)*\SD�����Lr��7�z�+�~7[_��o7���*�[����zFF���-[��9������o{������������| |��?��?~�g6|��|<�{���}�{_���;�;���p�M7�k��&���'l��M�Mb���y:�R����d����x{\N���MLN�����T���_}���N��������\����ER�v�3���������'~"����
�~���9�����
������
7�P�X+��b���+�(_�}�%��;v�����y��rN/^��J����Q.����,�w����S1oc�q�x?E�^��~���v�v?����%c�!�������;���;���������q:������B������	s]�����w;�>(��.�����
����Q�����T��'>~��������x��@��8?���5PK7s�Y���j�[�Iv���j����7'KT�;�y���%c�'���t*�jN|���Y\�"��a��}����3�{�8?-0����~���!����������3��7�f��������k�w�����}#Y�5����}��K�����X�qE��@�i��z���L���#�Z[��s+I�n^���dz��^�Mc�a��D��m[���K���M���~�o���d�~02�)L]��rQ�8������oM�.V+��jNr�.�/�/��~�;�������Ok����k��r�}�C�gv��m��8?���%����J�]9��z>p�l��w�����!�������[��������=�����}��d����$��q}q�~�C+��|��Z��KH/{����<�L�����*�fc���
��2�9/>�tx�[�J��#�nV�����W��pX������0<<�J-��0Rj�a:�|[i<m�tFq<o~~>=z4�j���w��1Uv�k��6���oN��(��od.������\f5�|�������x|N��w�n������:���K����oK���~�P83~I2���cn�?�]�s�_�vxmtO2u�t����w��a��w&S4J��}�}mG��^�~|hT��e��ym�����7�-����o��ph�=���f�e��������)�J~�;���r(@�?�x2��N��z2��Z�|@�+�+	����p��l��i)����N�];��U��K/
{��	�]vY���+��W^.������k��W].������k�������2l��'l�qI��m[���
[�l	���abb"l��)�������022R���^��\�;����^E��[��j>��O�����N�0�V��udsx���ZE���&c��9���k����o��S����t��������+2P<��/s��W������_H��6���o=�?�Z����y�z~q=q}�B�����sY2��Z	�V��6����	�g�;g_��^��_�-���K��hw2}��t���0���0����i�%a��-Ul�X`bx9�jN|���;��]��]K2��Z9�Vs���?������K�+�"^v}�s��\�z���I|�S��
W,��A;����������_f������K��������e��p��~?l�y5�{��y��������z����J�=�
�Z�KH/��o����d*��������+�Z_��=�P����������	��K_J��'���Lj��?>����L]��������d���^su�w��{�K�9����&S��}�?�����w'S�=��%�e�o�1���G��[��X� f�\W����\��O�2�n��'����v�@x�����#��9���t*�~�;��{�L���7��fx��C����0=�-��j��t�r�xx��{��[������V�a�t���QE��Y+����&[�����C��t2U�������������]w$sk���7�-�N�����0z�;��@7�%��p2
����Xm�e��sc:{NLN�������j}��!�H"�W��7��;�~_9���m����"Q,
�o��djU��O��������`��\�z>��n���09{�f�����Zn�����-�����u�x�{�]�p��(	��O�R�y�l2�>q�x�~/2����S����t����w�G������P��8?����`*Z��Z���V�q��:������b��m���a��7��������m���������\\>�O�`
��#{S ��c^Bz]6n�~{�}���|�+�����#�����x������Z�c;�:��{��s����3;����7&�?��_N�z��#G�O<�;�����]�������=����n?>4#������#_�l81V������=�Z���s�O|�s��g&�������S�Tk��?�'�Lh\?�2[���h��^q�����"�����d�q�/
?��0���a��g����0_j��g��R���0~��a���F����LK.��h� �=�����H����lB��{�
�>�hy|#|{����^:�L]������u{����+�����?�:�Z2�*��z��L����C�%S����7���/���Q���F�,�����~�e�������g���6�l����nb�t^Z���av����/�M����n���uw2wU/\���0�}%��<&w��/�2��mJnz�\&4���e�}.>�te�;%}��d��z��\8>���L�nei!��>���[w���K���Xr+�K��:��t

4H���	��������+�����w���A���������������X/y��F/���N�M%SK�G����d�F:v�X8|�p��8q"��j�������*�]�v%s��t;�(�I?��\f�fgf�/�N��z��#
gO�;�\�\}u����,X�]�������E.���2O�]\����2�K��������~|	h���Sh�A��~���z��f��F���/}�7����d���<�KX��=U�zj-����O~�����O5���AK�///����0;;&&&���dNn��U-gs	������n?�2��\&4�_s������W��^k�-������dh��t��'?�LEU-�����p�&D�|������^K>���/�����}d?(����r�����\�����.o��@�|�S�\N�7".�7H{cQ�-[��]�v����~�e����2���l�?eV�}+?��_	{��:��?&�N&�^(���������j!�T-��%�_��7&�?��������<�����{��dv������nm(Y�1�����t81�-�[[�+�||8���0[zf6�H�������ks�
{����N����\�\&��_s��H.������/���T2�����l�z}+ggf�/�N��z�����������dr+@�\b�)4� AH?{�����w7v!�����'�����?��av|{2��$u��u�	�^��_2�G�s�r���~�e��Lh^���|��������},�a�o�R�I.��h� ������>xQu��|U�^��>N�ooj��X8�����h����]UoB���@=��K�����{.�_�eBk�_�����T�#����{���@G�%��B
����>p�@x���91�}�=�����?��{>�?��������0?R=	=�86-�
S�.	����%�:M�\h�~�e��Lh�~��y��Ca����l���]�i��B�����������B.��h� ��~���������y��7����^��:�s�~ �	d�!'�W�%��B
�@=t���\&���o8���BP 

@�(4���BP 

@�(4���BP 

@�(4���BP 

@�(4���w~��p�}�����Z�����'K�����0�1^c��5k�u�������������Y���b���iw���8��})���f��w������q���f����8lV�����s�q4����8?�_�c��������^�-��Vb��1m��Z��1�H��[/��c%�L>�bl��HL����V�ur�K��j��4�|�sz�Z�������Zw���:�n�-O��R'���\7�c�����8Zk�q�c>����3�Vb����N������Y�����

�/Y�c���+��{o��0��2qY�(��f����z��z�-.��N��b����A��Z�Tk��~��'���tr�C=�[\�Q����x�K���8��5�;�n��K���C�9g��{�P��sa��d�����dL�_���>3���Q�:���y�\7�Qo���z5��|�'f}t^��s#u�����c��:u�u���y����Fc���|�'�*�s������N�;�n�����-.�(�|zA<����LG���\7r���l� ����\��|c#4�����~yL[#'��\7�Vm�h��,��e��Pl�$��<�w����)�~=.wr���q��X��X������:n�n?>�spM����j}'���k�����['c���Fhu��A�k&�b�7�:�k���)�F�(�zc�g@���:n�n?>�szU'�[:yn���N�_��O'���\7��h�V��d���r'�M1��q���f������B��r'�ME����x$�;����xR��:�Z� �a>	���V������;�L���v��b�[���k-���N��b�C����T��|��u�\���f�U��|<���g���d�[�u�������u3x�qPO�f�k,wr����~�m�n?>U�s�x��/��l���x>Po��_��_�%1f��V+��:��\7D���x�3�V��X�Zc$1f����Z,���j�����2q^-�\7�Q-��1��Z,����d���z�m�x��?������/���~�v�����c���}����]�-�����E'�M�U;f��?n������m����uS�b(q�jq����N�;�n_����s��ZqVM\O�>�U[w~�z���\7�'��oV��r'�������
6H�����wr�?��AK'5����	�z���O.S��7�/��uSl��"mk�X^���tC>.���|����s�Q���8����������_���fp��?���������%����s����v��u������3�������z�/��|<�m���j:��:��@������Q��!�b���H~�v�Z���s�C��X+�R�O������3`�d_��m�n?>Q~�s�A/��yK'�-���I��I[��#U���9���o��)�|\���8Z�X��O�j$��Fc(�ll�<.��o������ml�b=/�~�����\7������u^�	B�x������		tB������||��E/��X��2�>�}����)�|le[�8��=�,��'���Ov�z>O��]Ov�v������3�.�^�9��m��=��9^��5�;�n.�}�������PD���98�"��<����B7�c+�j�+ge�����.g����D�������s��Q���������N�����r�8����z���3�����Y����_���9����-�e�}n�]�y��?fg[=�H'��>OhU3��z��]�����r�^7���cg�]~��s�����{��s�L�k,wr�\(�����x$ac�?d����A�Fh�0+{��N����V�z��e�[.K��Z�{����#��c���g�dc��s�t���[�� �����j�[5�\7����������2�-���_,��c��x�W�����K�5��k�����q� ��h�������bL��s�����dLwr�[;��(w��w=��!G��g�~q��&�Ll���a#e�a�8���}�g�`��ni�H�~|(�f�7�l�l��������\7�V-N���b=/�|l��������z���~c)�j�A'���\7����������2�-���_,��c��x�W�����K�5����p�E�Y�=�X2Vq���'ck�������x ����]+��d��t������{�I�*~��dlU'�M�=����X%�����d�>��^���9��gC�;Wx��G���gE'�u�G�N��{o2�:�|�%�����E�����7������q� :p�@2VQ��m3�/t�}���������|%�S���4C��~��} ��A?iW|F�8��||�������~������~���{_��3��O�2�e2b|��i?��)4��|��z�����I����r������`=��N����_f��u�'��qO/�&��}������(Z�����1�n�Pd���x���z�k,�O:�98��S���/tS6���]b��f���O:�D>3�e��K������������^������~�:�2)�v�{�1�n�/��~B�Sh��*=��W�V�Uc�'����	v6��N��b���l���|�3�Xk�=�$�n�|��N��8��Uq?���f����O7����~�e�	@g8���_��V;�T#�C?��>�I�����8E�&���q�g@�8��Wl�y��
�.��~�:�2��/���_�*�	�F��g������?���q]-����a�/��\7���@�mv�����b�����/

�oq^=�1�<�qE;��I���&c��o_�����tSLd���~�e�	@g8gPU��U��p�|�z�s����]�x�G��R>3D���]�&�k�U}���Z�-�<�p�B'��I�U�N��'�i�2D�e�O�

=����XE+_����n�&��jq�L��j��Iur�OL��b<�REZ���������&�c��|l�y�x�Y/���s�Q�CL��V�b��������q�����t8t�g#�+����X�����98E��w���C=b��Ww2��/tB������`��.E��l��m�14���t���y���S1���d�������e2���d���Y��u�FPh6H>�O�yB
)�s�K�Z�a�wk�����zIm�u�����h*�u��M��N�b�g�������@g�;�����}�x|f0��o�����Z��t[��-���q��x�H���H����(4 &3���,	@�U>�c�C�*U�jl6q;��]�����?��Jj���1>��^O��V*��m����_�H���������Rt��Z^5>����y��2)�2A�Lz�B�@�X��T�� ���{��'k�N������&c���qO7����A����?����vv�i�N�w�����1���v������w��8���~�e�	@g8�W�	l�����n�����U����g���v)�j� �����(���F�[���s�-d��	�eR�eBg�k,�O�

@U��������#��N�sz]��^O����t*�����d��1�c'�l����6�O��|������7=����p� ����x�w�����Hs�Y1��o���v�^�_��tC3�-�������S�o�������4�=����/�nRh:d��(������G�?g(�z����$c���11��yL��r�d��c>���s�x�;��������^�||>3��K��u�^�y>����y�*�2�B�L�B�Lz�B�@R��n�VG��{,k�N���������W�9#��h��rQ~ig<u�\�yQ����cj����������1�A���l?����;��3�h�9���A#�C����W�x��]�����O�1��n�����i�eB�~��~��Y���z�B�f1���(�I��4Y�&����;9��y�����^����{��|�����w��}�������.E��_�4�[�-�I�eRT�eRT�e��zV�$�����wD�����J���N����O�


����A������)�T��_'�u�G�N�G��W�G�c>��_c�~����~��t�}�N��]�,��6��6���1�+������v���������������F������d��k,�O�w

}����Xm�e}�c#�U1��
����'�L_O'�
��U'�u�G��{��'�O~��?1�����\7@�9�U���/�N����A�3����Y���/P�q�lt������p�A���yK'�-����:y��yB/s��W��	��k,wr��)

=��$�c�=��U�B�u�ZQ���F�4��_������V�{zU�8������N��tr�O>&Z������X�����98�*���Zea�t2��/�������b|�u�^�|����v��t���y����|�'�"��/�"��D�����\7l�����C%c!<���XcT�����%��J��?��5�Z��N���c���?��o����^�=��_��S+��s�Q�*�|E�����s��3H�9���P�#ol�|��_T�G���A#�C�������.E����X/����c��}�������*k�|��o�/�~�=vF�|��/.������X�����J=_ �2�/����I���RM���|��d������u3X�{�H��g>��sU��Z�=�$���������	�z�I:y��<�Fd��Q�3y���:~:�3(�5��'���~��w�(~���z5�M'c��B���A?��]�,�}�������^����j:yn���z�ci#M�L�~��~���4�_c�~B�Y�!�%�7����[��:����L�>�)=�����x��{����-������������T>nj�Y$��e�x������g-�e�}��]�y��7������g'���\7�+{��7��~��N��e_��m�n?>QvsN���fl����y�z�A����/l�|����������/tR��@�>��/�X��v����T����36�q���hgl��[+F�|���
����_���9�,k����z�}n�]�y��?�s���c��Z��������}��]�1�^�?n�?Q���N�;�nW��Wo�G���\7��ni�5^�	B�x���N.�_�9�F�O�b�u��z���s�w�f�
Q>v����}��_3�Y������W�|d�����4�Th����(Z�hL4z��w�F���|3�f0e����_6~bkg���|���U��.������"r� ��sl��4z�`�����s_9A����z]�1��z���s�w�f�M1t���3`pd_��m�n?>Q~�[�x���Z����N�[8oa#�������Y�~���]��u3��;�����3�-����Fc���g��6������Y7�)�����[;����{m����v���`�!tG~��'���e-�tJ�A��L[K�-��e���@�e���uC���S���N�f'�M1T;������8�_.N��]>�O���V��}��n[��|\�X��:~f���j���{�����N����=��Ft2��u����~�m�n?>U~�sN?��1��1U[����j����M1������Z�����1�����Nhe�>3�1>�qc%�k��%��}�����x���Z:�n��Zl4���\�v���|����Z���g@��_��~
���PD��������s��Z�5��:o�����s��}���n1����^�g����/�8/�Sk��tr�C��b��Z��8]Kv��>��[=��i��l1N�qc%\\�����j��'������j:�nS�8���x��u�*���Fk��
��������^���Nh&�k���`X�5r���uC~?i��,��e���N��tr��f�'�zc����N������fb���������������PT��$16=g������&���['c����T>������zQ3��^[O��@��ur��Fcg��VgW����������PT�1�%q�u���y����F����|�'��S1���r'���k&~b�7�:y\���<�xi&:o��n*����f��"�����+�N�C��3����L\6�����>z��dj}q��|�:�nh����5C���������(Z�H����N�;�n���X�����98�$�f#�Q��z��/�N����^��gE��>�h�����V��_�1(�N�[8o��u�����V5C��Yo9�����T#1���r'�
y���\7tLRp�:��,���U:A.W������u��B�T��VZ����8��?���E+:�n�)�M6���#qO��1R-��tl��P���z�u������|i�����x[���t���nC6>�x+�5�;��"����m�n?>�����s�s�v����^�0��w2��/tJ���4F}f���J+�^k�q~��V���f�����c���+���������s�/{��z�:yn���N�q���f��1;u����|��3Cq:�Vb(]wv�����Vtr��4�c��P���cg��~[7�!q����\w��_��h�P�Sz!����P2��K�u;�(�	�C.�r��7��Ph
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@������044Tn?�p2�b��t���]�����-�{������(��Z/�]ol�"�8c3�=���"66^��h�N���+b�2�������y�en����N�\7kSh����W����p��w&c{���������d���duLj>��l{*������)Z_�Nul����E�q����w�]�q�����{�I�h�N�!c���:�������~���_&@q)4@[�;0�������1;��1b|����?Z��u�?����<��6N���ig�j�"�o�e�I�e�BtL�j���8|�����m�6Z�V'��N���5�9��aw�m��7���4���<����1�9�H����_&

�V�=�X2��|�����?�o1y���}���������E9�z!
h?�2���NQh���V����{����se�|�_����I����v��$���E�q8p �x����!��Cvr���~��e��N�;�n��������V��9N���Y���m���'c�5Sa���8�^"g	t�B�M��������v��w&c�#�	�9K���!�:���P������-������{������/?��������-�����������[\o+���#�nS������Z�:��&����������%����;;//{[���������eok�����[��5}������:����?^�8���Y�vd���7]7@/���H[/�8�����H�'{�x �[�����l�(�]W|�Y�6e����mYO�������������=N�
�yy���=�?�_�}�����ckD���^��um���nKl����>��_m��Um�����V��#��_������V��Z>*{{l�n�z�{��}���S�'���!;//{[���eok����+����J+�x��dc���_�n��[�!�%�7�"y������������z���{o�>kit���Z������<�r���������k�<N�m�-;������s��V�����������^��Y��E������F�!�V��g�>7R��W5��h��S|����j�z��g�Vo�j-�u5����4������>N�>�z�Q�6f[�z�7���b"�L=�������E��X���{��h@?��������Vo���N1_��s���it���Z�j>*.��F�/�zs^��D������Z�k���Z^���iw,f������6'��H�|���}6�����)V��Uy�����X�7�'V��U����Mq�z�%}��������8P~���{���X���Xl�����k$�Ri���hL��i$g���|L#��4GV��4���}�T|�����{�I���F�b#�e��_#�����n�4������-��7���W^|���_�����Xl4�Xo�4#�9��	��P�6��S�x"��%�$&���y�=�����z(�X6!/&�'.����d�yiB���s��;�������GM�*���O�U��V��������?�����-Yk�^�����h����������d�B�m���A\.�Z�z���s�����e���F���^��w�N�bV��)Y�^�j�y���U�1����D�L���%��V����5���������-w�y��9�z��K�c�������F^�h�e���e'��x���<d6�k����������XL��{���R�����!j6��U�����8����T_������E�lO>�S���#U��e��ugu*��{m�������~��^r�Pzi@|��
���{�����Q_O�\j�������>F�z����������z���o�����H�Z���m��|����s[�1���j�����������[v��jid������\�y����m�}1>N���%��z�'jf�A��,�m#u���M6��^^%��c���5#�Q��[|N�=V������}:����?����F��Z���{��}��������z_��������y�����j�E�cq��c[�q��km{�1jmC�-�|�`Pu�s��������8����T���Fe#mk�c��<�������z����[��R-�T�=\�1��_6���zc �ZY��e�F��/[�c1�l��{�����%��z�'jt`�?c�5���*_	7+[�4�����#����l���U=�������*�v�z������Y�����t�_�����P�jj��W|����q�
���x��*�����%/V����[�}�j$zM�s��\P��Y��b��Z��Z���jm�Z�R-w���Vn,J�py��^���g:����������Qo2?���ok$:M���[o{�����Qr����X��>���7��_&�+�-���%���'��%������4mG�����������������}������;�����p���Z��T������X�z������������N��^�qV���G���V��n�zsW�m�w���u�l$��{�H.�u3cg�z^�zs�����u�e�{��F����_���w{��R�_��eg���_&�^

��|R�VE�T�I�FTK���|R1���k�>��|�����$c��t�z;�F���nE7c��}+#���y��XYY)�T�v���^�q�s/�Q-��{�\U�4�=�
���%����f����<T+�a'��^��X��=l�n��#�6��h~������Ki4W��-��m�4�=��e�YvN7c���6#���y�X�/h'�hY��f��X�v�����h��(�t��snD��SOb3&�6��cL>n�f������l��(���j������������g~���L=���j9�nid{�� ��77�b�V�p��[��J�x��3�<����r~�����r����F����nO=�2�,;����_&0��e�V��'�:�<�?�v%M���a�	�Z��������n�b'*���Yc��CCC���:���Wr���J��u��}����k��s���9�f
�/Eu3;��k������b�3�o���[z%gu�/\/�D��47%g��n��~���Rh��e���vlW���v]H�����v$EI:����b�2�����/c2�����E{���b����4��v�M���Wr����>jW��vk�k�H������8��f����A���	x�_7K��i�3���@7�J�2��py�/S��5�e��	��B4$&�b�*��bR+{��	����LltOL`������ul�=kW��������i�;���6�k�8[���v�Ms�W�����
d�xI;���Rt�$9��&g�z���M��,&c���u�V�3��~�
��&��4��:%^;Xf;V�C��Xb������D1�b�\�!gI=���M���hUbc2{�m����b���bG��br����XL�i���j%��vHjl�$c��)^��l~3v�Ls���sO����������E-�
��H�2�,{�~���!w�y��VlY��g���]��I[+��3hj��=1�U-����i�:&/c��/c��A��T�$�cR;���z9��[��u�m���;�����"X|��s���|���
z,�b,�q��V��Z����_fw��_��e�z,����/�

��l����b��m����]��I[;=��c�X���"Q�&q��u�|���L��OQb1��c���5�:�S��8�����s0����I�|R�����e|t�m�"�b����Z��f^���[��(}��R+?$g�q���~�_&�	

�6�_8���$V��b3����N}����L��b����^���m�b1����w�}ahh(�[[L��c��u;�Y�3c39�^���F/��oK>��e�j�,�S����X�1��7ck$�;�t�~����~�r���_�~�@�)4@��������M��}�T�"�F:
���$m/%���X��T�������j	�F;
�A���>������lG^'��F�b��V��Zb���F��v��w�������j�.��j������)��fl�nG�z�	�V�b�R��U���)g�>�2��/h�B�E#<�}�{^���D\��k5>�`2�����Q�nO~[��R�d���N��,_D���$t#���O�_�J�����]�]M�=�sv�l�F�g{�m�g>��dlm��U\#E�hPb�Z^��<m��tB��,�A���N����l�A�E�2�^��M�7Q�H��vx��G��U1��^���d���c�T������=k%F���j�p��(�^�5���(2�O�R�5�S��X�W���V�����I���(�^�q�u�A��|L��Z'�j9�nK�g-��e���:��������V���<d>�_�v�X�oG�<mT-N��!@����=q{��{��-��e�YVtj��e��	���JI2N��e��(���QL���^���K;6���Ft&�������9d+��������??l��a�����m������j��Q~����d5k=V~[���j�%G�sL���F��u7��oD,���m_�u��E��TO�4��@��v�����n?>@?��3�Z�s�|L�kj���zy����]=���f��-���k�P������e��X����N�!��A������q���t:[��j�^m[������m��T�j�l������%�s���$��Y��y�z�J��z�"�,�N�[��U��S`�?��v�/N���%�7�������{�=�\�(�����96��}�����C=t���t������a=��N��T{�����Z�����v6�x��;{-����}����^�Fc/����`�T�L�H�~|�~P��R6/��y�V�1���Q#9�fe�[���z^�f_�x�j�g-�.��N�!��f[��0�L-���V���������Z����g�F�����z�����j��Ni%�R������������5�O��k$�U�rk�d�����:���4"�����l6Vb�g;`�T�L�5��+��+[A3[]��b���9�P�����s�)>�z�)n�z�X���r���_�X��;�L�vV|�������X�:��hPb1>��2�PV�^���b��g��4������F��q[��+�e�������}���<�z�i�_�j� �b{��������/�{�1�m��+���/�^����	t�B���$�F]��i1�q��k�o�I�~�,�)>���������	�x�j	��q�mq�lr2�<�@x������J��m�{��d�9�\wjb1&�������U-�����q����U�6�9��f����[�#�}�����_���q{����9�A���
�y���V���v��Z�b-�e�G�7J�CY�lK+�� �,�N�;5��_&�MC����d�:

%c���tJ�P/�O�$aL(����K�����K����L�D�L��%��~��(��@�Qh
D�(��@�Qh
dh�$�CCC��*/!���\�\&P�L��%��p2
@�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@�Qh
D�(��@���w���twa���:��];l��R�$�`���.�[���B%�5QQ"�U��A�w+�JU�[�����T��J�HK�Bj"`M��}��Z���������@@����{��{g�;������?��|V�����33g���{�y`C4���������<5��}��.p|��p����5����/��f��aNS��H�����!�w��?���5V���3&3f�0����X&0�x�����S|��X��g����i���.�b���@�A����\7[z����`��{�v����O����n��X�m=��c�1��9�Yo�9;;+��E,�9�L�n��L,�M����Yo���J,�9�L�n��L,�M����Yo��`.�� �����b�xE �(�E����&��]��\,�7I�m�0�1��>m�0���+b��!b���X&������e��eB{b�l���F"`���y#o���\p��������4=|�pw������"8�D:�g�3T�0���M���	fK'�YO,�����2��e���~�2�=�L��@p���������������|�b>>�����b��Or����_�}�������������O����||F3q����`���>[|r�����<�;���4��G:U]������������}���}�c����hf�zS'��\X6��Kb��'���X�6�e�#�	���$�	yb���en�Xf?b�l������b�NNN��+���|��}i��|v������_�����^��d����
��~�wo��.>]��E5^��/�]�y�^~c����{��c��
#�����o����^�v�������_�{���w���������O��JG�lR�g�}�����`v*����y��.�����gw_��W�O�{�+^�{���{�;��{��^W|Jj�z������Po�X�X&�6�e^�lF,s{�2�#��b���X�L� �yI,������X�v�eG,`=���L�?P��_��������~�����`v���2�l�s�NOO��K�m���6�������/����\���X&��u���"��$(�^K�F�N�Y�o�.��
q��i���'v?�3?���'?Y���X&��Qu����b�}�l`^b�W�2�ib������a�e%��+b��4��a�en�X���2�2
@����/��������|���t��lt9}��X'���o�X|r<�E�t~~^�5��,��q��������������x����[�'��:�������O(E���5	8��^������F���/|��|��_��_*>i����������_|Bi�zS��[�n=@��r�y�e��e�%��q�e7��q�eb�yb�pI,sb��M,sb�l��������w����������+>�z�;���/�+�B�����G.�:&�����=z����|��*�1ze�X�<9�)>���������J�\>
x��5�}��GS�u�?&��������D�����W���4�?t��.���w����v��j9j[���;�d�����jp��54
�5��z��f������s��]>��!�eb�Ew�A����R�oz��@�������e���W./�y��2�2i/��b���7��_�LX&��zb�����2��X�X&��e�e�4O������?5T���FO���n��r��j�����e0 ��V.�����e�@�>q�����A�&iv�t?�W*�=��*������o����O������e"�Hkm��WZ����!F��n�i���v��nb�������zvH�L��e���C�)��<?�!�YO,S,�L�L����?�e^Yc�)�e���e���D,S,S}�B,S,�`�6���sO���$PUJ���"�cR}�=??/��K@m_�c�����>����D@�P�]�����6L������8��������6bn_�������#M�i�H�m�Q>KQw��}}�l�?�q��?��k#��iE�k��z�*�F����<L,S,s��2/�e��X�%�L`hb���e�en�X�%�L���$�	�3���������GW#�VG�m��n�i���o]�4����b���C��?
fG>�x��N�y5�������"�t�#�4��iV�����D`!������F�m�.���n�yzzZ��C��[������G������"�5I���:�O��`i�j*�}�bD�j����}���	R7=V.�������O���G>r������T�2��,b���e�en�X��L��xc��&���X&��f�2�2�J,��X&M�e^���h�M{�?���q%��������f�},��xA>�:*l�@R<��l�Cn�������_tO	��4�t���VT�m�_|��J��D�q�t����/?��/���z����4���i�HZn�����jyo:Zg)q1E�O��l[��}�c���u��2��������Z�7���2���W�Xfwb��2f,�"�I���u"�X�?�%�����2���W�Xfwb���]������PG,��z�3������n17�1��C5�[�/'��Um�i�7^���������/���!e�q��|.����t�b����wiP;F�m���}��P����W�����U�k���Cu"�V�T���v��>#�2��~v�?�~�s�+��C��"���X�X�����_�~^�@L�&�2�So���e6'�)��E�e^'&Cb���70���Ce�!1QJ��O�wv���3�_���O���a��'O�=(�!|��q�#���v�;����^��W
�F �����w��./���x�hW���U��lW�a�&iG�e�<^��=p_�U5=�����\���)k~F�����}�����������
w�6���O�}��[m��?���O��-��2�������4��(��^���{��:������Y�w�����D���w�����{���\|RoM���t;m����K�{�����;�����)�e^�\���D,��7������O��-�s�d�2�X����e��;�8����v���X�{o�i�}���\����e�U��M]��X�t�2�oG,��X���)���D����������>P\f?��2�_��/�;���G����:� R�Q���=�8/c� Gp��O���G���6y�dd��X��W��������[N0;��q���������#�!Q#W���Tz�H�pq
(��U��GP�P}��O��� �=���������#�!�
�[�2���/�9��<�����V����*��rb������.sZ����X�ub��%�9-��� �9-�L�������?S�-Gf0��|��~1Rq_� ���y1w]��<V@:��b�@�����>F���v�1��}�gm�&
~���T� �R���_��k������/����H�b]�����v�y6��s�A��6ggg��v���L�i��7�^b����\��y�e��X�<�2���e�C,S,s-�2�!��nb�����h���H��0^�b��sKC�T\
�F08�hMG���Z���B�A����BX/��}�c�4"�j�{hi����dj�?K1�I��nI���������R-�C����������Rc�qk1����jlSl���#o�������X�X�Z�e�C,��Xfwq���L8~b����\��y�e^��.�W["�	�3����7}c17��W���loC�5�o{c�|OG��}���t���"�2������z����@{l#������i��O�����uyI��6�^�C�:_J�
�������.@�ox���y��c�bM����/�So`��2�!�yI,s��2�!��n[�e.�X&?��y�e^�\>��y�e��X�<�2`z`r?r�M��r<s2\Xi��������:����y1w)
���s�N�s/��x9���S/��D� �.�Nn$��nG@=�L�U��Z17�k5y�j��K���7�j1wX�����W�
��XW��`��jk��w���cJ�|�;����Po���e�C,��X��-2�9$��A�e^�&��p�2���e�C,��X��i�9��u���X&L�@L��}�7������AFr����q93���Z���1R����m�}�����~n��]�]{��#��	0�"������uL��)��8�����H���v]!�57RpW����L����������/���A�����Lk!����?����W���/>9,�cU:�f��P�f�)�[�����k��-o�}��|O��\���c#G=�|oj�����Xf=��e�Xf���RymX�5B,�ik�e���o^L,3�Q����N�+�L���i�e��\&�2�%��4��~�U��a�������<y X���:�(~)Y�^?�?�����������������o[��:D��:Zm���P�F ��V?���V���uK�>D����4�UM�n#�#^���������[��[Mo�g���Bw_���l��_���q�2���T�x��������~��
���,�u��D�=;;����QWwr�:���]���u��}�C�}���������C�QM��o}��=�y������4���f�&w���b��0,����������s���>b��>C�����W{��<g��L�:!���?����>�������;T�4�2��7M���������|��{�K,���1���@�������X���u����2���^�>�;&��N�e�#�Yo
���4�6��5���g�~�)��Mz������K��sEu�H3�>V�\*G������/����$�����D��%�z������?�R:�.��Ki<�yU��b�h��5�:g����zU��>���F� v)��4�����xG1E��)������w^����
�\b���e�b���2���j��/����e�6�ev#��V�e����Xf=�L��@l�[���w?�Co-~��:��iF�����{�����OE� F&�1�X���4p�F�V�OS�l�w�L}���^��j���?^�4�W���i�YZ7�k@yMm�em���`H�9M�M,����^�������w?
�;��;/�^�5�`9�2���g�e6��%��|b���2����;M,3O,�C,���{�XfsiZb��'�YO,�:b���2�?
�y?�����������K}F��W����4�Y�*[�����/��j�B��A�6���>�4�b�������>�+��Wv/������K�|�
���4�r���=|����,�i�J�|*W���c��\Z�������z�2�������m�o�������������u[z��M,���=K,��4�b������2�'�YO,�:w|���6L��+b��mb����Yb��������3��'�9=��zb����'�	�h�����w�eW����U�E���]���[W#��x.�#��yA������������j�M����2��}+���(��tT���C�#�VE��z�_���������R������J�����_/~Z��V�u�%U
��Cu"W�OOO���U�\���T�K~t�]��]�g������I�����^��L���w���i��Po�e�l���%�)�y��2��2�'��K,�ib�@_b��U���2�2��Xf�X&��e��e@?��.`����_��{�]��^�����U)�O���b��H#�����os�A�R�m��n��r����"��������Fi ~�mv}���D���bD�4`��;����k���9������^���b��R����*p�Rp��]�W1�n�i�4`\jZ_�?6��NT�P���|���<i})����c[i0;<x�����������������4��O���?�����5�'�a��X�}���'��4��zb��G,��X&u�2��e}�e�'�YO,���e����Xf=�L���q]Dc��������s����>u-��O�~��������za_�x��#��{�����/����x��gs��Cb�ggg/�/��`T��C��(������>��-������}������VO����������+��%��������N4�Nn9T�i�����#�H�w������o���O����>���/���%��}�'�)���%/Jb��E,�:�L���N,`;��%��}�'�)���%/Jb��E,�:�L���N,`;���|�"��L����;w�����{�7~C����w�L,{��.��85�m�h�������HG��m�I��Lm�1�=N������������^����{���R����w�L,{����+��(�M����l��\�c��&uN]G�c������W����O���2������
�b����C�������;�}�M��e�hs�A|e9�2�[CL&�1�O�:���C,��5�`�2����x�o�kwh�����������r�e^���L�c��&uN]�X�uk�7�T'�
����.��W��v���_����r��^���{2}�������I��s���Ey�h�MG�-E�������#�/�7n���m��.�����W���$c?�������s���O������������?����v���������}���u���q��G�=U'�`t��>e�L�����o��4>����^|���������_��W]L��-�r���������?��;�(�	�=b��cpmb����X��r?���2�A,���'��!b���7��~.�	PO,���e�������>b����H��\�����X&��e�������X&@=���h�%�hB�\`
�2�5K�3���h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
���<~�������b��)��b�@Wb���&��3���h6�@�!�
9y�D1�g���
0�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!'��(�`p�=������b������Uw�����}{w������7�O�3��l���?�;99yi�����w�������a5�����g�|�)���nr�:���oS8i9��mE�~-�������E}����?1��O���������Y*esu����uJ��P��:���N��Q���m���������0�X��{��]0�d �����[�.���0.
lH4�+�7M��@�>�l1w���
"��cLe�d.�N��
N`q��vI��C�=*>��E���������S~{.L%�sQu�`�����}��X7�s��6"��zzZ��w���b�����s�����q`Wt 8����s�^��gh�X5�[�!^���U�s���b�R<D����L�%���$[0F�8�M
�0<
l@4��v���7o�,~����>[�]j�qm�"��6�@o��):�D�.�
G�g�����y�:�]�tPc	��l�c��C1�;w�\L�>X���}"���;��N?Q�s��Cv��S
�n������y�80�U�=zt���<(>�+�����u��Jr�����*�E_��4��U._>|�h@��|6)]�	}�����Q/W��z
t������*}nJ��:VU]7�;==�>��R)���]��>���	�����o�{Yl3�%M�}���;��q���l�o�{dh':���i��<S�����-Ui���h����#L4���v�7n\���r��/�9��>D�h�[�u"�9��D��S4>�):T��*�T�1�T*�w��'�LtHtI���t���v�:�����D�S����xA��b�"�b���w�&�=1���[Flg��)
�hl�vt���/����8;;{i��T�~�8�j���Et �q��h4Z��tV�*��=�)-c�������
������O�oaN��p9u��c�o��Y*esZ��o�����q0���u1�(�:J�<����B�'�f��c]�W<���=9u��
���]�7���}?��T�O��A2��Xs
��u��C��C��N����{����
��$�K����}j#������������T4�K�j�������^���>�^|�P����7��u��[���os����K���}�����s�i�!��Q��p�r����2P�~s��o
s2���:;;+�.5m�����^L��R��1�r�^��g�e����O>��s�C�w�^1w�|�����:�����/�����9X���w��]��}���0'
�h\�v��5fo"��u��N�i�����q�o ��\��}37�����
��{���n��G�sta��#�k\�e@�����?m x���b������
DC5�����/��'''�)~S�2iZ�4�mW�n���\��(;�eb���t�\�J�%�aTS�tb�Z�"�2��4���S��4�y_��������3�t?bj�C��;'����Ne���5v��m7��]�r�Gl;�?]��X��}�3��>����*�D�ly]���t�1���!������|��QNC�i���6���)occ��trM������Q���[v�|�cn�~n��2��e�V6b?r�T���s�l���r����>v����L7~�U.���}Z6��4_r�t��Rn����~e�������!��G�����osW��M�������y������|���*_��V�cdz����Z�C��w��4si���.;������>��k9�:��5>��W�����2U�����N����o�������)w�m��+E�i~�e���r��?!M��y�S���s����"��������h�1G'Nku��~���>|�T:!��~v��������S�}m#����C*M?�6�����S�����d�]�1�U*]&w>�l�i:�}�'�M�84�y���4r�s��e�]����1���t�\Yh�LN���)��{b�m��T���;�m�a��u����W�����;K�����7���i���6O�4f��;�4d^�����s�W���Xv�r���Sn����zm���1��[���r��o�r���H���|�������N���r�����g�}��oN�����,W�6���NU����>U�9��Vi����n/�>�]��s�m�a:�9��v�����^���U�F�<���6e��t���O��Z�3�f_�y�[o_YI��Z�c�4�}����s�������4���w������������k�s
�<��qTr�r�~�cggg������_��t�����m�}�	�L���&���s�,�'b�>�#������MQ���}��!���n�o!q~s��O�r�T�Z����b���oAkS'ri�����.y���9�v���!����R��ZZ}\c,�}�m���i���|����:�F]�:���\���~��,����5�<GC\�=�P�H�q�(�s�2���������z<�����T�eKs�s��\5D~����|���m��r���9���������Qn�YJ��}�z=���8�.�0D�������Fy�:���,Iy�m�!����7��D�5�N������s|�?)
�\'��7os�D'���e����i1w)����xH���������b���������X9�6����Nt�)������mY����S�}Li9�c��i�)�q^�6�.u-m��o��Ct��u��/u9Cl�o��R�W[K��k��C���4O�X�����>��.S7�4]nN}�z�rq��/�c?�B�C���������:p5��>m������]��uu�_��o�s[C��R������g[s���D>��-��k�����3w��m�e�����g���6�t��������R��F�m��.n�u������>r�;���L�1G%Niu��~��v����R>,>�R�}�m��t�Jo�H;�^�P��R������S�o�>w�wu���P>���N�6����������59]�	]�3~��SN��4�c�����&y��S�^l��fL�y�-S�m�1dY�N]��+M���t���y���C9����;��y.���}�m��]��}�X��6��q��]�7159�t�}�?��O?�)����������V�<4�N�^u��F]�Fz���T����<����[7>+�����mol������2���s�8sri4���C�e��t���5�|���|.�6������������c�����N�uKm��N����$�r��Sy~R����:d�y�)M#���fU������f��~n��m3~�[/���������>��n���-?���t(���DL��?~���K�,_�~��[��b�C"���1�����������l���6�}m�.��9��}��Z^R��}�2~�O��������Sn�rjRv"���1�����������l��}�Q�S���['�,��y�����-���c�-�~���3�m?>�m?>�����o��e#�.r��$��s��T�������6s�U�4��lT?��Cn*�}b��m��$�X
�\��>
c�}�@���]���Xr�,�cu�+���~�N���������SNm�������59]�	m��.ocjr|��?$�Nu:��\���i^u���Cu ���������.�/_�ecjr�C�<49}�_�[��cj{����&��o�C���k�|:��>�������/?cj�Fh�^.O��&�>����<��M�sP�������s�d���o"w���m�i��#w~��Sn��t�l�=?]�m��6s��:���.��������\*�����u���]��:�W!��[���M�7���d_����C�g����v�������4��W�c:�~�vc:���cn�^��n���s_�m��T,y�5�"���Y�������4�Qn���v�������[�:u����������i��������
u�\N��H��z��}3�#�����SNm�]?��ahu�i���G���G���7ns�=����?=-~w���������_�]y�����ux���v�n�*~�n�9��|���_�t����q��r�<�������9������"����X�.?��tq���h�{p�Q?c�T�r�T�����s�r���{��s�ry���E}kZ?s��C������wM.�������nK�_m-�>��.����N�����>����U�����/�,����e��Z�tuu���7�u(w��\�r"�x�(-�9`��������w���w��~a�k^�17�n�����\�|rr�x����v��%Pn�,����w����*��{�{���CY�qn����.L�,UUw-���f�q������#�1��i��p-���5�h�S40�u��u����ur����v��,�0�ic�R,�kPe�m#�:�j��zh��i�h>�~ui=D�.�(�k ��qz�CeK�z�++];��a����%��5����}}��r��NCY�(�M�8��]���;gm�7���Xs]]h#�(w�]:���}����s<�!�~�:�+w��-����w���b����!�����D�����I����r�������!u�
]��K|_���s���K����f�M���:�g�5�1�e��#�����!�}���"��o<4�����NX�I#�8�h����v��G��o�NCm��>�Ru��i$�t��\��+G���^�r
���1VG�����:�"�&�}�<w-�� <�J�s1�%����\�^�t�k���>�����!CY����\��;�E��9���q���C�s��o���.�j���9Ky�yg�w��u"Wry?�%��Jc_�r���1GY��ZE9�N�]����Kk(�}���!��Qn����*#SZ�qn��q)�R�\���T����N�qS^�������X�.�~�$U6i�
G��R,����[7PR��(W�4��}�n�r������}Z��u����������h�M��H#�v�������y]�������,�~������:8��/W�r�v�O����<�c�#k��N1��]��X�vCt��J�q�N�c����r�h���!��[�m����9J�[l������7vy���.r��:�6�+.��
�SSn�Qn�^Sq�sy�������!-�8������J�2������g�)�Gm������D����
���k���F��4��n�28t��)���rru:���N��i��}i�f�����*;������=c9����T�����Pr<�����\��6����:�F]:ju1���Z�r��y�:<�������~�i+��.�!o��1�e�6��mG�}���5�L��6���m���g�c��r;�5��!�'g����Mu��/M�,5���k�m��Y�}�e2� p�����M�x3
G��hD:D��>������C]���T������Pw�u��|�[Z��l�_�Vl3yG���^��x�����wK��������1��TG�{��su9:����4f���X��M��k(}�w
�:F]�k�|��9����x�B��]���s���������q�S���������}���F� �u�pvvV��v�r;��#��u�
9k�_�����S���m-1����t}�K��K��l8bkh��!�������8��������'���l��!�+Kn�[���S���6
�����N�Au����
�c��8���iG�!����N9����������u����!e]�<puFY���Ax�W]E�/�]����6�&�0�s���;m�]���������o�0��oK�[S�����Y��9��P�����g�x~�4���"���w^s��1�>�:p4p$�n<�%��?:��������
���� �l����k��O�h'W7r��6��v�i�F��u��SP��#^��6���P���i��Nu���j2�@Ul����8v�u.5������z����l���6S���ak�~}����^XW��|��r��T�6��}�����?N��_�K��1������`!���e}X��M�\��������5I�M�6�SPtPn�~4t����=�7��=k��s����������yH�c�k����1Z��N\{��_�=h�w�m;==-��[���r0��{k��"ha��X��&��@���KC�hl������G����7�<s���b�R�sN��z�o��F������������������f�H#��2�@t�[z�@�.�������bn\�o�w?�:fNU��Pn��{'��/r�$����r����g1�Mo��{'��@0�h��v�LE��h�_m�St���Q�k�����4�N�~��\mD�kH�b
���^��3m;���}�i����5�%��%��"�H3�.hR���
�#P/�gy-j��V�}�;���N����T���?F������x%W�����v����u���
9My]�m+�<��C��.��y��A�6�}<�x�3@w8�NiKm���hY�`?��
��sO4�?�F�k5D}��������t�i_��C��vP�5���Q�(=WC�T�	/��W������Bc�c�_sYj\��/�S�����������m�A!g�r���e��7K�y����(�]������Y17�~���^��J�]o��{��w�C1�2g��� �Xc*�g���������
�7V���a���h�Ej�Q����)�=�GI�$����)D��1t������A�Kc�������Nx9i'��������X�t��}����r�'���C!'���� #��v�t-�����>�tUW~�{G�)�#w}�������.����Pn������{�{=�q,��m�������u����������N�}`���+��8�3���Xs��c�f�.��;:�t�������\��-4��5�.�1-�u����m�����������{1�U�y�����ONN.����X�H����`�y���W���.�-s��uzzZ�4=u�T�g���h�����}���P���b��5�)�"���y�V���&r��n����:&����9�s�=?t�,��eid��Kk-�}���kn�{�����!��1�Trq���H����z��������uy�LL�@Gl
��C������kC��t��R�����Y1�^����y�1t�F�!��
�o��u�8:���v��6=���}gWa��GZ};�A������\���i]��S
������+]::�����>�;�.��Kq
�u��y)�z��y���<��k�@9C=7m��/w��Z��xvY�8�]�bJ���5���5,��k���7��0�Y��c�zt�1��;by\�uj�w����>��9���(��<`?
��
�Y��n�o����~4L�uN�JC��1t4N���~�u���!�P����c�m�k�5�^B�b�kZk�����uN�J!'�
�]�q�{�u�����������.�,���\�S?�t�����
y�r������ky�C�Sd��B�|������Y������&���o���!�/���1�Tr���^���9�'�k���Qu��%����@G$��niDrr���[s��2��pnS~{��_�89��i$��&��>V���i�?�08m����1�����(?�N._B]������F�K���_�Zs����W��m��-?FySG��;wm:?F����TW������x����F����ir���9�w��+�C�]�����Az��~ur��myns�[��sx��f�vu����R����\�3��c��"���kL%��s��i��u?������9q.re0�u��pL4pD4�]����m��5��Z�N�C�5���h5'�894m$��u����t�r��r{�a����X
�c���7-suu���f]��h@���PwmXR�J�k:K��s��>|X�]�z���I��\'�C�4t����;wM�P���2�U���w�uu����w]8t����s<��Q7��i�r��������7�\�kZ��z�9Q��O�{s\���m�������^n��m�Y��G~�q�eKz~l��C��C�x��J�����z�w�e(���se���P����r��p�@G$��w���CI���.���Qf�@�P���N9S�}�}�}-�.
J�*�V��W��&�d���m���c���UM���N
cu�
���Q��e.u��*u
���*W��m3�yI*������u��c�m;:��Wuen�z��P'w��e��EY�c_y�n;���i��F]���U���V*�c�ua���9�w�����:����r���\���E��w�i��9��E������kQ����qu���������w�[�c�k��}����I7s<?��G�vY�������eS�S���W�5��S������.�S��E�2��:x�Q�s���8��i��������gc�m/�l.>|j�)���r�/�����e�lL}��K�8�T�L���;���K'�^n�RM�S����Bn�.������&�<��-�����C�~u�������6�������%��C��.Ss��%��&�<���r�/������!�[LS��!����������6����~�����i:�wc�[.r��I�8T'M��4����w�����UF���R�s\������<�.��aW��
q<M�il��r�/��������sH�i�7�����t�]��9���8rit=�M(��*����������"w����:�t�����8KM���)W�������$��n�:M]����r�z�Z��������5Y������s�p��G%�
��
@Ku~~^�]��7�����R|sS9���s'ZFf��+��c���[����&e�.��hr���il���n��Q�=��7��������}3d[qu�����u��vc������5�%��&�|�{��-LY��r���>�����o�m��a-u!�q���>������!u�c��\�����������u�[�>�+�m���4C���(��,�}�����I;M�s�����f��>e0�}�1�}��K��G~������wm�w/)Ey�s�p4pdr
��p;m<8W#�9��n�{,�U��ij�9�({S���
��*�S����������;��
��5��B��.u5���K��r�m�6��9D����`y�����.U�u���{_l;:�t)oa�2W��:BUY�������-Q�����n��e��1W�>q�S����n+����m�����e���S':���\�8==-��;;;+�����%�����4�)���*�m�?�{m���8���
YW������v��P����O��r9��c�kHz��#��������,5��s�&���.�^g��@���P��p�
��{��b�J]��c�$�������b*���+s�J����T�!g�A/���q���������f4����;����\����r�Uw�r�7�6�!�u�Z����6��~�Q-{��5�=�k|�<�+K9C���>*�����K]�}iykR��,s�u��R*>��?]*�e9�eb�CK�9w�!~_��P�?���j~�o��1Eu����.y<du��c>��;������=t>����~]E���)�=w��qG�L��<��|�u]�r��r[�w�z�s��q|����Cy
i��1�P��F�u��y�>�����Rc��KMk���]����	`<'���G������
���8�����:kD��h4��D��[��x������t�X����gn`
��#Y��x�A�=zt1mn�r�8��
`��sd�of�����6&\�7/,��7�9� ���X��"�@ ���)>�� �1���:==-�.��!hN��KU� RB����L}�8�v<���O���c>te��#
��o3J;��)�����os��
�[�`�L����������-
����b���{���y���Ff/���IDAT�� ��g�-~`��d����{�������_�p������1x/}h����y��o5�N�sK;��"�>�oU��0�d���m�F�m�An��U�t����@G������{��s�������A������h�S�W�U��tt�~�����9�I�X"�RN�s:�@�}�v1�h`���_�]��s9??/�.�!�|q-�F�1�6�O%
�`81Hc.z=~����G*b,k)�\�%��>�l�tg��
�F��o���s��
#�X�\#�*
�`x1���[5�_����h`#���w��-��S�@'T�u�Ac������i�C��������?
lH�b|���?_�4���� ��.���7�����������
@;1xcws`,'��(��#�L�?��
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l��`C4b��
��h6�@�!�
1�l�n��S>�1
��IEND�B`�
v31-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v31-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 6b8b92c57ad03a121f34972fb9a95170c1a1902b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v31 1/4] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/optimizer/path/equivclass.c | 435 +++++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  53 +++
 src/include/nodes/pathnodes.h           |  82 +++++
 src/include/optimizer/paths.h           |   9 +-
 src/tools/pgindent/typedefs.list        |   2 +
 10 files changed, 565 insertions(+), 130 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b92e2a0fc9f..d6a0dd524c4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7828,14 +7828,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, rel->relids);
+	while ((em = eclass_child_member_iterator_next(&it)))
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7846,6 +7845,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7899,9 +7899,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7cafaca33c5..aebf809a645 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,11 +33,15 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										EquivalenceMember *parent,
-										Oid datatype);
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -68,6 +72,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(EquivalenceChildMemberIterator *it,
+										  PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -372,8 +380,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -389,8 +397,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -421,10 +429,10 @@ 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,
-							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +518,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +538,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +559,30 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * 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(),
+ * 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,
+					 JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -616,7 +649,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +665,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +686,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -689,14 +723,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -760,19 +794,20 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +834,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +877,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -854,9 +891,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_WINDOWFUNCS |
 							   PVC_INCLUDE_PLACEHOLDERS);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -900,6 +937,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -938,7 +976,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1563,7 +1601,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1574,10 +1613,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, join_relids);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1593,6 +1631,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1610,6 +1649,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1684,6 +1724,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1691,7 +1732,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2404,6 +2445,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2525,8 +2567,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2597,8 +2639,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2695,6 +2737,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2707,7 +2750,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2720,15 +2762,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2741,8 +2777,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2758,6 +2794,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2787,9 +2824,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members = lappend(child_rel->eclass_child_members,
+														  child_em);
 
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2818,6 +2857,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2839,7 +2879,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2852,15 +2891,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2869,8 +2902,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2885,6 +2918,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2915,9 +2950,38 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * Update the corresponding inverted indexes.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * EquivalenceChildMemberIterator. Since the iterator
+					 * needs EquivalenceMembers whose em_relids has child
+					 * relations, skipping the update of this inverted index
+					 * allows for faster iteration.
+					 */
+					if (root->top_parent_relid_array[j] == 0)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2966,12 +3030,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -2986,6 +3050,205 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   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;
+
+	/*
+	 * 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;
+
+	/*
+	 * 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.
+	 */
+
+	/*
+	 * 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] == 0)
+			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);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * setup_eclass_child_member_iterator().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_ec != ec)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3040,6 +3303,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3061,15 +3325,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3084,8 +3347,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3303,8 +3566,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6e2051efc65..d19b45a1293 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3739,7 +3739,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3756,7 +3756,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3776,9 +3777,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_child_member_iterator(&it, root, pathkey->pk_eclass,
+										   index->rel->relids);
+		while ((member = eclass_child_member_iterator_next(&it)))
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3813,6 +3815,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 816a2b2a576..abb05991186 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1284,7 +1288,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1328,7 +1332,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1469,7 +1473,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1500,7 +1504,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1982,7 +1986,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2196,7 +2200,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2220,7 +2224,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2289,7 +2293,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4555,7 +4559,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4564,7 +4569,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4589,7 +4595,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6239,7 +6245,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6306,7 +6312,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6334,7 +6340,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6350,7 +6356,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6421,7 +6427,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6430,7 +6437,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6456,8 +6463,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6466,7 +6474,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6825,7 +6833,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6888,7 +6897,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 17e51cd75d7..734d54a7305 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..a07bca8431b 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,15 +119,21 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +154,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +202,28 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * find_relids_top_parents() can early find all of the given Relids
+		 * are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +277,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +673,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +791,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +979,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1542,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 00c700cc3e7..6bdac3bad2c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -208,6 +208,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -264,6 +266,21 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -976,6 +993,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1395,6 +1423,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1469,8 +1503,56 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* 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 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, root, ec, rel);
+ * while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_child_member_iterator(&it);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 46955d128f0..2214c97d3ec 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -137,7 +137,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -179,6 +180,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   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 List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9a3bee93dec..a9a03b619d9 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -689,7 +689,9 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v31-0002-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v31-0002-Resolve-conflict-with-commit-66c0185.patchDownload
From f145ef33bd40507148a31ba0a7c5d084c2d928c0 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v31 2/4] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 46 ++++++++++++++++++++-----
 src/backend/optimizer/util/inherit.c    |  8 +++--
 src/backend/optimizer/util/relnode.c    |  8 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 51 insertions(+), 13 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index aebf809a645..26834953a89 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2976,7 +2976,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					 * relations, skipping the update of this inverted index
 					 * allows for faster iteration.
 					 */
-					if (root->top_parent_relid_array[j] == 0)
+					if (root->top_parent_relid_array[j] == -1)
 						continue;
 					indexes->joinrel_indexes =
 						bms_add_member(indexes->joinrel_indexes,
@@ -3013,7 +3013,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3030,12 +3032,40 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		child_rel->eclass_child_members =
+			lappend(child_rel->eclass_child_members, child_em);
+
+		/*
+		 * Make an UNION parent-child relationship between parent_em and
+		 * child_rel->relid. We record this relationship in
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3121,7 +3151,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 		/*
 		 * If this relation is a parent, we don't have to do anything.
 		 */
-		if (root->top_parent_relid_array[i] == 0)
+		if (root->top_parent_relid_array[i] == -1)
 			continue;
 
 		/*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 734d54a7305..08cd39a6c85 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index a07bca8431b..c8e535617b8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6bdac3bad2c..645c5898d4d 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -276,7 +276,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v31-0003-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v31-0003-Introduce-indexes-for-RestrictInfo.patchDownload
From aae7c79f5d8ab3c3946b09760acb6dbb421154e5 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v31 3/4] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 515 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c |  49 +-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/restrictinfo.c |   5 +
 src/include/nodes/pathnodes.h             |  51 ++-
 src/include/optimizer/paths.h             |  21 +-
 10 files changed, 583 insertions(+), 71 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 73d78617009..cc62be7221e 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 26834953a89..305a23cc16a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
 											   Expr *expr, Relids relids,
 											   JoinDomain *jdomain,
 											   Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -318,7 +322,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -329,6 +332,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -349,8 +354,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -362,10 +369,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -376,13 +382,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_parent_eq_member(ec1, item2, item2_relids,
 								   jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -393,13 +400,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_parent_eq_member(ec2, item1, item1_relids,
 								   jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -410,6 +418,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -419,8 +429,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -442,6 +452,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -583,6 +595,167 @@ add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+					 Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -704,8 +877,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1063,7 +1236,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1156,6 +1329,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1165,9 +1339,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1225,9 +1399,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1237,7 +1411,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1303,7 +1478,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1360,11 +1535,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1405,11 +1581,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1777,12 +1953,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1794,12 +1974,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1861,10 +2041,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1875,9 +2056,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1888,9 +2072,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1963,7 +2151,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2689,16 +2877,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3486,7 +3677,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3574,7 +3765,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3731,3 +3922,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b33fc671775..e47952a5533 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -36,9 +36,10 @@
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
 static void remove_rel_from_query(PlannerInfo *root, int relid,
 								  SpecialJoinInfo *sjinfo);
-static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
+static void remove_rel_from_restrictinfo(PlannerInfo *root,
+										 RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -445,7 +446,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 			 * that any such PHV is safe (and updated its ph_eval_at), so we
 			 * can just drop those references.
 			 */
-			remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+			remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 
 			/*
 			 * Cross-check that the clause itself does not reference the
@@ -474,7 +475,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			bms_is_member(ojrelid, ec->ec_relids))
-			remove_rel_from_eclass(ec, relid, ojrelid);
+			remove_rel_from_eclass(root, ec, relid, ojrelid);
 	}
 
 	/*
@@ -547,19 +548,28 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
  * we have to also clean up the sub-clauses.
  */
 static void
-remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
+remove_rel_from_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo, int relid,
+							 int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(root, rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -584,14 +594,14 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 				{
 					RestrictInfo *rinfo2 = lfirst_node(RestrictInfo, lc2);
 
-					remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+					remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 				}
 			}
 			else
 			{
 				RestrictInfo *rinfo2 = castNode(RestrictInfo, orarg);
 
-				remove_rel_from_restrictinfo(rinfo2, relid, ojrelid);
+				remove_rel_from_restrictinfo(root, rinfo2, relid, ojrelid);
 			}
 		}
 	}
@@ -607,9 +617,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = bms_del_member(ec->ec_relids, relid);
@@ -636,11 +648,12 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
-		remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+		remove_rel_from_restrictinfo(root, rinfo, relid, ojrelid);
 	}
 
 	/*
@@ -648,7 +661,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7b1a8a0a9f1..017f7de75ac 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -664,6 +664,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 5d9225e9909..aadf4c88e52 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1153,6 +1153,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 5b3dc0d8653..1e543f705a0 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -512,6 +512,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..980ab4d8bed 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,9 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
 	return restrictinfo;
 }
 
@@ -394,6 +397,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 645c5898d4d..238b25d157b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -346,6 +346,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1429,6 +1435,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1447,8 +1471,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1543,14 +1569,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2700,7 +2732,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2818,6 +2855,10 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 2214c97d3ec..f9349d655e1 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -129,6 +129,8 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(PlannerInfo *root, RestrictInfo *rinfo,
+								 Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -165,7 +167,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -201,6 +204,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v31-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v31-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From b5b28330556bca820f95c7cbdba4d079270a7db5 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v31 4/4] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  18 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 197 insertions(+), 175 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..21c2593e959 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_next(). If you
+ * change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 305a23cc16a..173449080f0 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1953,16 +1953,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec, nominal_join_relids, true,
+								false);
+	while ((restrictinfo = eclass_rinfo_iterator_next(&iter)) != 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) &&
@@ -1970,6 +1968,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2041,11 +2040,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2056,12 +2054,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								true, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2071,14 +2068,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								false, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2088,6 +2084,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3924,179 +3921,166 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator
+ *	  Setup a RestrictInfoIterator 'iter' so that it can iterate over
+ *	  RestrictInfos. We iterate through root->eq_sources if 'is_source' is
+ *	  true, or root->eq_derives otherwise. The members must be from 'ec' and
+ *	  satisfy "bms_is_subset(relids, rinfo->clause_relids)" if 'is_strict' is
+ *	  true, or "bms_overlap(relids, rinfo->clause_relids)" otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
+							EquivalenceClass *ec, Relids relids,
+							bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator(). NULL is returned if there are no
+ *	  members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
+
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 238b25d157b..8b4c8319aad 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1564,6 +1564,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index f9349d655e1..07525dca66d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -204,18 +204,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+extern void setup_eclass_rinfo_iterator(RestrictInfoIterator *iter,
+										PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+										Relids relids,
+										bool is_source,
+										bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index a9a03b619d9..9e4a4481a7a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2479,6 +2479,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

#101Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#100)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Feb 12, 2025 at 8:29 AM Yuya Watari <watari.yuya@gmail.com> wrote:

Compared to previous versions, v31 reduces code size while maintaining
(or slightly improving) high speedups and low memory consumption. I
would greatly appreciate any feedback, reviews or further suggestions.

I noticed that the patches did not apply to the current HEAD, so I
have rebased them.

--
Best regards,
Yuya Watari

Attachments:

v32-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v32-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 1ef1e412e203deb2121c8a81caa40272f2f07f10 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v32 1/4] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/optimizer/path/equivclass.c | 435 +++++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  53 +++
 src/include/nodes/pathnodes.h           |  82 +++++
 src/include/optimizer/paths.h           |   9 +-
 src/tools/pgindent/typedefs.list        |   2 +
 10 files changed, 565 insertions(+), 130 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b92e2a0fc9f..22586f6fce9 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7828,14 +7828,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, rel->relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7846,6 +7845,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7899,9 +7899,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..70d80f272d1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,11 +33,15 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										EquivalenceMember *parent,
-										Oid datatype);
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -68,6 +72,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(EquivalenceChildMemberIterator *it,
+										  PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -372,8 +380,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -389,8 +397,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -421,10 +429,10 @@ 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,
-							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +518,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +538,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +559,30 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * 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(),
+ * 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,
+					 JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -616,7 +649,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +665,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +686,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -689,14 +723,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -760,19 +794,20 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +834,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +877,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -855,9 +892,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -901,6 +938,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -939,7 +977,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1564,7 +1602,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1575,10 +1614,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, join_relids);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1632,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1611,6 +1650,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1685,6 +1725,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1692,7 +1733,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2405,6 +2446,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2526,8 +2568,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2598,8 +2640,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2696,6 +2738,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2708,7 +2751,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,15 +2763,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2742,8 +2778,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2759,6 +2795,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2788,9 +2825,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members =
+					lappend(child_rel->eclass_child_members, child_em);
 
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2819,6 +2858,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2840,7 +2880,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,15 +2892,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2870,8 +2903,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2886,6 +2919,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2916,9 +2951,38 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * Update the corresponding inverted indexes.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * EquivalenceChildMemberIterator. Since the iterator
+					 * needs EquivalenceMembers whose em_relids has child
+					 * relations, skipping the update of this inverted index
+					 * allows for faster iteration.
+					 */
+					if (root->top_parent_relid_array[j] == 0)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2967,12 +3031,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -2987,6 +3051,205 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   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;
+
+	/*
+	 * 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;
+
+	/*
+	 * 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.
+	 */
+
+	/*
+	 * 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] == 0)
+			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);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * setup_eclass_child_member_iterator().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_ec != ec)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3041,6 +3304,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3062,15 +3326,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3085,8 +3348,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3304,8 +3567,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index a43ca16d683..30e443d1554 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3739,7 +3739,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3756,7 +3756,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3776,9 +3777,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_child_member_iterator(&it, root, pathkey->pk_eclass,
+										   index->rel->relids);
+		while ((member = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3813,6 +3815,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 816a2b2a576..abb05991186 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1284,7 +1288,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1328,7 +1332,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1469,7 +1473,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1500,7 +1504,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1982,7 +1986,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2196,7 +2200,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2220,7 +2224,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2289,7 +2293,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4555,7 +4559,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4564,7 +4569,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4589,7 +4595,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6239,7 +6245,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6306,7 +6312,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6334,7 +6340,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6350,7 +6356,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6421,7 +6427,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6430,7 +6437,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6456,8 +6463,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6466,7 +6474,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6825,7 +6833,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6888,7 +6897,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 17e51cd75d7..734d54a7305 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..9c689fa2cd5 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,15 +119,21 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +154,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +202,28 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * setup_eclass_child_member_iterator() can early find all of the
+		 * given Relids are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +277,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +673,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +791,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +979,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1542,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index fbf05322c75..75db449966c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -213,6 +213,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -269,6 +271,21 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -981,6 +998,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1400,6 +1428,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1474,8 +1508,56 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* 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 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, root, ec, rel);
+ * while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_child_member_iterator(&it);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..ef832963604 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -182,6 +183,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   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 List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index bce4214503d..bc8283f6cd4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -689,7 +689,9 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v32-0002-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v32-0002-Resolve-conflict-with-commit-66c0185.patchDownload
From 318e73e803ad61ab9242117827e6837bd0e46690 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v32 2/4] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 46 ++++++++++++++++++++-----
 src/backend/optimizer/util/inherit.c    |  8 +++--
 src/backend/optimizer/util/relnode.c    |  8 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 51 insertions(+), 13 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 70d80f272d1..3415c91a912 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2977,7 +2977,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					 * relations, skipping the update of this inverted index
 					 * allows for faster iteration.
 					 */
-					if (root->top_parent_relid_array[j] == 0)
+					if (root->top_parent_relid_array[j] == -1)
 						continue;
 					indexes->joinrel_indexes =
 						bms_add_member(indexes->joinrel_indexes,
@@ -3014,7 +3014,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3031,12 +3033,40 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		child_rel->eclass_child_members =
+			lappend(child_rel->eclass_child_members, child_em);
+
+		/*
+		 * Make an UNION parent-child relationship between parent_em and
+		 * child_rel->relid. We record this relationship in
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3122,7 +3152,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 		/*
 		 * If this relation is a parent, we don't have to do anything.
 		 */
-		if (root->top_parent_relid_array[i] == 0)
+		if (root->top_parent_relid_array[i] == -1)
 			continue;
 
 		/*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 734d54a7305..08cd39a6c85 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 9c689fa2cd5..ff25cd86448 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 75db449966c..7b8088e0eec 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -281,7 +281,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v32-0003-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v32-0003-Introduce-indexes-for-RestrictInfo.patchDownload
From ffb1fe52ea6319e31322857e915f297edb5418d8 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v32 3/4] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 514 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c | 131 +++++-
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/restrictinfo.c |   7 +
 src/backend/rewrite/rewriteManip.c        |   5 +-
 src/include/nodes/pathnodes.h             |  54 ++-
 src/include/optimizer/paths.h             |  20 +-
 11 files changed, 665 insertions(+), 79 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 73d78617009..cc62be7221e 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 3415c91a912..73a4710522a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
 											   Expr *expr, Relids relids,
 											   JoinDomain *jdomain,
 											   Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -318,7 +322,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -329,6 +332,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -349,8 +354,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -362,10 +369,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -376,13 +382,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_parent_eq_member(ec1, item2, item2_relids,
 								   jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -393,13 +400,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_parent_eq_member(ec2, item1, item1_relids,
 								   jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -410,6 +418,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -419,8 +429,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -442,6 +452,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -583,6 +595,166 @@ add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -704,8 +876,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1064,7 +1236,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1157,6 +1329,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1166,9 +1339,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1226,9 +1399,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1238,7 +1411,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1304,7 +1478,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1361,11 +1535,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1406,11 +1581,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1778,12 +1953,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1795,12 +1974,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1862,10 +2041,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1876,9 +2056,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1889,9 +2072,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1964,7 +2151,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2690,16 +2877,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3487,7 +3677,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3575,7 +3765,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3732,3 +3922,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 3aa04d0d4e1..a3008d0fdea 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -58,7 +58,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   int relid, int ojrelid, int subst);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
@@ -455,7 +455,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			(sjinfo == NULL || bms_is_member(ojrelid, ec->ec_relids)))
-			remove_rel_from_eclass(ec, relid, ojrelid, subst);
+			remove_rel_from_eclass(root, ec, relid, ojrelid, subst);
 	}
 
 	/*
@@ -622,17 +622,25 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 static void
 remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -680,9 +688,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid, int subst)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec, int relid,
+					   int ojrelid, int subst)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, relid, subst);
@@ -709,9 +719,10 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid, int subst)
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (ojrelid == -1)
 			ChangeVarNodes((Node *) rinfo, relid, subst, 0);
@@ -724,7 +735,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid, int subst)
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1478,10 +1489,12 @@ is_innerrel_unique_for(PlannerInfo *root,
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
@@ -1518,17 +1531,50 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * index.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+			Assert(bms_is_member(i, indexes->derive_indexes));
+			indexes->derive_indexes =
+				bms_del_member(indexes->derive_indexes, i);
+		}
+
+		/*
+		 * Can't delete the element because we would need to rebuild all the
+		 * eq_derives indexes. But set NULL to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach_node(RestrictInfo, rinfo, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1539,8 +1585,11 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach_node(RestrictInfo, other, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
+
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
 
@@ -1551,13 +1600,47 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Remove all references between this RestrictInfo and its
+			 * relating index.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+				Assert(bms_is_member(i, indexes->source_indexes));
+				indexes->source_indexes =
+					bms_del_member(indexes->source_indexes, i);
+			}
+
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_derives indexes. But set NULL to detect potential
+			 * problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources,
+			 * we must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/* Make sure that we didn't break EquivalenceClass indexes */
+	verify_eclass_indexes(root, ec);
+#endif
 }
 
 /*
@@ -1723,7 +1806,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7b1a8a0a9f1..017f7de75ac 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -664,6 +664,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 5d9225e9909..aadf4c88e52 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1153,6 +1153,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 5b3dc0d8653..1e543f705a0 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -512,6 +512,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..e011da4f6bc 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,11 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
+	restrictinfo->root = root;
+
 	return restrictinfo;
 }
 
@@ -394,6 +399,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 9433548d279..cd8e4011427 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -18,6 +18,7 @@
 #include "nodes/nodeFuncs.h"
 #include "nodes/pathnodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/paths.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
@@ -649,8 +650,8 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 			expression_tree_walker((Node *) rinfo->clause, ChangeVarNodes_walker, (void *) context);
 			expression_tree_walker((Node *) rinfo->orclause, ChangeVarNodes_walker, (void *) context);
 
-			rinfo->clause_relids =
-				adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index);
+			update_clause_relids(rinfo,
+								 adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index));
 			rinfo->num_base_rels = bms_num_members(rinfo->clause_relids);
 			rinfo->left_relids =
 				adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 7b8088e0eec..1c4d0b1a4ce 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -351,6 +351,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1434,6 +1440,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1452,8 +1476,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1548,14 +1574,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2705,7 +2737,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2823,6 +2860,13 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
+
+	/* PlannerInfo where this RestrictInfo was created */
+	PlannerInfo *root pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ef832963604..79c09cf9f4f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -132,6 +132,7 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -168,7 +169,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +206,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v32-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v32-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From aeadd1cdda985f436ad749c4a9a68397fda83024 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v32 4/4] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  18 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 197 insertions(+), 175 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..21c2593e959 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_next(). If you
+ * change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 73a4710522a..e0f95da9a18 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1953,16 +1953,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec, nominal_join_relids, true,
+								false);
+	while ((restrictinfo = eclass_rinfo_iterator_next(&iter)) != 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) &&
@@ -1970,6 +1968,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2041,11 +2040,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2056,12 +2054,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								true, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2071,14 +2068,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								false, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2088,6 +2084,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3924,179 +3921,166 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator
+ *	  Setup a RestrictInfoIterator 'iter' so that it can iterate over
+ *	  RestrictInfos. We iterate through root->eq_sources if 'is_source' is
+ *	  true, or root->eq_derives otherwise. The members must be from 'ec' and
+ *	  satisfy "bms_is_subset(relids, rinfo->clause_relids)" if 'is_strict' is
+ *	  true, or "bms_overlap(relids, rinfo->clause_relids)" otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
+							EquivalenceClass *ec, Relids relids,
+							bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator(). NULL is returned if there are no
+ *	  members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
+
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1c4d0b1a4ce..3f0d686108c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1569,6 +1569,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 79c09cf9f4f..1361e20641a 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -206,18 +206,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+extern void setup_eclass_rinfo_iterator(RestrictInfoIterator *iter,
+										PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+										Relids relids,
+										bool is_source,
+										bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index bc8283f6cd4..f96860984d0 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2480,6 +2480,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

#102Yuya Watari
watari.yuya@gmail.com
In reply to: Yuya Watari (#101)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello,

On Wed, Feb 19, 2025 at 4:33 PM Yuya Watari <watari.yuya@gmail.com> wrote:

I noticed that the patches did not apply to the current HEAD, so I
have rebased them.

The previous patches did not apply to the current master, so I have
rebased them.

--
Best regards,
Yuya Watari

Attachments:

v33-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v33-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 33fc52b0a7c2720b2173d9ce70c6ed937fc5a131 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v33 1/4] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/optimizer/path/equivclass.c | 435 +++++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/inherit.c    |  14 +
 src/backend/optimizer/util/relnode.c    |  53 +++
 src/include/nodes/pathnodes.h           |  82 +++++
 src/include/optimizer/paths.h           |   9 +-
 src/tools/pgindent/typedefs.list        |   2 +
 10 files changed, 565 insertions(+), 130 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1131a8bf77e..d4a26b59330 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7829,14 +7829,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, rel->relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7847,6 +7846,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -7900,9 +7900,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..70d80f272d1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,11 +33,15 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										EquivalenceMember *parent,
-										Oid datatype);
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -68,6 +72,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_transformed_child_version(EquivalenceChildMemberIterator *it,
+										  PlannerInfo *root,
+										  EquivalenceClass *ec,
+										  RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -372,8 +380,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -389,8 +397,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -421,10 +429,10 @@ 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,
-							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +518,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +538,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +559,30 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * 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(),
+ * 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,
+					 JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -616,7 +649,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +665,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +686,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_child_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -689,14 +723,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -760,19 +794,20 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +834,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +877,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -855,9 +892,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, relids);
+	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -901,6 +938,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	return NULL;
 }
@@ -939,7 +977,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1564,7 +1602,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceChildMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1575,10 +1614,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_child_member_iterator(&it, root, ec, join_relids);
+	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1632,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_child_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1611,6 +1650,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1685,6 +1725,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1692,7 +1733,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2405,6 +2446,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2526,8 +2568,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2598,8 +2640,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2696,6 +2738,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2708,7 +2751,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,15 +2763,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2742,8 +2778,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2759,6 +2795,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2788,9 +2825,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members =
+					lappend(child_rel->eclass_child_members, child_em);
 
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2819,6 +2858,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2840,7 +2880,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,15 +2892,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2870,8 +2903,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2886,6 +2919,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2916,9 +2951,38 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * Update the corresponding inverted indexes.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * EquivalenceChildMemberIterator. Since the iterator
+					 * needs EquivalenceMembers whose em_relids has child
+					 * relations, skipping the update of this inverted index
+					 * allows for faster iteration.
+					 */
+					if (root->top_parent_relid_array[j] == 0)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2967,12 +3031,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -2987,6 +3051,205 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_child_member_iterator
+ *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
+ *	  EquivalenceMembers in 'ec'.
+ *
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_child_member_iterator().
+ */
+void
+setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+								   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;
+
+	/*
+	 * 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;
+
+	/*
+	 * 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.
+	 */
+
+	/*
+	 * 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] == 0)
+			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);
+}
+
+/*
+ * add_transformed_child_version
+ *	  Add a transformed EquivalenceMember referencing the given child_rel to
+ *	  the iterator.
+ *
+ * This function is expected to be called only from
+ * setup_eclass_child_member_iterator().
+ */
+static void
+add_transformed_child_version(EquivalenceChildMemberIterator *it,
+							  PlannerInfo *root,
+							  EquivalenceClass *ec,
+							  RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_ec != ec)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3041,6 +3304,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3062,15 +3326,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_child_member_iterator(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3085,8 +3348,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3304,8 +3567,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index a43ca16d683..30e443d1554 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3739,7 +3739,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3756,7 +3756,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceChildMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3776,9 +3777,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_child_member_iterator(&it, root, pathkey->pk_eclass,
+										   index->rel->relids);
+		while ((member = eclass_child_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3813,6 +3815,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_child_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 816a2b2a576..abb05991186 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -297,7 +301,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1284,7 +1288,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1328,7 +1332,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1469,7 +1473,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1500,7 +1504,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1982,7 +1986,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2196,7 +2200,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2220,7 +2224,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2289,7 +2293,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4555,7 +4559,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4564,7 +4569,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4589,7 +4595,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6239,7 +6245,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6306,7 +6312,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6334,7 +6340,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6350,7 +6356,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6421,7 +6427,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6430,7 +6437,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6456,8 +6463,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6466,7 +6474,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6825,7 +6833,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6888,7 +6897,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 17e51cd75d7..734d54a7305 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,6 +470,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
+	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -577,6 +578,19 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
+	/*
+	 * Find a top parent rel's index and save it to top_parent_relid_array.
+	 */
+	if (root->top_parent_relid_array == NULL)
+		root->top_parent_relid_array =
+			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
+	Assert(root->top_parent_relid_array[childRTindex] == 0);
+	topParentRTindex = parentRTindex;
+	while (root->append_rel_array[topParentRTindex] != NULL &&
+		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
+		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
+	root->top_parent_relid_array[childRTindex] = topParentRTindex;
+
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..9c689fa2cd5 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,15 +119,21 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
+	root->top_parent_relid_array = (Index *)
+		palloc0(size * sizeof(Index));
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -148,6 +154,27 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
+
+	/*
+	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
+	 * in the last foreach loop because there may be multi-level parent-child
+	 * relations.
+	 */
+	for (int i = 0; i < size; i++)
+	{
+		int			top_parent_relid;
+
+		if (root->append_rel_array[i] == NULL)
+			continue;
+
+		top_parent_relid = root->append_rel_array[i]->parent_relid;
+
+		while (root->append_rel_array[top_parent_relid] != NULL &&
+			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
+			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
+
+		root->top_parent_relid_array[i] = top_parent_relid;
+	}
 }
 
 /*
@@ -175,12 +202,28 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->top_parent_relid_array =
+			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+		/*
+		 * We do not allocate top_parent_relid_array here so that
+		 * setup_eclass_child_member_iterator() can early find all of the
+		 * given Relids are top-level.
+		 */
+		root->top_parent_relid_array = NULL;
+	}
+
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +277,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +673,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +791,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +979,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1542,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index fbf05322c75..75db449966c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -213,6 +213,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -269,6 +271,21 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
+	/*
+	 * top_parent_relid_array is the same length as simple_rel_array and holds
+	 * the top-level parent indexes of the corresponding rels within
+	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * i.e., is itself in a top-level.
+	 */
+	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -981,6 +998,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1400,6 +1428,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as iterate_child_rel_equivalences() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1474,8 +1508,56 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* 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 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, root, ec, rel);
+ * while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_child_member_iterator(&it);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceChildMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..ef832963604 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -182,6 +183,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
+											   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 List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 56989aa0b84..e221172f6f4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -695,7 +695,9 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceChildMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v33-0002-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v33-0002-Resolve-conflict-with-commit-66c0185.patchDownload
From 51d3694ffe0b33f2f62a3a2901d29ec3a3604a90 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v33 2/4] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 46 ++++++++++++++++++++-----
 src/backend/optimizer/util/inherit.c    |  8 +++--
 src/backend/optimizer/util/relnode.c    |  8 +++--
 src/include/nodes/pathnodes.h           |  2 +-
 4 files changed, 51 insertions(+), 13 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 70d80f272d1..3415c91a912 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2977,7 +2977,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					 * relations, skipping the update of this inverted index
 					 * allows for faster iteration.
 					 */
-					if (root->top_parent_relid_array[j] == 0)
+					if (root->top_parent_relid_array[j] == -1)
 						continue;
 					indexes->joinrel_indexes =
 						bms_add_member(indexes->joinrel_indexes,
@@ -3014,7 +3014,9 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
+		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3031,12 +3033,40 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		child_rel->eclass_child_members =
+			lappend(child_rel->eclass_child_members, child_em);
+
+		/*
+		 * Make an UNION parent-child relationship between parent_em and
+		 * child_rel->relid. We record this relationship in
+		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		parent_relid = bms_next_member(parent_em->em_relids, -1);
+		if (root->top_parent_relid_array == NULL)
+		{
+			/*
+			 * If the array is NULL, allocate it here.
+			 */
+			root->top_parent_relid_array = (Index *)
+				palloc(root->simple_rel_array_size * sizeof(Index));
+			MemSet(root->top_parent_relid_array, -1,
+				   sizeof(Index) * root->simple_rel_array_size);
+		}
+		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
+			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
+		root->top_parent_relid_array[child_rel->relid] = parent_relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3122,7 +3152,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 		/*
 		 * If this relation is a parent, we don't have to do anything.
 		 */
-		if (root->top_parent_relid_array[i] == 0)
+		if (root->top_parent_relid_array[i] == -1)
 			continue;
 
 		/*
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 734d54a7305..08cd39a6c85 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -582,9 +582,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * Find a top parent rel's index and save it to top_parent_relid_array.
 	 */
 	if (root->top_parent_relid_array == NULL)
+	{
 		root->top_parent_relid_array =
-			(Index *) palloc0(root->simple_rel_array_size * sizeof(Index));
-	Assert(root->top_parent_relid_array[childRTindex] == 0);
+			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
+		MemSet(root->top_parent_relid_array, -1,
+			   sizeof(Index) * root->simple_rel_array_size);
+	}
+	Assert(root->top_parent_relid_array[childRTindex] == -1);
 	topParentRTindex = parentRTindex;
 	while (root->append_rel_array[topParentRTindex] != NULL &&
 		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 9c689fa2cd5..ff25cd86448 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -133,7 +133,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 	root->top_parent_relid_array = (Index *)
-		palloc0(size * sizeof(Index));
+		palloc(size * sizeof(Index));
+	MemSet(root->top_parent_relid_array, -1,
+		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -206,7 +208,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 		root->top_parent_relid_array =
-			repalloc0_array(root->top_parent_relid_array, Index, root->simple_rel_array_size, new_size);
+			repalloc_array(root->top_parent_relid_array, Index, new_size);
+		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
+			   sizeof(Index) * add_size);
 	}
 	else
 	{
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 75db449966c..7b8088e0eec 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -281,7 +281,7 @@ struct PlannerInfo
 	/*
 	 * top_parent_relid_array is the same length as simple_rel_array and holds
 	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be zero if the rel has no parents,
+	 * simple_rel_array. The element can be -1 if the rel has no parents,
 	 * i.e., is itself in a top-level.
 	 */
 	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-- 
2.45.2.windows.1

v33-0003-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v33-0003-Introduce-indexes-for-RestrictInfo.patchDownload
From 558d18fe3168f5d9c000e93f3e37847aeef27844 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v33 3/4] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 514 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c | 132 ++++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/restrictinfo.c |   7 +
 src/backend/rewrite/rewriteManip.c        |   5 +-
 src/include/nodes/pathnodes.h             |  54 ++-
 src/include/optimizer/paths.h             |  20 +-
 11 files changed, 665 insertions(+), 80 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 73d78617009..cc62be7221e 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5839,7 +5839,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 3415c91a912..73a4710522a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
 											   Expr *expr, Relids relids,
 											   JoinDomain *jdomain,
 											   Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -318,7 +322,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -329,6 +332,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -349,8 +354,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -362,10 +369,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -376,13 +382,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_parent_eq_member(ec1, item2, item2_relids,
 								   jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -393,13 +400,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_parent_eq_member(ec2, item1, item1_relids,
 								   jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -410,6 +418,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -419,8 +429,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -442,6 +452,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -583,6 +595,166 @@ add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -704,8 +876,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1064,7 +1236,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1157,6 +1329,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1166,9 +1339,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1226,9 +1399,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1238,7 +1411,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1304,7 +1478,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1361,11 +1535,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1406,11 +1581,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1778,12 +1953,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1795,12 +1974,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1862,10 +2041,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1876,9 +2056,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1889,9 +2072,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1964,7 +2151,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2690,16 +2877,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3487,7 +3677,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3575,7 +3765,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3732,3 +3922,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index b1e173c63bc..acade358fac 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -58,7 +58,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   SpecialJoinInfo *sjinfo,
 								   int relid, int subst);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
@@ -473,7 +473,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			(sjinfo == NULL || bms_is_member(sjinfo->ojrelid, ec->ec_relids)))
-			remove_rel_from_eclass(ec, sjinfo, relid, subst);
+			remove_rel_from_eclass(root, ec, sjinfo, relid, subst);
 	}
 
 	/*
@@ -640,17 +640,25 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 static void
 remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -699,10 +707,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
-					   int relid, int subst)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
+					   SpecialJoinInfo *sjinfo, int relid, int subst)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, relid, subst);
@@ -734,9 +743,10 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (sjinfo == NULL)
 			ChangeVarNodes((Node *) rinfo, relid, subst, 0);
@@ -749,7 +759,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1503,10 +1513,12 @@ is_innerrel_unique_for(PlannerInfo *root,
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
@@ -1543,17 +1555,50 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * index.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+			Assert(bms_is_member(i, indexes->derive_indexes));
+			indexes->derive_indexes =
+				bms_del_member(indexes->derive_indexes, i);
+		}
+
+		/*
+		 * Can't delete the element because we would need to rebuild all the
+		 * eq_derives indexes. But set NULL to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach_node(RestrictInfo, rinfo, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1564,8 +1609,11 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach_node(RestrictInfo, other, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
+
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
 
@@ -1576,13 +1624,47 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Remove all references between this RestrictInfo and its
+			 * relating index.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+				Assert(bms_is_member(i, indexes->source_indexes));
+				indexes->source_indexes =
+					bms_del_member(indexes->source_indexes, i);
+			}
+
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_derives indexes. But set NULL to detect potential
+			 * problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources,
+			 * we must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/* Make sure that we didn't break EquivalenceClass indexes */
+	verify_eclass_indexes(root, ec);
+#endif
 }
 
 /*
@@ -1748,7 +1830,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 36ee6dd43de..ab17820da56 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -664,6 +664,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index bcc40dd5a84..ce17049b205 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1286,6 +1286,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 5b3dc0d8653..1e543f705a0 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -512,6 +512,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..e011da4f6bc 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,11 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
+	restrictinfo->root = root;
+
 	return restrictinfo;
 }
 
@@ -394,6 +399,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 98a265cd3d5..73e294a2ff5 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -18,6 +18,7 @@
 #include "nodes/nodeFuncs.h"
 #include "nodes/pathnodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/paths.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
@@ -649,8 +650,8 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 			expression_tree_walker((Node *) rinfo->clause, ChangeVarNodes_walker, (void *) context);
 			expression_tree_walker((Node *) rinfo->orclause, ChangeVarNodes_walker, (void *) context);
 
-			rinfo->clause_relids =
-				adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index);
+			update_clause_relids(rinfo,
+								 adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index));
 			rinfo->num_base_rels = bms_num_members(rinfo->clause_relids);
 			rinfo->left_relids =
 				adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 7b8088e0eec..1c4d0b1a4ce 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -351,6 +351,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1434,6 +1440,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as iterate_child_rel_equivalences() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1452,8 +1476,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1548,14 +1574,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2705,7 +2737,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2823,6 +2860,13 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
+
+	/* PlannerInfo where this RestrictInfo was created */
+	PlannerInfo *root pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ef832963604..79c09cf9f4f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -132,6 +132,7 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -168,7 +169,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +206,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v33-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v33-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From b8a6ea68e89512bd5e43a54a4e58a758c6496018 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v33 4/4] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 312 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  18 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 197 insertions(+), 175 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..21c2593e959 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_next(). If you
+ * change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 73a4710522a..e0f95da9a18 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1953,16 +1953,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec, nominal_join_relids, true,
+								false);
+	while ((restrictinfo = eclass_rinfo_iterator_next(&iter)) != 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) &&
@@ -1970,6 +1968,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2041,11 +2040,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2056,12 +2054,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								true, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2071,14 +2068,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator(&iter, root, ec,
+								bms_union(leftem->em_relids, rightem->em_relids),
+								false, true);
+	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2088,6 +2084,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3924,179 +3921,166 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator
+ *	  Setup a RestrictInfoIterator 'iter' so that it can iterate over
+ *	  RestrictInfos. We iterate through root->eq_sources if 'is_source' is
+ *	  true, or root->eq_derives otherwise. The members must be from 'ec' and
+ *	  satisfy "bms_is_subset(relids, rinfo->clause_relids)" if 'is_strict' is
+ *	  true, or "bms_overlap(relids, rinfo->clause_relids)" otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
+							EquivalenceClass *ec, Relids relids,
+							bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator(). NULL is returned if there are no
+ *	  members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
+
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1c4d0b1a4ce..3f0d686108c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1569,6 +1569,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceChildMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 79c09cf9f4f..1361e20641a 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -206,18 +206,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+extern void setup_eclass_rinfo_iterator(RestrictInfoIterator *iter,
+										PlannerInfo *root,
 										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+										Relids relids,
+										bool is_source,
+										bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e221172f6f4..e4fec5c04ea 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2495,6 +2495,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

#103newtglobal postgresql_contributors
postgresql_contributors@newtglobalcorp.com
In reply to: Yuya Watari (#102)
Re: [PoC] Reducing planning time when tables have many partitions

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, failed
Spec compliant: tested, failed
Documentation: tested, failed

Hi Yuya,
Tested this patch and noted that this patch significantly improves query planning time, especially as the number of partitions increases. While the impact is minimal for small partition counts (2–8), the improvement becomes substantial from 16 partitions onward, reaching up to ~86.6% reduction at 768 partitions. Larger partitions (512–1024) see a dramatic speedup, cutting planning time by over 2.7 seconds. The results confirm that the patch optimizes partitioned query execution efficiently. This enhancement is crucial for databases handling large partitioned tables, leading to better performance and scalability.
Regards,
NewtGlobal PostgreSQL contributors

#104David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#102)
Re: [PoC] Reducing planning time when tables have many partitions

On Sat, 1 Mar 2025 at 23:07, Yuya Watari <watari.yuya@gmail.com> wrote:

The previous patches did not apply to the current master, so I have
rebased them.

Thank you for continuing to work on this. My apologies for having
completely disappeared from this thread for so long.

Looking at v33-0001, there are a few choices you've made that are not
clear to me:

1) Can you describe the difference between
PlannerInfo.top_parent_relid_array and RelOptInfo.top_parent_relids?
If you've added the PlannerInfo field for performance reasons, then
that needs to be documented. I think the bar for adding another field
to do the same thing should be quite high. The
RelOptInfo.top_parent_relids field already is commented with
"redundant, but handy", so having another field in another struct
that's also redundant leads me to think that some design needs more
thought.

If you need a cheap way to take the same shortcut as you're doing in
setup_eclass_child_member_iterator() with "if
(root->top_parent_relid_array == NULL)", then maybe PlannerInfo should
have a boolean field to record if there are any other member rels

2) I think the naming of setup_eclass_child_member_iterator() and
dispose_eclass_child_member_iterator() is confusing. From the names,
I'd expect these to only be returning em_is_child == true members, but
that's not the case.

3) The header comment for setup_eclass_child_member_iterator() does
not seem concise enough. It claims "so that it can iterate over
EquivalenceMembers in 'ec'.", but what does that mean? The definition
of "EquivalenceMembers in 'ec'" isn't clear. Is that just the class's
ec_members, or also the child members that are stored somewhere else.
Users of this function need to know what they'll get so they know
which members they need to ignore or which they can assume won't be
returned. If you don't document that, then it's quite hard to
determine where the faulty code is when we get bugs. The "relids"
parameter needs to be documented too.

4) add_transformed_child_version sounds like it does some
transformation, but all it does is add the EMs for the given
RelOptInfo to the iterator's list. I don't quite follow what's being
"transformed". Maybe there's a better name?

That's all I have for now.

David

#105Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#104)
5 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

Thank you very much for your thorough review and valuable comments.

I have refactored the patches based on your feedback and attached the
updated versions (v34). Additionally, I have included a diff between
v33 and v34 for your quick reference.

On Thu, Mar 13, 2025 at 1:53 PM David Rowley <dgrowleyml@gmail.com> wrote:

1) Can you describe the difference between
PlannerInfo.top_parent_relid_array and RelOptInfo.top_parent_relids?
If you've added the PlannerInfo field for performance reasons, then
that needs to be documented. I think the bar for adding another field
to do the same thing should be quite high. The
RelOptInfo.top_parent_relids field already is commented with
"redundant, but handy", so having another field in another struct
that's also redundant leads me to think that some design needs more
thought.

If you need a cheap way to take the same shortcut as you're doing in
setup_eclass_child_member_iterator() with "if
(root->top_parent_relid_array == NULL)", then maybe PlannerInfo should
have a boolean field to record if there are any other member rels

Thank you for highlighting this. I initially introduced
PlannerInfo.top_parent_relid_array primarily for performance reasons
to quickly determine whether a relation is a parent or child,
particularly in setup_eclass_child_member_iterator(). As you
mentioned, earlier versions utilized the check "if
(root->top_parent_relid_array == NULL)" to skip unnecessary operations
when no child relations exist.

However, I have realized that the same behavior can be achieved by
using root->append_rel_array. Specifically, if a relation is a parent,
the corresponding AppendRelInfo is NULL, and if there are no child
relations at all, the entire array itself is NULL. So,
PlannerInfo.top_parent_relid_array is no longer necessary.

In v34-0001, I removed root->top_parent_relid_array and instead
utilized root->append_rel_array. However, this caused issues in
add_setop_child_rel_equivalences(), since this function adds a new
child EquivalenceMember without building a parent-child relationship
in root->append_rel_array. To address this, I have created a dummy
AppendRelInfo object in v34-0002. This is just a workaround, and there
may be a more elegant solution. I'd greatly appreciate any suggestions
or alternative approaches you might have.

2) I think the naming of setup_eclass_child_member_iterator() and
dispose_eclass_child_member_iterator() is confusing. From the names,
I'd expect these to only be returning em_is_child == true members, but
that's not the case.

I agree the original naming was misleading. In v34-0001, I have
renamed these functions to
setup_eclass_all_member_iterator_for_relids() and
dispose_eclass_all_member_iterator_for_relids(). To align with this
change, I have also renamed EquivalenceChildMemberIterator to
EquivalenceAllMemberIterator. Does this new naming better address your
concern?

3) The header comment for setup_eclass_child_member_iterator() does
not seem concise enough. It claims "so that it can iterate over
EquivalenceMembers in 'ec'.", but what does that mean? The definition
of "EquivalenceMembers in 'ec'" isn't clear. Is that just the class's
ec_members, or also the child members that are stored somewhere else.
Users of this function need to know what they'll get so they know
which members they need to ignore or which they can assume won't be
returned. If you don't document that, then it's quite hard to
determine where the faulty code is when we get bugs. The "relids"
parameter needs to be documented too.

I have clarified the header comment in v34-0001. It now explicitly
states that the iterator iterates over all parent members and child
members whose em_relids are subsets of the given 'relids'. I have also
clearly documented the parameters, including 'relids'.

4) add_transformed_child_version sounds like it does some
transformation, but all it does is add the EMs for the given
RelOptInfo to the iterator's list. I don't quite follow what's being
"transformed". Maybe there's a better name?

Thank you for highlighting this. The original name was indeed
misleading. I have renamed this function to
add_eclass_child_members_to_iterator().

--
Best regards,
Yuya Watari

Attachments:

diff-v33-v34.txttext/plain; charset=US-ASCII; name=diff-v33-v34.txtDownload
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index d4a26b59330..2eb0f16b0f6 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7830,11 +7830,11 @@ EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
-	EquivalenceChildMemberIterator it;
+	EquivalenceAllMemberIterator it;
 	EquivalenceMember *em;
 
-	setup_eclass_child_member_iterator(&it, root, ec, rel->relids);
-	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, rel->relids);
+	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7846,7 +7846,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
-	dispose_eclass_child_member_iterator(&it);
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	return NULL;
 }
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 21c2593e959..5aecf96cbb7 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1302,8 +1302,8 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
  *
- * NOTE: The routine here is similar to eclass_rinfo_iterator_next(). If you
- * change here, you should adjust the logic there.
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_for_relids_next().
+ * If you change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e0f95da9a18..c6ecfb874c8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -76,10 +76,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
-static void add_transformed_child_version(EquivalenceChildMemberIterator *it,
-										  PlannerInfo *root,
-										  EquivalenceClass *ec,
-										  RelOptInfo *child_rel);
+static void add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
+												 PlannerInfo *root,
+												 EquivalenceClass *ec,
+												 RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -821,7 +821,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		EquivalenceChildMemberIterator it;
+		EquivalenceAllMemberIterator it;
 		EquivalenceMember *cur_em;
 
 		/*
@@ -837,8 +837,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		setup_eclass_child_member_iterator(&it, root, cur_ec, rel);
-		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
+		setup_eclass_all_member_iterator_for_relids(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 		{
 			/*
 			 * Ignore child members unless they match the request.
@@ -858,7 +858,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
-		dispose_eclass_child_member_iterator(&it);
+		dispose_eclass_all_member_iterator_for_relids(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -970,15 +970,15 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	EquivalenceChildMemberIterator it;
+	EquivalenceAllMemberIterator it;
 	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	setup_eclass_child_member_iterator(&it, root, ec, relids);
-	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, relids);
+	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
 		Expr	   *emexpr;
 
@@ -1006,7 +1006,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
-	dispose_eclass_child_member_iterator(&it);
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	return NULL;
 }
@@ -1049,7 +1049,7 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	EquivalenceChildMemberIterator it;
+	EquivalenceAllMemberIterator it;
 	EquivalenceMember *em;
 
 	/*
@@ -1064,8 +1064,8 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	setup_eclass_child_member_iterator(&it, root, ec, relids);
-	while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, relids);
+	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
 		List	   *emvars;
 		ListCell   *lc2;
@@ -1110,7 +1110,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
-	dispose_eclass_child_member_iterator(&it);
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	return NULL;
 }
@@ -1777,7 +1777,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	EquivalenceChildMemberIterator it;
+	EquivalenceAllMemberIterator it;
 	EquivalenceMember *cur_em;
 
 	/*
@@ -1789,8 +1789,8 @@ 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.
 	 */
-	setup_eclass_child_member_iterator(&it, root, ec, join_relids);
-	while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, join_relids);
+	while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1807,7 +1807,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
-	dispose_eclass_child_member_iterator(&it);
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1957,9 +1957,9 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	RestrictInfo *restrictinfo;
 	RestrictInfoIterator iter;
 
-	setup_eclass_rinfo_iterator(&iter, root, ec, nominal_join_relids, true,
-								false);
-	while ((restrictinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, nominal_join_relids, true,
+										   false);
+	while ((restrictinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
 		Relids		clause_relids = restrictinfo->required_relids;
 
@@ -1968,7 +1968,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
-	dispose_eclass_rinfo_iterator(&iter);
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2054,10 +2054,10 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	setup_eclass_rinfo_iterator(&iter, root, ec,
-								bms_union(leftem->em_relids, rightem->em_relids),
-								true, true);
-	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   true, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
@@ -2068,12 +2068,12 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
-	dispose_eclass_rinfo_iterator(&iter);
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
-	setup_eclass_rinfo_iterator(&iter, root, ec,
-								bms_union(leftem->em_relids, rightem->em_relids),
-								false, true);
-	while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   false, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
@@ -2084,7 +2084,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
-	dispose_eclass_rinfo_iterator(&iter);
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3159,12 +3159,12 @@ 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
-					 * EquivalenceChildMemberIterator. Since the iterator
-					 * needs EquivalenceMembers whose em_relids has child
-					 * relations, skipping the update of this inverted index
-					 * allows for faster iteration.
+					 * EquivalenceAllMemberIterator. Since the iterator needs
+					 * EquivalenceMembers whose em_relids has child relations,
+					 * skipping the update of this inverted index allows for
+					 * faster iteration.
 					 */
-					if (root->top_parent_relid_array[j] == -1)
+					if (root->append_rel_array[j] == NULL)
 						continue;
 					indexes->joinrel_indexes =
 						bms_add_member(indexes->joinrel_indexes,
@@ -3203,7 +3203,6 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		EquivalenceMember *parent_em;
 		EquivalenceMember *child_em;
 		PathKey    *pk;
-		Index		parent_relid;
 
 		if (tle->resjunk)
 			continue;
@@ -3232,28 +3231,26 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		/*
 		 * Make an UNION parent-child relationship between parent_em and
 		 * child_rel->relid. We record this relationship in
-		 * root->top_parent_relid_array, which generally has AppendRelInfo
+		 * root->append_rel_array, which generally has AppendRelInfo
 		 * relationships. We use the same array here to retrieve UNION child
 		 * members.
 		 *
+		 * XXX Here we create a dummy AppendRelInfo object to know that the
+		 * relation of 'child_rel->relid' is a child and there are some
+		 * children because root->append_rel_array is not NULL. However this
+		 * is just a workaround. We must consider better way.
+		 *
 		 * XXX Here we treat the first member of parent_em->em_relids as a
 		 * parent of child_rel. Is this correct? What happens if
 		 * parent_em->em_relids has two or more members?
 		 */
-		parent_relid = bms_next_member(parent_em->em_relids, -1);
-		if (root->top_parent_relid_array == NULL)
-		{
-			/*
-			 * If the array is NULL, allocate it here.
-			 */
-			root->top_parent_relid_array = (Index *)
-				palloc(root->simple_rel_array_size * sizeof(Index));
-			MemSet(root->top_parent_relid_array, -1,
-				   sizeof(Index) * root->simple_rel_array_size);
-		}
-		Assert(root->top_parent_relid_array[child_rel->relid] == -1 ||
-			   root->top_parent_relid_array[child_rel->relid] == parent_relid);
-		root->top_parent_relid_array[child_rel->relid] = parent_relid;
+		if (root->append_rel_array == NULL)
+			root->append_rel_array = palloc0_array(AppendRelInfo *, root->simple_rel_array_size);
+		root->append_rel_array[child_rel->relid] = makeNode(AppendRelInfo);
+		root->append_rel_array[child_rel->relid]->parent_relid =
+			bms_next_member(parent_em->em_relids, -1);
+		root->append_rel_array[child_rel->relid]->child_relid =
+			child_rel->relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3269,18 +3266,32 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 }
 
 /*
- * setup_eclass_child_member_iterator
- *	  Setup an EquivalenceChildMemberIterator 'it' so that it can iterate over
- *	  EquivalenceMembers in 'ec'.
+ * setup_eclass_all_member_iterator_for_relids
+ *	  Setup an EquivalenceAllMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members associated with the given 'ec' that
+ *	  are relevant to the specified 'relids'.
  *
- * Once used, the caller should dispose of the iterator by calling
- * dispose_eclass_child_member_iterator().
+ * This iterator returns:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *	- Once used, the caller should dispose of the iterator by calling
+ *	  dispose_eclass_all_member_iterator_for_relids().
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	relids - The Relids used to filter for relevant child members.
  */
 void
-setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
-								   PlannerInfo *root,
-								   EquivalenceClass *ec,
-								   Relids relids)
+setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
+											PlannerInfo *root,
+											EquivalenceClass *ec,
+											Relids relids)
 {
 	Bitmapset  *matching_indexes;
 	int			i;
@@ -3296,7 +3307,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 	 * 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)
+	if (root->append_rel_array == NULL)
 		return;
 
 	/*
@@ -3339,7 +3350,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 		/*
 		 * If this relation is a parent, we don't have to do anything.
 		 */
-		if (root->top_parent_relid_array[i] == -1)
+		if (root->append_rel_array[i] == NULL)
 			continue;
 
 		/*
@@ -3347,7 +3358,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 		 */
 		child_rel = root->simple_rel_array[i];
 		if (child_rel != NULL)
-			add_transformed_child_version(it, root, ec, child_rel);
+			add_eclass_child_members_to_iterator(it, root, ec, child_rel);
 
 		/*
 		 * Union indexes for join rels.
@@ -3373,7 +3384,7 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 		 * 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);
+			add_eclass_child_members_to_iterator(it, root, ec, child_joinrel);
 #ifdef USE_ASSERT_CHECKING
 		else
 		{
@@ -3400,18 +3411,18 @@ setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
 }
 
 /*
- * add_transformed_child_version
- *	  Add a transformed EquivalenceMember referencing the given child_rel to
- *	  the iterator.
+ * add_eclass_child_members_to_iterator
+ *	  Add a child EquivalenceMember referencing the given child_rel to
+ *	  the iterator from RelOptInfo.
  *
  * This function is expected to be called only from
- * setup_eclass_child_member_iterator().
+ * setup_eclass_all_member_iterator_for_relids().
  */
 static void
-add_transformed_child_version(EquivalenceChildMemberIterator *it,
-							  PlannerInfo *root,
-							  EquivalenceClass *ec,
-							  RelOptInfo *child_rel)
+add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
+									 PlannerInfo *root,
+									 EquivalenceClass *ec,
+									 RelOptInfo *child_rel)
 {
 	ListCell   *lc;
 
@@ -3439,13 +3450,13 @@ add_transformed_child_version(EquivalenceChildMemberIterator *it,
 }
 
 /*
- * 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.
+ * eclass_all_member_iterator_for_relids_next
+ *	  Get a next EquivalenceMember from an EquivalenceAllMemberIterator 'it'
+ *	  that was setup by setup_eclass_all_member_iterator_for_relids(). NULL is
+ *	  returned if there are no members left.
  */
 EquivalenceMember *
-eclass_child_member_iterator_next(EquivalenceChildMemberIterator *it)
+eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it)
 {
 	if (++it->index < list_length(it->ec_members))
 		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
@@ -3453,11 +3464,11 @@ eclass_child_member_iterator_next(EquivalenceChildMemberIterator *it)
 }
 
 /*
- * dispose_eclass_child_member_iterator
+ * dispose_eclass_all_member_iterator_for_relids
  *	  Free any memory allocated by the iterator.
  */
 void
-dispose_eclass_child_member_iterator(EquivalenceChildMemberIterator *it)
+dispose_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it)
 {
 	/*
 	 * XXX Should we use list_free()? I decided to use this style to take
@@ -3521,7 +3532,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
-		EquivalenceChildMemberIterator it;
+		EquivalenceAllMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3543,14 +3554,14 @@ 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.)
 		 */
-		setup_eclass_child_member_iterator(&it, root, cur_ec, rel->relids);
-		while ((cur_em = eclass_child_member_iterator_next(&it)) != NULL)
+		setup_eclass_all_member_iterator_for_relids(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 		{
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 		}
-		dispose_eclass_child_member_iterator(&it);
+		dispose_eclass_all_member_iterator_for_relids(&it);
 
 		if (!cur_em)
 			continue;
@@ -3921,20 +3932,21 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * setup_eclass_rinfo_iterator
- *	  Setup a RestrictInfoIterator 'iter' so that it can iterate over
- *	  RestrictInfos. We iterate through root->eq_sources if 'is_source' is
- *	  true, or root->eq_derives otherwise. The members must be from 'ec' and
- *	  satisfy "bms_is_subset(relids, rinfo->clause_relids)" if 'is_strict' is
- *	  true, or "bms_overlap(relids, rinfo->clause_relids)" otherwise.
+ * setup_eclass_rinfo_iterator_for_relids
+ *	  Setup a RestrictInfoIterator 'iter' to iterate over RestrictInfos
+ *	  associated with the given 'ec'. It iterates through root->eq_sources if
+ *	  'is_source' is true, or root->eq_derives otherwise. The members must be
+ *	  from 'ec' and satisfy "bms_is_subset(relids, rinfo->clause_relids)" if
+ *	  'is_strict' is true, or "bms_overlap(relids, rinfo->clause_relids)"
+ *	  otherwise.
  *
  * Once used, the caller should dispose of the iterator by calling
- * dispose_eclass_rinfo_iterator().
+ * dispose_eclass_rinfo_iterator_for_relids().
  */
 void
-setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
-							EquivalenceClass *ec, Relids relids,
-							bool is_source, bool is_strict)
+setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter, PlannerInfo *root,
+									   EquivalenceClass *ec, Relids relids,
+									   bool is_source, bool is_strict)
 {
 	iter->root = root;
 	iter->ec = ec;
@@ -3947,13 +3959,13 @@ setup_eclass_rinfo_iterator(RestrictInfoIterator *iter, PlannerInfo *root,
 }
 
 /*
- * eclass_rinfo_iterator_next
+ * eclass_rinfo_iterator_for_relids_next
  *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
- *	  setup by setup_eclass_rinfo_iterator(). NULL is returned if there are no
- *	  members left.
+ *	  setup by setup_eclass_rinfo_iterator_for_relids(). NULL is returned if
+ *	  there are no members left.
  */
 RestrictInfo *
-eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
+eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter)
 {
 	RestrictInfo *rinfo;
 	bitmapword	mask;
@@ -4074,11 +4086,11 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter)
 }
 
 /*
- * dispose_eclass_rinfo_iterator
+ * dispose_eclass_rinfo_iterator_for_relids
  *	  Free any memory allocated by the iterator.
  */
 void
-dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter)
+dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter)
 {
 	/* Do nothing */
 }
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 30e443d1554..38f7322caf9 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3756,7 +3756,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		EquivalenceChildMemberIterator it;
+		EquivalenceAllMemberIterator it;
 		EquivalenceMember *member;
 
 
@@ -3777,9 +3777,9 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		setup_eclass_child_member_iterator(&it, root, pathkey->pk_eclass,
-										   index->rel->relids);
-		while ((member = eclass_child_member_iterator_next(&it)) != NULL)
+		setup_eclass_all_member_iterator_for_relids(&it, root, pathkey->pk_eclass,
+													index->rel->relids);
+		while ((member = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 		{
 			int			indexcol;
 
@@ -3815,7 +3815,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
-		dispose_eclass_child_member_iterator(&it);
+		dispose_eclass_all_member_iterator_for_relids(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 08cd39a6c85..17e51cd75d7 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -470,7 +470,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
-	Index		topParentRTindex;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
 	TupleDesc	child_tupdesc;
@@ -578,23 +577,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	Assert(root->append_rel_array[childRTindex] == NULL);
 	root->append_rel_array[childRTindex] = appinfo;
 
-	/*
-	 * Find a top parent rel's index and save it to top_parent_relid_array.
-	 */
-	if (root->top_parent_relid_array == NULL)
-	{
-		root->top_parent_relid_array =
-			(Index *) palloc(root->simple_rel_array_size * sizeof(Index));
-		MemSet(root->top_parent_relid_array, -1,
-			   sizeof(Index) * root->simple_rel_array_size);
-	}
-	Assert(root->top_parent_relid_array[childRTindex] == -1);
-	topParentRTindex = parentRTindex;
-	while (root->append_rel_array[topParentRTindex] != NULL &&
-		   root->append_rel_array[topParentRTindex]->parent_relid != 0)
-		topParentRTindex = root->append_rel_array[topParentRTindex]->parent_relid;
-	root->top_parent_relid_array[childRTindex] = topParentRTindex;
-
 	/*
 	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
 	 */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff25cd86448..8dc51353791 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -126,16 +126,11 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
-		root->top_parent_relid_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
-	root->top_parent_relid_array = (Index *)
-		palloc(size * sizeof(Index));
-	MemSet(root->top_parent_relid_array, -1,
-		   sizeof(Index) * size);
 
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
@@ -156,27 +151,6 @@ setup_simple_rel_arrays(PlannerInfo *root)
 
 		root->append_rel_array[child_relid] = appinfo;
 	}
-
-	/*
-	 * Find a top parent rel's relid for each AppendRelInfo. We cannot do this
-	 * in the last foreach loop because there may be multi-level parent-child
-	 * relations.
-	 */
-	for (int i = 0; i < size; i++)
-	{
-		int			top_parent_relid;
-
-		if (root->append_rel_array[i] == NULL)
-			continue;
-
-		top_parent_relid = root->append_rel_array[i]->parent_relid;
-
-		while (root->append_rel_array[top_parent_relid] != NULL &&
-			   root->append_rel_array[top_parent_relid]->parent_relid != 0)
-			top_parent_relid = root->append_rel_array[top_parent_relid]->parent_relid;
-
-		root->top_parent_relid_array[i] = top_parent_relid;
-	}
 }
 
 /*
@@ -204,27 +178,12 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
-	{
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
-		root->top_parent_relid_array =
-			repalloc_array(root->top_parent_relid_array, Index, new_size);
-		MemSet(root->top_parent_relid_array + root->simple_rel_array_size, -1,
-			   sizeof(Index) * add_size);
-	}
 	else
-	{
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
-		/*
-		 * We do not allocate top_parent_relid_array here so that
-		 * setup_eclass_child_member_iterator() can early find all of the
-		 * given Relids are top-level.
-		 */
-		root->top_parent_relid_array = NULL;
-	}
-
 	root->eclass_indexes_array =
 		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 3f0d686108c..0abd8dd4d9b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -278,14 +278,6 @@ struct PlannerInfo
 	 */
 	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
 
-	/*
-	 * top_parent_relid_array is the same length as simple_rel_array and holds
-	 * the top-level parent indexes of the corresponding rels within
-	 * simple_rel_array. The element can be -1 if the rel has no parents,
-	 * i.e., is itself in a top-level.
-	 */
-	Index	   *top_parent_relid_array pg_node_attr(read_write_ignore);
-
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -1436,9 +1428,9 @@ typedef struct JoinDomain
  *
  * EquivalenceClass->ec_members can only have parent members, and child members
  * are stored in RelOptInfos, from which those child members are translated. To
- * lookup child EquivalenceMembers, we use EquivalenceChildMemberIterator. See
+ * lookup child EquivalenceMembers, we use EquivalenceAllMemberIterator. See
  * its comment for usage. The approach to lookup child members quickly is
- * described as iterate_child_rel_equivalences() comment.
+ * described as setup_eclass_all_member_iterator_for_relids() comment.
  *
  * At various locations in the query planner, we must search for source and
  * derived RestrictInfos regarding a given EquivalenceClass.  For the common
@@ -1538,28 +1530,33 @@ typedef struct EquivalenceMember
 } EquivalenceMember;
 
 /*
- * EquivalenceChildMemberIterator
+ * EquivalenceAllMemberIterator
  *
- * 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.
+ * EquivalenceAllMemberIterator is designed to iterate over all parent
+ * EquivalenceMembers and child members associated with the given 'ec' that
+ * are relevant to the specified 'relids'. In particular, it iterates over:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
  *
- * The most common way to use this struct is as follows:
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *
+ * The most common way to use this iterator is as follows:
  * -----
  * PlannerInfo					   *root = given;
  * EquivalenceClass				   *ec = given;
  * Relids							rel = given;
- * EquivalenceChildMemberIterator	it;
+ * EquivalenceAllMemberIterator		it;
  * EquivalenceMember			   *em;
  *
- * setup_eclass_child_member_iterator(&it, root, ec, rel);
- * while ((em = eclass_child_member_iterator_next(&it)) != NULL)
+ * setup_eclass_all_member_iterator_for_relids(&it, root, ec, rel);
+ * while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
  * {
  *     use em ...;
  * }
- * dispose_eclass_child_member_iterator(&it);
+ * dispose_eclass_all_member_iterator_for_relids(&it);
  * -----
  */
 typedef struct
@@ -1567,7 +1564,7 @@ typedef struct
 	int			index;			/* current index within 'ec_members'. */
 	bool		modified;		/* is 'ec_members' a newly allocated one? */
 	List	   *ec_members;		/* parent and child members */
-} EquivalenceChildMemberIterator;
+} EquivalenceAllMemberIterator;
 
 /*
  * RestrictInfoIterator
@@ -1576,7 +1573,7 @@ typedef struct
  * EquivalenceClass that satisfy a certain condition. You need to first
  * initialize an iterator, and then use move next during the iteration. You
  * can specify the condition during initialization. For more details, see the
- * comment in setup_eclass_rinfo_iterator().
+ * comment in setup_eclass_rinfo_iterator_for_relids().
  *
  * The most common way to use this struct is as follows:
  * -----
@@ -1586,12 +1583,12 @@ typedef struct
  * RestrictInfoIterator iter;
  * RestrictInfo		   *rinfo;
  *
- * setup_eclass_rinfo_iterator(&iter, root, ec, relids, true, true);
- * while ((rinfo = eclass_rinfo_iterator_next(&iter)) != NULL)
+ * setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
  * {
  *     use rinfo ...;
  * }
- * dispose_eclass_rinfo_iterator(&iter);
+ * dispose_eclass_rinfo_iterator_for_relids(&iter);
  * -----
  */
 typedef struct
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 1361e20641a..e8706b2774c 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -185,12 +185,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
-extern void setup_eclass_child_member_iterator(EquivalenceChildMemberIterator *it,
-											   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 setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
+														PlannerInfo *root,
+														EquivalenceClass *ec,
+														Relids relids);
+extern EquivalenceMember *eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it);
+extern void dispose_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
@@ -206,14 +206,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern void setup_eclass_rinfo_iterator(RestrictInfoIterator *iter,
-										PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids,
-										bool is_source,
-										bool is_strict);
-extern RestrictInfo *eclass_rinfo_iterator_next(RestrictInfoIterator *iter);
-extern void dispose_eclass_rinfo_iterator(RestrictInfoIterator *iter);
+extern void setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter,
+												   PlannerInfo *root,
+												   EquivalenceClass *ec,
+												   Relids relids,
+												   bool is_source,
+												   bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 433577a3d8a..51d45b6677c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -696,7 +696,7 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
-EquivalenceChildMemberIterator
+EquivalenceAllMemberIterator
 EquivalenceClass
 EquivalenceClassIndexes
 EquivalenceMember
v34-0001-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v34-0001-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 8a88942c01e73976ad93f4e99ee782839a6b4650 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v34 1/4] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/optimizer/path/equivclass.c | 449 +++++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/plan/createplan.c |  61 ++--
 src/backend/optimizer/util/relnode.c    |  16 +
 src/include/nodes/pathnodes.h           |  79 +++++
 src/include/optimizer/paths.h           |   9 +-
 src/tools/pgindent/typedefs.list        |   2 +
 9 files changed, 525 insertions(+), 130 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1131a8bf77e..2eb0f16b0f6 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7829,14 +7829,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceAllMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, rel->relids);
+	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7847,6 +7846,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	return NULL;
 }
@@ -7900,9 +7900,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..9ac15cca8fa 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,11 +33,15 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids,
-										JoinDomain *jdomain,
-										EquivalenceMember *parent,
-										Oid datatype);
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
+static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
+											   Expr *expr, Relids relids,
+											   JoinDomain *jdomain,
+											   Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -68,6 +72,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
+												 PlannerInfo *root,
+												 EquivalenceClass *ec,
+												 RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -372,8 +380,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em2 = add_parent_eq_member(ec1, item2, item2_relids,
+								   jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -389,8 +397,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+		em1 = add_parent_eq_member(ec2, item1, item1_relids,
+								   jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -421,10 +429,10 @@ 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,
-							jdomain, NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+		em1 = add_parent_eq_member(ec, item1, item1_relids,
+								   jdomain, item1_type);
+		em2 = add_parent_eq_member(ec, item2, item2_relids,
+								   jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +518,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +538,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +559,30 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * 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(),
+ * 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,
+					 JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -616,7 +649,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceAllMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +665,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_all_member_iterator_for_relids(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +686,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_all_member_iterator_for_relids(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -689,14 +723,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
+								 jdomain, opcintype);
 
 	/*
-	 * add_eq_member doesn't check for volatile functions, set-returning
-	 * functions, aggregates, or window functions, but such could appear in
-	 * sort expressions; so we have to check whether its const-marking was
-	 * correct.
+	 * add_parent_eq_member doesn't check for volatile functions,
+	 * set-returning functions, aggregates, or window functions, but such
+	 * could appear in sort expressions; so we have to check whether its
+	 * const-marking was correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -760,19 +794,20 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceAllMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, relids);
+	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +834,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	return NULL;
 }
@@ -841,7 +877,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceAllMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -855,9 +892,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, relids);
+	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -901,6 +938,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	return NULL;
 }
@@ -939,7 +977,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
@@ -1564,7 +1602,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceAllMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1575,10 +1614,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_all_member_iterator_for_relids(&it, root, ec, join_relids);
+	while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1632,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_all_member_iterator_for_relids(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1611,6 +1650,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1685,6 +1725,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1692,7 +1733,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2405,6 +2446,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
+			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2526,8 +2568,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2598,8 +2640,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2696,6 +2738,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2708,7 +2751,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,15 +2763,9 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2742,8 +2778,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			 * combinations of children.  (But add_child_join_rel_equivalences
 			 * may add targeted combinations for partitionwise-join purposes.)
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2759,6 +2795,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2788,9 +2825,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members =
+					lappend(child_rel->eclass_child_members, child_em);
 
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2819,6 +2858,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
+	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -2840,7 +2880,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,15 +2892,9 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
@@ -2870,8 +2903,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			 * We consider only original EC members here, not
 			 * already-transformed child members.
 			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2886,6 +2919,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2916,9 +2951,38 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * Update the corresponding inverted indexes.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * EquivalenceAllMemberIterator. Since the iterator needs
+					 * EquivalenceMembers whose em_relids has child relations,
+					 * skipping the update of this inverted index allows for
+					 * faster iteration.
+					 */
+					if (root->append_rel_array[j] == NULL)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2967,12 +3031,12 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_parent_eq_member(pk->pk_eclass,
+							 tle->expr,
+							 child_rel->relids,
+							 parent_em->em_jdomain,
+							 parent_em,
+							 exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -2987,6 +3051,219 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_all_member_iterator_for_relids
+ *	  Setup an EquivalenceAllMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members associated with the given 'ec' that
+ *	  are relevant to the specified 'relids'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *	- Once used, the caller should dispose of the iterator by calling
+ *	  dispose_eclass_all_member_iterator_for_relids().
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	relids - The Relids used to filter for relevant child members.
+ */
+void
+setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
+											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;
+
+	/*
+	 * If there are no child relations, there is nothing to do. This
+	 * effectively avoids regression for non-partitioned cases.
+	 */
+	if (root->append_rel_array == NULL)
+		return;
+
+	/*
+	 * 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.
+	 */
+
+	/*
+	 * 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->append_rel_array[i] == NULL)
+			continue;
+
+		/*
+		 * Add child members that mention this relation to the iterator.
+		 */
+		child_rel = root->simple_rel_array[i];
+		if (child_rel != NULL)
+			add_eclass_child_members_to_iterator(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_eclass_child_members_to_iterator(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);
+}
+
+/*
+ * add_eclass_child_members_to_iterator
+ *	  Add a child EquivalenceMember referencing the given child_rel to
+ *	  the iterator from RelOptInfo.
+ *
+ * This function is expected to be called only from
+ * setup_eclass_all_member_iterator_for_relids().
+ */
+static void
+add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
+									 PlannerInfo *root,
+									 EquivalenceClass *ec,
+									 RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_ec != ec)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->modified)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->modified = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * eclass_all_member_iterator_for_relids_next
+ *	  Get a next EquivalenceMember from an EquivalenceAllMemberIterator 'it'
+ *	  that was setup by setup_eclass_all_member_iterator_for_relids(). NULL is
+ *	  returned if there are no members left.
+ */
+EquivalenceMember *
+eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it)
+{
+	if (++it->index < list_length(it->ec_members))
+		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
+	return NULL;
+}
+
+/*
+ * dispose_eclass_all_member_iterator_for_relids
+ *	  Free any memory allocated by the iterator.
+ */
+void
+dispose_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it)
+{
+	/*
+	 * XXX Should we use list_free()? I decided to use this style to take
+	 * advantage of speculative execution.
+	 */
+	if (unlikely(it->modified))
+		pfree(it->ec_members);
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3041,6 +3318,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceAllMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3062,15 +3340,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_all_member_iterator_for_relids(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_all_member_iterator_for_relids(&it);
 
 		if (!cur_em)
 			continue;
@@ -3085,8 +3362,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3304,8 +3581,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index a43ca16d683..38f7322caf9 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3739,7 +3739,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3756,7 +3756,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceAllMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3776,9 +3777,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_all_member_iterator_for_relids(&it, root, pathkey->pk_eclass,
+													index->rel->relids);
+		while ((member = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3813,6 +3815,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_all_member_iterator_for_relids(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 75e2b0b9036..eff5d93aa3a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -294,7 +298,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1281,7 +1285,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1325,7 +1329,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1466,7 +1470,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1497,7 +1501,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1979,7 +1983,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2193,7 +2197,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2217,7 +2221,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2286,7 +2290,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4544,7 +4548,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4553,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4578,7 +4584,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6228,7 +6234,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6295,7 +6301,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6323,7 +6329,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6339,7 +6345,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6410,7 +6416,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6419,7 +6426,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6445,8 +6452,9 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6455,7 +6463,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6812,7 +6820,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6875,7 +6884,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..8dc51353791 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,9 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
@@ -181,6 +184,9 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
 
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+
 	root->simple_rel_array_size = new_size;
 }
 
@@ -234,6 +240,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -629,6 +636,12 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
 	{
@@ -741,6 +754,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +942,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1505,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index fbf05322c75..9651f52d405 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -213,6 +213,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -269,6 +271,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster lookups of
+	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -981,6 +990,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	Index		join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1400,6 +1420,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceAllMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as setup_eclass_all_member_iterator_for_relids() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1474,8 +1500,61 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* 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;
 
+/*
+ * EquivalenceAllMemberIterator
+ *
+ * EquivalenceAllMemberIterator is designed to iterate over all parent
+ * EquivalenceMembers and child members associated with the given 'ec' that
+ * are relevant to the specified 'relids'. In particular, it iterates over:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * PlannerInfo					   *root = given;
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceAllMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_all_member_iterator_for_relids(&it, root, ec, rel);
+ * while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_all_member_iterator_for_relids(&it);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceAllMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..954ab6e1955 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
@@ -182,6 +183,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
+														PlannerInfo *root,
+														EquivalenceClass *ec,
+														Relids relids);
+extern EquivalenceMember *eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it);
+extern void dispose_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 93339ef3c58..31ffc643916 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -696,7 +696,9 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
+EquivalenceAllMemberIterator
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
 ErrorContextCallback
 ErrorData
-- 
2.45.2.windows.1

v34-0002-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v34-0002-Resolve-conflict-with-commit-66c0185.patchDownload
From 7d2cccc78ea7a4fb148d9ea77db871fed70d9c5b Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v34 2/4] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 39 +++++++++++++++++++++----
 1 file changed, 33 insertions(+), 6 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9ac15cca8fa..33de4439bec 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3014,6 +3014,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
 
 		if (tle->resjunk)
@@ -3031,12 +3032,38 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_parent_eq_member(pk->pk_eclass,
-							 tle->expr,
-							 child_rel->relids,
-							 parent_em->em_jdomain,
-							 parent_em,
-							 exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		child_rel->eclass_child_members =
+			lappend(child_rel->eclass_child_members, child_em);
+
+		/*
+		 * Make an UNION parent-child relationship between parent_em and
+		 * child_rel->relid. We record this relationship in
+		 * root->append_rel_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we create a dummy AppendRelInfo object to know that the
+		 * relation of 'child_rel->relid' is a child and there are some
+		 * children because root->append_rel_array is not NULL. However this
+		 * is just a workaround. We must consider better way.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		if (root->append_rel_array == NULL)
+			root->append_rel_array = palloc0_array(AppendRelInfo *, root->simple_rel_array_size);
+		root->append_rel_array[child_rel->relid] = makeNode(AppendRelInfo);
+		root->append_rel_array[child_rel->relid]->parent_relid =
+			bms_next_member(parent_em->em_relids, -1);
+		root->append_rel_array[child_rel->relid]->child_relid =
+			child_rel->relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

v34-0003-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v34-0003-Introduce-indexes-for-RestrictInfo.patchDownload
From 4a6df93c71ffaafd37a67fc0e1534bf67a8c83c1 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v34 3/4] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 514 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c | 132 ++++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/restrictinfo.c |   7 +
 src/backend/rewrite/rewriteManip.c        |   5 +-
 src/include/nodes/pathnodes.h             |  54 ++-
 src/include/optimizer/paths.h             |  20 +-
 11 files changed, 665 insertions(+), 80 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 256568d05a2..7d23a8385b6 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5849,7 +5849,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 33de4439bec..1facf547bb9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
 											   Expr *expr, Relids relids,
 											   JoinDomain *jdomain,
 											   Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -318,7 +322,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -329,6 +332,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -349,8 +354,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -362,10 +369,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -376,13 +382,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_parent_eq_member(ec1, item2, item2_relids,
 								   jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -393,13 +400,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_parent_eq_member(ec2, item1, item1_relids,
 								   jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -410,6 +418,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -419,8 +429,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -442,6 +452,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -583,6 +595,166 @@ add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -704,8 +876,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1064,7 +1236,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1157,6 +1329,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1166,9 +1339,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1226,9 +1399,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1238,7 +1411,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1304,7 +1478,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1361,11 +1535,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1406,11 +1581,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1778,12 +1953,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1795,12 +1974,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1862,10 +2041,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1876,9 +2056,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1889,9 +2072,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1964,7 +2151,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2690,16 +2877,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3498,7 +3688,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3586,7 +3776,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3743,3 +3933,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 8a8d4a2af33..cc3fec7c718 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -58,7 +58,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   SpecialJoinInfo *sjinfo,
 								   int relid, int subst);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
@@ -473,7 +473,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			(sjinfo == NULL || bms_is_member(sjinfo->ojrelid, ec->ec_relids)))
-			remove_rel_from_eclass(ec, sjinfo, relid, subst);
+			remove_rel_from_eclass(root, ec, sjinfo, relid, subst);
 	}
 
 	/*
@@ -640,17 +640,25 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 static void
 remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -699,10 +707,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
-					   int relid, int subst)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
+					   SpecialJoinInfo *sjinfo, int relid, int subst)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, relid, subst);
@@ -734,9 +743,10 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (sjinfo == NULL)
 			ChangeVarNodes((Node *) rinfo, relid, subst, 0);
@@ -749,7 +759,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1504,10 +1514,12 @@ is_innerrel_unique_for(PlannerInfo *root,
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
@@ -1544,17 +1556,50 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * index.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+			Assert(bms_is_member(i, indexes->derive_indexes));
+			indexes->derive_indexes =
+				bms_del_member(indexes->derive_indexes, i);
+		}
+
+		/*
+		 * Can't delete the element because we would need to rebuild all the
+		 * eq_derives indexes. But set NULL to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach_node(RestrictInfo, rinfo, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1565,8 +1610,11 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach_node(RestrictInfo, other, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
+
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
 
@@ -1577,13 +1625,47 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Remove all references between this RestrictInfo and its
+			 * relating index.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+				Assert(bms_is_member(i, indexes->source_indexes));
+				indexes->source_indexes =
+					bms_del_member(indexes->source_indexes, i);
+			}
+
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_derives indexes. But set NULL to detect potential
+			 * problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources,
+			 * we must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/* Make sure that we didn't break EquivalenceClass indexes */
+	verify_eclass_indexes(root, ec);
+#endif
 }
 
 /*
@@ -1749,7 +1831,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a4d523dcb0f..d2a229776a4 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -665,6 +665,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index d131a5bbc59..15293ddbcbd 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1296,6 +1296,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 5b3dc0d8653..1e543f705a0 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -512,6 +512,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..e011da4f6bc 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,11 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
+	restrictinfo->root = root;
+
 	return restrictinfo;
 }
 
@@ -394,6 +399,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 1c61085a0a7..92faa486225 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -18,6 +18,7 @@
 #include "nodes/nodeFuncs.h"
 #include "nodes/pathnodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/paths.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
@@ -649,8 +650,8 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 			expression_tree_walker((Node *) rinfo->clause, ChangeVarNodes_walker, (void *) context);
 			expression_tree_walker((Node *) rinfo->orclause, ChangeVarNodes_walker, (void *) context);
 
-			rinfo->clause_relids =
-				adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index);
+			update_clause_relids(rinfo,
+								 adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index));
 			rinfo->num_base_rels = bms_num_members(rinfo->clause_relids);
 			rinfo->left_relids =
 				adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 9651f52d405..093c40288fd 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -343,6 +343,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1426,6 +1432,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as setup_eclass_all_member_iterator_for_relids() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1444,8 +1468,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1545,14 +1571,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2702,7 +2734,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2820,6 +2857,13 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
+
+	/* PlannerInfo where this RestrictInfo was created */
+	PlannerInfo *root pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 954ab6e1955..4b8dc7b1d6f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -132,6 +132,7 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -168,7 +169,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +206,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

v34-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v34-0004-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From e22c65332714758ab30541693b85fb76ea3b508e Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v34 4/4] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 313 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  20 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 199 insertions(+), 176 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..5aecf96cbb7 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_for_relids_next().
+ * If you change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 1facf547bb9..c6ecfb874c8 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1953,16 +1953,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, nominal_join_relids, true,
+										   false);
+	while ((restrictinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != 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) &&
@@ -1970,6 +1968,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2041,11 +2040,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2056,12 +2054,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   true, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2071,14 +2068,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   false, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2088,6 +2084,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3935,179 +3932,167 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator_for_relids
+ *	  Setup a RestrictInfoIterator 'iter' to iterate over RestrictInfos
+ *	  associated with the given 'ec'. It iterates through root->eq_sources if
+ *	  'is_source' is true, or root->eq_derives otherwise. The members must be
+ *	  from 'ec' and satisfy "bms_is_subset(relids, rinfo->clause_relids)" if
+ *	  'is_strict' is true, or "bms_overlap(relids, rinfo->clause_relids)"
+ *	  otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator_for_relids().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter, PlannerInfo *root,
+									   EquivalenceClass *ec, Relids relids,
+									   bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_for_relids_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator_for_relids(). NULL is returned if
+ *	  there are no members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
+
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator_for_relids
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 093c40288fd..0abd8dd4d9b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1566,6 +1566,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceAllMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator_for_relids().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator_for_relids(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 4b8dc7b1d6f..e8706b2774c 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -206,18 +206,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+extern void setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter,
+												   PlannerInfo *root,
+												   EquivalenceClass *ec,
+												   Relids relids,
+												   bool is_source,
+												   bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 31ffc643916..51d45b6677c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2504,6 +2504,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

#106David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#105)
Re: [PoC] Reducing planning time when tables have many partitions

On Fri, 14 Mar 2025 at 23:10, Yuya Watari <watari.yuya@gmail.com> wrote:

I have refactored the patches based on your feedback and attached the
updated versions (v34). Additionally, I have included a diff between
v33 and v34 for your quick reference.

Thanks for updating the patch. I've looked at v34-0001. Here are my
review comments:

1. There are various places in equivclass.c that Assert the member
isn't a child which have a comment "/* no children yet */", e.g.
generate_base_implied_equalities_const() there's
"Assert(!cur_em->em_is_child); /* no children yet */". The comment
implies that ec_members will later contain em_is_child members, but
that's not true. What you've written in
generate_implied_equalities_for_column() seems like the correct way to
handle this, i.e. "Child members should not exist in ec_members"

2. There's a comment in setup_eclass_all_member_iterator_for_relids which says:

* whose em_relids is a subset of the given 'child_relids'. The inverted

is 'child_relids' meant to be 'relids'? Otherwise, I don't know what
'child_relids' is.

3. Can you explain why you've added the following assert to
reconsider_full_join_clause()?

Assert(!bms_is_empty(coal_em->em_relids));

There are no other changes to that function and I can't quite figure
out why that Assert is relevant now if it wasn't before. Or is this a
case of adding additional protection against this? If so, is there
more risk now than there was before?

4. Is there any point in renaming add_eq_member() to
add_parent_eq_member()? You don't have a function called
add_child_eq_member() so is the "parent" word needed here?

5. In add_child_join_rel_equivalences() 'lc' can be moved into the
scope of the while loop.

6. The following comment in add_child_join_rel_equivalences() should
be deleted. That used to be above the "if (cur_em->em_is_child)
continue;" statement and it does not seem relevant to the code you've
replaced that with.

/*
* We consider only original EC members here, not
* already-transformed child members.
*/

7. EquivalenceAllMemberIterator's "modified" field does not seem like
a great name. Is it better to call this something like
"list_is_copy"?

8. It looks like most of the changes in createplan.c are there because
you need to get the PlannerInfo down to a few functions where it's
currently unavailable. I think you should break this out into another
precursor patch which does this only. It'll be easier to review the
main patch this way.

9. The new PlannerInfo parameter in prepare_sort_from_pathkeys() isn't
documented in the "Input Parameters:" header comment. Likewise for
make_sort_from_pathkeys() and make_incrementalsort_from_pathkeys()

10. The comment for PlannerInfo.eclass_indexes_array states it's for
"faster lookups of RestrictInfo". Shouldn't it be "faster
EquivalenceMember lookups"?

/*
* eclass_indexes_array is the same length as simple_rel_array and holds
* the indexes of the corresponding rels for faster lookups of
* RestrictInfo. See the EquivalenceClass comment for more details.
*/
struct EquivalenceClassIndexes *eclass_indexes_array
pg_node_attr(read_write_ignore);

I'm also not sure this comment would be accurate enough with only that
fix as it looks like we don't store any indexes for base rels.

11. In setup_simple_rel_arrays(), is there any point in pallocing
memory for eclass_indexes_array when root->append_rel_list is empty?

12. join_rel_list_index isn't being initialized in all the places that
do makeNode(RelOptInfo); Maybe -1 is a good initial value? (See my
next point)

13. RelOptInfo.join_rel_list_index is an index into
PlannerInfo.join_rel_list. It shouldn't be of type "Index". "int" is
the correct type for an index into a List.

14. In add_join_rel(), if you assign the join_rel_list_index before
the lappend, you don't need to "- 1".

On Thu, Mar 13, 2025 at 1:53 PM David Rowley <dgrowleyml@gmail.com> wrote:

1) Can you describe the difference between
PlannerInfo.top_parent_relid_array and RelOptInfo.top_parent_relids?
If you've added the PlannerInfo field for performance reasons, then
that needs to be documented. I think the bar for adding another field
to do the same thing should be quite high. The
RelOptInfo.top_parent_relids field already is commented with
"redundant, but handy", so having another field in another struct
that's also redundant leads me to think that some design needs more
thought.

If you need a cheap way to take the same shortcut as you're doing in
setup_eclass_child_member_iterator() with "if
(root->top_parent_relid_array == NULL)", then maybe PlannerInfo should
have a boolean field to record if there are any other member rels

Thank you for highlighting this. I initially introduced
PlannerInfo.top_parent_relid_array primarily for performance reasons
to quickly determine whether a relation is a parent or child,
particularly in setup_eclass_child_member_iterator(). As you
mentioned, earlier versions utilized the check "if
(root->top_parent_relid_array == NULL)" to skip unnecessary operations
when no child relations exist.

However, I have realized that the same behavior can be achieved by
using root->append_rel_array. Specifically, if a relation is a parent,
the corresponding AppendRelInfo is NULL, and if there are no child
relations at all, the entire array itself is NULL. So,
PlannerInfo.top_parent_relid_array is no longer necessary.

In v34-0001, I removed root->top_parent_relid_array and instead
utilized root->append_rel_array. However, this caused issues in
add_setop_child_rel_equivalences(), since this function adds a new
child EquivalenceMember without building a parent-child relationship
in root->append_rel_array. To address this, I have created a dummy
AppendRelInfo object in v34-0002. This is just a workaround, and there
may be a more elegant solution. I'd greatly appreciate any suggestions
or alternative approaches you might have.

I don't currently have the answers you need here. The problem is down
to how prepunion.c hacks together a targetlist with Vars having
varno==0. For the union planner work I did last year, to make the
union planner properly know if a union child had the required
PathKeys, I had to add EquivalenceMembers for each union child. At
the moment I don't really know if we could get away with classing
union children's non-junk target entries as fully-fledged EMs, or if
these should be child EMs. There's some contradiction as to how the
RelOptInfo is set up without a parent in recurse_set_operations(), and
making these child EMs as you're doing in the v34-0002 patch. The
problem with building the RelOptInfo with a parent is that
build_simple_rel() would try to apply the same base quals to the child
as the parent has. That's not the correct thing to do for union
children as each union child can have different quals. I just don't
think we have the correct design for the union planner just yet. I
don't currently have ideas on how to make this better. Maybe
build_simple_rel() needs some way to distinguish "this rel has a
parent" and "this rel should copy the parent's quals". However,
redesigning how this works now likely is a bad idea as it just feels a
bit late in the release cycle for that. You can see I chickened out
doing that in 12933dc60, previously 66c0185a3.

2) I think the naming of setup_eclass_child_member_iterator() and
dispose_eclass_child_member_iterator() is confusing. From the names,
I'd expect these to only be returning em_is_child == true members, but
that's not the case.

I agree the original naming was misleading. In v34-0001, I have
renamed these functions to
setup_eclass_all_member_iterator_for_relids() and
dispose_eclass_all_member_iterator_for_relids(). To align with this
change, I have also renamed EquivalenceChildMemberIterator to
EquivalenceAllMemberIterator. Does this new naming better address your
concern?

It's better. These names still feel very long. Also, I don't think the
iterator struct's name needs to be specific to "AllMembers". Surely
another setup function could have an iterator loop over any members it
likes. Likewise, the dispose function does not seem very specific to
"AllMembers". It's really the setup function that controls which
members are going to be visited. I suggest the next function, the
dispose function and the struct are given much more generic names.
setup_eclass_all_member_iterator_for_relids() feels long. Maybe
eclass_member_iterator_with_children and forget trying to include
"relids" in the name?

David

#107Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#106)
6 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

Thank you for your prompt response and detailed review. I have
addressed your comments and updated the patches accordingly (attached
as v35).

On Wed, Mar 19, 2025 at 7:48 PM David Rowley <dgrowleyml@gmail.com> wrote:

1. There are various places in equivclass.c that Assert the member
isn't a child which have a comment "/* no children yet */", e.g.
generate_base_implied_equalities_const() there's
"Assert(!cur_em->em_is_child); /* no children yet */". The comment
implies that ec_members will later contain em_is_child members, but
that's not true. What you've written in
generate_implied_equalities_for_column() seems like the correct way to
handle this, i.e. "Child members should not exist in ec_members"

Thank you for pointing this out. I have updated these assertions and comments.

2. There's a comment in setup_eclass_all_member_iterator_for_relids which says:

* whose em_relids is a subset of the given 'child_relids'. The inverted

is 'child_relids' meant to be 'relids'? Otherwise, I don't know what
'child_relids' is.

This was my mistake. I have corrected it to 'relids'.

3. Can you explain why you've added the following assert to
reconsider_full_join_clause()?

Assert(!bms_is_empty(coal_em->em_relids));

There are no other changes to that function and I can't quite figure
out why that Assert is relevant now if it wasn't before. Or is this a
case of adding additional protection against this? If so, is there
more risk now than there was before?

I'm sorry, but I cannot recall exactly why I initially added this
assertion. Upon review, I realized it was incorrect and unnecessary,
so I have removed it.

4. Is there any point in renaming add_eq_member() to
add_parent_eq_member()? You don't have a function called
add_child_eq_member() so is the "parent" word needed here?

I agree with you. I have reverted to the original name, add_eq_member().

5. In add_child_join_rel_equivalences() 'lc' can be moved into the
scope of the while loop.

Fixed.

6. The following comment in add_child_join_rel_equivalences() should
be deleted. That used to be above the "if (cur_em->em_is_child)
continue;" statement and it does not seem relevant to the code you've
replaced that with.

/*
* We consider only original EC members here, not
* already-transformed child members.
*/

Removed as suggested.

7. EquivalenceAllMemberIterator's "modified" field does not seem like
a great name. Is it better to call this something like
"list_is_copy"?

Thank you for the suggestion. I have renamed it to "list_is_copy."

8. It looks like most of the changes in createplan.c are there because
you need to get the PlannerInfo down to a few functions where it's
currently unavailable. I think you should break this out into another
precursor patch which does this only. It'll be easier to review the
main patch this way.

I have separated these changes into a distinct precursor patch as suggested.

9. The new PlannerInfo parameter in prepare_sort_from_pathkeys() isn't
documented in the "Input Parameters:" header comment. Likewise for
make_sort_from_pathkeys() and make_incrementalsort_from_pathkeys()

I have updated the documentation in each of these functions.

10. The comment for PlannerInfo.eclass_indexes_array states it's for
"faster lookups of RestrictInfo". Shouldn't it be "faster
EquivalenceMember lookups"?

/*
* eclass_indexes_array is the same length as simple_rel_array and holds
* the indexes of the corresponding rels for faster lookups of
* RestrictInfo. See the EquivalenceClass comment for more details.
*/
struct EquivalenceClassIndexes *eclass_indexes_array
pg_node_attr(read_write_ignore);

I have revised this comment accordingly.

I'm also not sure this comment would be accurate enough with only that
fix as it looks like we don't store any indexes for base rels.

It is true that currently, indexes for EquivalenceMembers do not store
information about base rels. However, the subsequent commit (v35-0004)
introduces indexes for base rels to enable faster RestrictInfo
lookups. Therefore, if we commit the later patch as well, the comment
will remain accurate. What do you think about this?

11. In setup_simple_rel_arrays(), is there any point in pallocing
memory for eclass_indexes_array when root->append_rel_list is empty?

As you mentioned, there is no need to allocate the array in v35-0002
(faster lookups for EquivalenceMembers) when root->append_rel_list is
empty. However, the allocation becomes necessary when introducing
indexes for RestrictInfos in v35-0004. To allow committing only
v35-0002 independently, I now avoid allocating the array when
root->append_rel_list is empty in v35-0002 and instead ensure that it
is always allocated in v35-0004.

12. join_rel_list_index isn't being initialized in all the places that
do makeNode(RelOptInfo); Maybe -1 is a good initial value? (See my
next point)

Fixed by initializing join_rel_list_index to -1.

13. RelOptInfo.join_rel_list_index is an index into
PlannerInfo.join_rel_list. It shouldn't be of type "Index". "int" is
the correct type for an index into a List.

Corrected to use "int".

14. In add_join_rel(), if you assign the join_rel_list_index before
the lappend, you don't need to "- 1".

Fixed as suggested.

I don't currently have the answers you need here. The problem is down
to how prepunion.c hacks together a targetlist with Vars having
varno==0. For the union planner work I did last year, to make the
union planner properly know if a union child had the required
PathKeys, I had to add EquivalenceMembers for each union child. At
the moment I don't really know if we could get away with classing
union children's non-junk target entries as fully-fledged EMs, or if
these should be child EMs. There's some contradiction as to how the
RelOptInfo is set up without a parent in recurse_set_operations(), and
making these child EMs as you're doing in the v34-0002 patch. The
problem with building the RelOptInfo with a parent is that
build_simple_rel() would try to apply the same base quals to the child
as the parent has. That's not the correct thing to do for union
children as each union child can have different quals. I just don't
think we have the correct design for the union planner just yet. I
don't currently have ideas on how to make this better. Maybe
build_simple_rel() needs some way to distinguish "this rel has a
parent" and "this rel should copy the parent's quals". However,
redesigning how this works now likely is a bad idea as it just feels a
bit late in the release cycle for that. You can see I chickened out
doing that in 12933dc60, previously 66c0185a3.

I agree it's challenging to redesign the union planner at this stage
of the release cycle. For now, I have proposed a workaround solution
(v35-0003, previously v34-0002). Would this workaround be acceptable
for the current release cycle?

It's better. These names still feel very long. Also, I don't think the
iterator struct's name needs to be specific to "AllMembers". Surely
another setup function could have an iterator loop over any members it
likes. Likewise, the dispose function does not seem very specific to
"AllMembers". It's really the setup function that controls which
members are going to be visited. I suggest the next function, the
dispose function and the struct are given much more generic names.
setup_eclass_all_member_iterator_for_relids() feels long. Maybe
eclass_member_iterator_with_children and forget trying to include
"relids" in the name?

Following your advice, I have simplified the naming:

* setup_eclass_all_member_iterator_for_relids() -->
setup_eclass_member_iterator_with_children()
* eclass_all_member_iterator_for_relids_next() --> eclass_member_iterator_next()
* dispose_eclass_all_member_iterator() --> dispose_eclass_member_iterator()
* EquivalenceAllMemberIterator --> EquivalenceMemberIterator

===

Thank you again for all your valuable feedback so far.

--
Best regards,
Yuya Watari

Attachments:

v35-0005-Introduce-RestrictInfoIterator-to-reduce-memory-.patchapplication/octet-stream; name=v35-0005-Introduce-RestrictInfoIterator-to-reduce-memory-.patchDownload
From 30d23cfcdd0c4e0fbb6b6ccf18ecbc86d5a5bac5 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH v35 5/5] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 313 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  20 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 199 insertions(+), 176 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..5aecf96cbb7 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_for_relids_next().
+ * If you change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 01832935211..1ffd2ce0a04 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1956,16 +1956,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, nominal_join_relids, true,
+										   false);
+	while ((restrictinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != 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) &&
@@ -1973,6 +1971,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2044,11 +2043,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2059,12 +2057,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   true, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2074,14 +2071,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   false, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2091,6 +2087,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3927,179 +3924,167 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator_for_relids
+ *	  Setup a RestrictInfoIterator 'iter' to iterate over RestrictInfos
+ *	  associated with the given 'ec'. It iterates through root->eq_sources if
+ *	  'is_source' is true, or root->eq_derives otherwise. The members must be
+ *	  from 'ec' and satisfy "bms_is_subset(relids, rinfo->clause_relids)" if
+ *	  'is_strict' is true, or "bms_overlap(relids, rinfo->clause_relids)"
+ *	  otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator_for_relids().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter, PlannerInfo *root,
+									   EquivalenceClass *ec, Relids relids,
+									   bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_for_relids_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator_for_relids(). NULL is returned if
+ *	  there are no members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
+
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator_for_relids
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 391d83169bb..c7cf1328d7a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1569,6 +1569,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator_for_relids().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator_for_relids(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index fba939ed855..13c739965ba 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -206,18 +206,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+extern void setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter,
+												   PlannerInfo *root,
+												   EquivalenceClass *ec,
+												   Relids relids,
+												   bool is_source,
+												   bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index c9688aa6b67..d2fc11ae19c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2529,6 +2529,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.45.2.windows.1

diff-v34-v35.txttext/plain; charset=US-ASCII; name=diff-v34-v35.txtDownload
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index dbf92c9b7ca..adcf6417468 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7829,11 +7829,11 @@ EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
-	EquivalenceAllMemberIterator it;
+	EquivalenceMemberIterator it;
 	EquivalenceMember *em;
 
-	setup_eclass_all_member_iterator_for_relids(&it, root, ec, rel->relids);
-	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+	setup_eclass_member_iterator_with_children(&it, root, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
@@ -7845,7 +7845,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
-	dispose_eclass_all_member_iterator_for_relids(&it);
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index c6ecfb874c8..1ffd2ce0a04 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -38,10 +38,10 @@ static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
 										 JoinDomain *jdomain,
 										 EquivalenceMember *parent,
 										 Oid datatype);
-static EquivalenceMember *add_parent_eq_member(EquivalenceClass *ec,
-											   Expr *expr, Relids relids,
-											   JoinDomain *jdomain,
-											   Oid datatype);
+static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+										Expr *expr, Relids relids,
+										JoinDomain *jdomain,
+										Oid datatype);
 static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
 						  RestrictInfo *rinfo);
 static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
@@ -76,7 +76,7 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
-static void add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
+static void add_eclass_child_members_to_iterator(EquivalenceMemberIterator *it,
 												 PlannerInfo *root,
 												 EquivalenceClass *ec,
 												 RelOptInfo *child_rel);
@@ -280,7 +280,8 @@ process_equivalence(PlannerInfo *root,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -388,8 +389,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_parent_eq_member(ec1, item2, item2_relids,
-								   jdomain, item2_type);
+		em2 = add_eq_member(ec1, item2, item2_relids,
+							jdomain, item2_type);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -406,8 +407,8 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_parent_eq_member(ec2, item1, item1_relids,
-								   jdomain, item1_type);
+		em1 = add_eq_member(ec2, item1, item1_relids,
+							jdomain, item1_type);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -439,10 +440,10 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_min_security = restrictinfo->security_level;
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
-		em1 = add_parent_eq_member(ec, item1, item1_relids,
-								   jdomain, item1_type);
-		em2 = add_parent_eq_member(ec, item2, item2_relids,
-								   jdomain, item2_type);
+		em1 = add_eq_member(ec, item1, item1_relids,
+							jdomain, item1_type);
+		em2 = add_eq_member(ec, item2, item2_relids,
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -576,7 +577,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 }
 
 /*
- * add_parent_eq_member - build a new parent EquivalenceMember and add it to an EC
+ * add_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
@@ -585,8 +586,8 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  * to see how to add child members.
  */
 static EquivalenceMember *
-add_parent_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-					 JoinDomain *jdomain, Oid datatype)
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
 {
 	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
 										   NULL, datatype);
@@ -821,7 +822,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		EquivalenceAllMemberIterator it;
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 
 		/*
@@ -837,8 +838,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		setup_eclass_all_member_iterator_for_relids(&it, root, cur_ec, rel);
-		while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+		setup_eclass_member_iterator_with_children(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
 			/*
 			 * Ignore child members unless they match the request.
@@ -858,7 +859,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
-		dispose_eclass_all_member_iterator_for_relids(&it);
+		dispose_eclass_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -895,14 +896,14 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
 
-	newem = add_parent_eq_member(newec, copyObject(expr), expr_relids,
-								 jdomain, opcintype);
+	newem = add_eq_member(newec, copyObject(expr), expr_relids,
+						  jdomain, opcintype);
 
 	/*
-	 * add_parent_eq_member doesn't check for volatile functions,
-	 * set-returning functions, aggregates, or window functions, but such
-	 * could appear in sort expressions; so we have to check whether its
-	 * const-marking was correct.
+	 * add_eq_member doesn't check for volatile functions, set-returning
+	 * functions, aggregates, or window functions, but such could appear in
+	 * sort expressions; so we have to check whether its const-marking was
+	 * correct.
 	 */
 	if (newec->ec_has_const)
 	{
@@ -970,15 +971,15 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	EquivalenceAllMemberIterator it;
+	EquivalenceMemberIterator it;
 	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	setup_eclass_all_member_iterator_for_relids(&it, root, ec, relids);
-	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+	setup_eclass_member_iterator_with_children(&it, root, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
 		Expr	   *emexpr;
 
@@ -1006,7 +1007,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
-	dispose_eclass_all_member_iterator_for_relids(&it);
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -1049,7 +1050,7 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	EquivalenceAllMemberIterator it;
+	EquivalenceMemberIterator it;
 	EquivalenceMember *em;
 
 	/*
@@ -1064,8 +1065,8 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	setup_eclass_all_member_iterator_for_relids(&it, root, ec, relids);
-	while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+	setup_eclass_member_iterator_with_children(&it, root, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
 		List	   *emvars;
 		ListCell   *lc2;
@@ -1110,7 +1111,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
-	dispose_eclass_all_member_iterator_for_relids(&it);
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -1373,7 +1374,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1443,7 +1445,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1777,7 +1780,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	EquivalenceAllMemberIterator it;
+	EquivalenceMemberIterator it;
 	EquivalenceMember *cur_em;
 
 	/*
@@ -1789,8 +1792,8 @@ 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.
 	 */
-	setup_eclass_all_member_iterator_for_relids(&it, root, ec, join_relids);
-	while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+	setup_eclass_member_iterator_with_children(&it, root, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
@@ -1807,7 +1810,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
-	dispose_eclass_all_member_iterator_for_relids(&it);
+	dispose_eclass_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -2406,7 +2409,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2533,7 +2537,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2630,7 +2635,6 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		if (matchleft && matchright)
 		{
 			cur_ec->ec_members = list_delete_nth_cell(cur_ec->ec_members, coal_idx);
-			Assert(!bms_is_empty(coal_em->em_relids));
 			return true;
 		}
 
@@ -2752,8 +2756,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			/* Child members should not exist in ec_members */
-			Assert(!em->em_is_child);
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2957,16 +2961,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			Assert(!cur_em->em_is_child);	/* Child members should not exist
-											 * in ec_members */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -3045,7 +3041,6 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 	Relids		child_relids = child_joinrel->relids;
 	Bitmapset  *matching_ecs;
 	MemoryContext oldcontext;
-	ListCell   *lc;
 	int			i;
 
 	Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
@@ -3067,6 +3062,7 @@ 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);
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -3086,12 +3082,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			Assert(!cur_em->em_is_child);	/* Child members should not exist
-											 * in ec_members */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -3159,7 +3151,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
-					 * EquivalenceAllMemberIterator. Since the iterator needs
+					 * EquivalenceMemberIterator. Since the iterator needs
 					 * EquivalenceMembers whose em_relids has child relations,
 					 * skipping the update of this inverted index allows for
 					 * faster iteration.
@@ -3266,8 +3258,8 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 }
 
 /*
- * setup_eclass_all_member_iterator_for_relids
- *	  Setup an EquivalenceAllMemberIterator 'it' to iterate over all parent
+ * setup_eclass_member_iterator_with_children
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
  *	  EquivalenceMembers and child members associated with the given 'ec' that
  *	  are relevant to the specified 'relids'.
  *
@@ -3280,7 +3272,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
  *	  em_relids is not a subset. So the caller must check that they satisfy
  *	  the desired condition.
  *	- Once used, the caller should dispose of the iterator by calling
- *	  dispose_eclass_all_member_iterator_for_relids().
+ *	  dispose_eclass_member_iterator().
  *
  * Parameters:
  *	root - The PlannerInfo context.
@@ -3288,10 +3280,10 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
  *	relids - The Relids used to filter for relevant child members.
  */
 void
-setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
-											PlannerInfo *root,
-											EquivalenceClass *ec,
-											Relids relids)
+setup_eclass_member_iterator_with_children(EquivalenceMemberIterator *it,
+										   PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids)
 {
 	Bitmapset  *matching_indexes;
 	int			i;
@@ -3300,7 +3292,7 @@ setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
 	 * Initialize the iterator.
 	 */
 	it->index = -1;
-	it->modified = false;
+	it->list_is_copy = false;
 	it->ec_members = ec->ec_members;
 
 	/*
@@ -3321,13 +3313,13 @@ setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
 	 * 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.
+	 * whose em_relids is a subset of the given '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 '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
@@ -3416,10 +3408,10 @@ setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
  *	  the iterator from RelOptInfo.
  *
  * This function is expected to be called only from
- * setup_eclass_all_member_iterator_for_relids().
+ * setup_eclass_member_iterator_with_children().
  */
 static void
-add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
+add_eclass_child_members_to_iterator(EquivalenceMemberIterator *it,
 									 PlannerInfo *root,
 									 EquivalenceClass *ec,
 									 RelOptInfo *child_rel)
@@ -3438,10 +3430,10 @@ add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
 		 * If this is the first time the iterator's list has been modified, we
 		 * need to make a copy of it.
 		 */
-		if (!it->modified)
+		if (!it->list_is_copy)
 		{
 			it->ec_members = list_copy(it->ec_members);
-			it->modified = true;
+			it->list_is_copy = true;
 		}
 
 		/* Add this child EquivalenceMember to the list */
@@ -3450,13 +3442,13 @@ add_eclass_child_members_to_iterator(EquivalenceAllMemberIterator *it,
 }
 
 /*
- * eclass_all_member_iterator_for_relids_next
- *	  Get a next EquivalenceMember from an EquivalenceAllMemberIterator 'it'
- *	  that was setup by setup_eclass_all_member_iterator_for_relids(). NULL is
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator_with_children(). NULL is
  *	  returned if there are no members left.
  */
 EquivalenceMember *
-eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it)
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
 {
 	if (++it->index < list_length(it->ec_members))
 		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
@@ -3464,17 +3456,17 @@ eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it)
 }
 
 /*
- * dispose_eclass_all_member_iterator_for_relids
+ * dispose_eclass_member_iterator
  *	  Free any memory allocated by the iterator.
  */
 void
-dispose_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it)
+dispose_eclass_member_iterator(EquivalenceMemberIterator *it)
 {
 	/*
 	 * XXX Should we use list_free()? I decided to use this style to take
 	 * advantage of speculative execution.
 	 */
-	if (unlikely(it->modified))
+	if (unlikely(it->list_is_copy))
 		pfree(it->ec_members);
 }
 
@@ -3532,7 +3524,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
-		EquivalenceAllMemberIterator it;
+		EquivalenceMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3554,14 +3546,14 @@ 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.)
 		 */
-		setup_eclass_all_member_iterator_for_relids(&it, root, cur_ec, rel->relids);
-		while ((cur_em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+		setup_eclass_member_iterator_with_children(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
 		}
-		dispose_eclass_all_member_iterator_for_relids(&it);
+		dispose_eclass_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 38f7322caf9..c206f260633 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3756,7 +3756,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		EquivalenceAllMemberIterator it;
+		EquivalenceMemberIterator it;
 		EquivalenceMember *member;
 
 
@@ -3777,9 +3777,9 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		setup_eclass_all_member_iterator_for_relids(&it, root, pathkey->pk_eclass,
-													index->rel->relids);
-		while ((member = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+		setup_eclass_member_iterator_with_children(&it, root, pathkey->pk_eclass,
+												   index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
 			int			indexcol;
 
@@ -3815,7 +3815,7 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
-		dispose_eclass_all_member_iterator_for_relids(&it);
+		dispose_eclass_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index eff5d93aa3a..163923072df 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -6201,6 +6201,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the plan node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' identifies the child relation being sorted, if any
@@ -6411,6 +6412,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
@@ -6446,6 +6448,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
  * make_incrementalsort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 8dc51353791..aa37dda82b8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,6 +119,11 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
+	/*
+	 * eclass_indexes_array is always needed, even if there are no
+	 * AppendRelInfos, because it stores indexes for RestrictInfos on base
+	 * rels.
+	 */
 	root->eclass_indexes_array = (EquivalenceClassIndexes *)
 		palloc0(size * sizeof(EquivalenceClassIndexes));
 
@@ -240,6 +245,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->join_rel_list_index = -1;
 	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
@@ -633,14 +639,14 @@ set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 static void
 add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 {
-	/* GEQO requires us to append the new joinrel to the end of the list! */
-	root->join_rel_list = lappend(root->join_rel_list, joinrel);
-
 	/*
 	 * Store the index of this joinrel to use in
 	 * add_child_join_rel_equivalences().
 	 */
-	joinrel->join_rel_list_index = list_length(root->join_rel_list) - 1;
+	joinrel->join_rel_list_index = list_length(root->join_rel_list);
+
+	/* GEQO requires us to append the new joinrel to the end of the list! */
+	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
 	/* store it into the auxiliary hashtable if there is one. */
 	if (root->join_rel_hash)
@@ -754,6 +760,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->join_rel_list_index = -1;
 	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
@@ -942,6 +949,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->join_rel_list_index = -1;
 	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
@@ -1505,7 +1513,8 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
-	upperrel->eclass_child_members = NIL;	/* XXX Is this required? */
+	upperrel->join_rel_list_index = -1;
+	upperrel->eclass_child_members = NIL;
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 899165b20d8..c7cf1328d7a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -276,8 +276,8 @@ struct PlannerInfo
 
 	/*
 	 * eclass_indexes_array is the same length as simple_rel_array and holds
-	 * the indexes of the corresponding rels for faster lookups of
-	 * RestrictInfo. See the EquivalenceClass comment for more details.
+	 * the indexes of the corresponding rels for faster EquivalenceMember
+	 * lookups. See the EquivalenceClass comment for more details.
 	 */
 	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
 
@@ -1003,7 +1003,7 @@ typedef struct RelOptInfo
 	 * information about a join rel
 	 */
 	/* index in root->join_rel_list of this rel */
-	Index		join_rel_list_index;
+	int			join_rel_list_index;
 
 	/*
 	 * EquivalenceMembers referencing this rel
@@ -1431,9 +1431,9 @@ typedef struct JoinDomain
  *
  * EquivalenceClass->ec_members can only have parent members, and child members
  * are stored in RelOptInfos, from which those child members are translated. To
- * lookup child EquivalenceMembers, we use EquivalenceAllMemberIterator. See
+ * lookup child EquivalenceMembers, we use EquivalenceMemberIterator. See
  * its comment for usage. The approach to lookup child members quickly is
- * described as setup_eclass_all_member_iterator_for_relids() comment.
+ * described as setup_eclass_member_iterator_with_children() comment.
  *
  * At various locations in the query planner, we must search for source and
  * derived RestrictInfos regarding a given EquivalenceClass.  For the common
@@ -1533,9 +1533,9 @@ typedef struct EquivalenceMember
 } EquivalenceMember;
 
 /*
- * EquivalenceAllMemberIterator
+ * EquivalenceMemberIterator
  *
- * EquivalenceAllMemberIterator is designed to iterate over all parent
+ * EquivalenceMemberIterator is designed to iterate over all parent
  * EquivalenceMembers and child members associated with the given 'ec' that
  * are relevant to the specified 'relids'. In particular, it iterates over:
  *	- All parent members stored directly in ec->ec_members.
@@ -1551,23 +1551,23 @@ typedef struct EquivalenceMember
  * PlannerInfo					   *root = given;
  * EquivalenceClass				   *ec = given;
  * Relids							rel = given;
- * EquivalenceAllMemberIterator		it;
+ * EquivalenceMemberIterator		it;
  * EquivalenceMember			   *em;
  *
- * setup_eclass_all_member_iterator_for_relids(&it, root, ec, rel);
- * while ((em = eclass_all_member_iterator_for_relids_next(&it)) != NULL)
+ * setup_eclass_member_iterator_with_children(&it, root, ec, rel);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
  * {
  *     use em ...;
  * }
- * dispose_eclass_all_member_iterator_for_relids(&it);
+ * dispose_eclass_member_iterator(&it);
  * -----
  */
 typedef struct
 {
 	int			index;			/* current index within 'ec_members'. */
-	bool		modified;		/* is 'ec_members' a newly allocated one? */
+	bool		list_is_copy;	/* is 'ec_members' a newly allocated one? */
 	List	   *ec_members;		/* parent and child members */
-} EquivalenceAllMemberIterator;
+} EquivalenceMemberIterator;
 
 /*
  * RestrictInfoIterator
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index e8706b2774c..13c739965ba 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -185,12 +185,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
-extern void setup_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it,
-														PlannerInfo *root,
-														EquivalenceClass *ec,
-														Relids relids);
-extern EquivalenceMember *eclass_all_member_iterator_for_relids_next(EquivalenceAllMemberIterator *it);
-extern void dispose_eclass_all_member_iterator_for_relids(EquivalenceAllMemberIterator *it);
+extern void setup_eclass_member_iterator_with_children(EquivalenceMemberIterator *it,
+													   PlannerInfo *root,
+													   EquivalenceClass *ec,
+													   Relids relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
+extern void dispose_eclass_member_iterator(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 85b330df4dd..d2fc11ae19c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -699,10 +699,10 @@ EphemeralNamedRelation
 EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
-EquivalenceAllMemberIterator
 EquivalenceClass
 EquivalenceClassIndexes
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
v35-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchapplication/octet-stream; name=v35-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchDownload
From cbc4afd14cd3ffa3d42f283473acf530cad43a04 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v35 1/5] Add the PlannerInfo context to the parameter of
 find_ec_member_matching_expr()

---
 src/backend/optimizer/path/equivclass.c |  4 +-
 src/backend/optimizer/plan/createplan.c | 64 +++++++++++++++----------
 src/include/optimizer/paths.h           |  3 +-
 3 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..7fe7cdff468 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -760,7 +760,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
@@ -939,7 +939,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 75e2b0b9036..163923072df 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -294,7 +298,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1281,7 +1285,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1325,7 +1329,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1466,7 +1470,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1497,7 +1501,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1979,7 +1983,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2193,7 +2197,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2217,7 +2221,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2286,7 +2290,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4544,7 +4548,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4553,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4578,7 +4584,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6195,6 +6201,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the plan node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' identifies the child relation being sorted, if any
@@ -6228,7 +6235,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6295,7 +6302,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6323,7 +6330,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6339,7 +6346,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6405,12 +6412,14 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6419,7 +6428,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6439,14 +6448,16 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  * make_incrementalsort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6455,7 +6466,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6812,7 +6823,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6875,7 +6887,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..84a000a3ef1 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
-- 
2.45.2.windows.1

v35-0002-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v35-0002-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From e851d7220d45251fb932fe4e1bb874f63705fcf0 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v35 2/5] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/optimizer/path/equivclass.c | 435 +++++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/util/relnode.c    |  33 +-
 src/include/nodes/pathnodes.h           |  79 +++++
 src/include/optimizer/paths.h           |   6 +
 src/tools/pgindent/typedefs.list        |   2 +
 8 files changed, 493 insertions(+), 101 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6beae0fa37f..adcf6417468 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7828,14 +7828,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7846,6 +7845,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -7899,9 +7899,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fe7cdff468..8cce165e95d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -68,6 +72,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_eclass_child_members_to_iterator(EquivalenceMemberIterator *it,
+												 PlannerInfo *root,
+												 EquivalenceClass *ec,
+												 RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -268,7 +276,8 @@ process_equivalence(PlannerInfo *root,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -373,7 +382,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -390,7 +399,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -422,9 +431,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +519,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +539,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +560,30 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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(),
+ * add_child_join_rel_equivalences() and add_setop_child_rel_equivalences()
+ * to see how to add child members.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -616,7 +650,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +666,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator_with_children(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +687,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -690,7 +725,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -764,15 +799,16 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +835,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +878,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -855,9 +893,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -901,6 +939,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -1162,7 +1201,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1231,7 +1271,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1564,7 +1605,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1575,10 +1617,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1635,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1611,6 +1653,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1685,6 +1728,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1692,7 +1736,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2181,7 +2225,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2308,7 +2353,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2526,8 +2572,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2598,8 +2644,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2696,6 +2742,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2708,7 +2755,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,29 +2767,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2759,6 +2791,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2788,9 +2821,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members =
+					lappend(child_rel->eclass_child_members, child_em);
 
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2840,7 +2875,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,25 +2888,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2886,6 +2911,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2916,9 +2943,38 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * Update the corresponding inverted indexes.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * EquivalenceMemberIterator. Since the iterator needs
+					 * EquivalenceMembers whose em_relids has child relations,
+					 * skipping the update of this inverted index allows for
+					 * faster iteration.
+					 */
+					if (root->append_rel_array[j] == NULL)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2971,7 +3027,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 					  tle->expr,
 					  child_rel->relids,
 					  parent_em->em_jdomain,
-					  parent_em,
+					  /* parent_em, */ /* Will be fixed in the next commit*/
 					  exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
@@ -2987,6 +3043,219 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator_with_children
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members associated with the given 'ec' that
+ *	  are relevant to the specified 'relids'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *	- Once used, the caller should dispose of the iterator by calling
+ *	  dispose_eclass_member_iterator().
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	relids - The Relids used to filter for relevant child members.
+ */
+void
+setup_eclass_member_iterator_with_children(EquivalenceMemberIterator *it,
+										   PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids)
+{
+	Bitmapset  *matching_indexes;
+	int			i;
+
+	/*
+	 * Initialize the iterator.
+	 */
+	it->index = -1;
+	it->list_is_copy = false;
+	it->ec_members = ec->ec_members;
+
+	/*
+	 * If there are no child relations, there is nothing to do. This
+	 * effectively avoids regression for non-partitioned cases.
+	 */
+	if (root->append_rel_array == NULL)
+		return;
+
+	/*
+	 * 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 '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 '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.
+	 */
+
+	/*
+	 * 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->append_rel_array[i] == NULL)
+			continue;
+
+		/*
+		 * Add child members that mention this relation to the iterator.
+		 */
+		child_rel = root->simple_rel_array[i];
+		if (child_rel != NULL)
+			add_eclass_child_members_to_iterator(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_eclass_child_members_to_iterator(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);
+}
+
+/*
+ * add_eclass_child_members_to_iterator
+ *	  Add a child EquivalenceMember referencing the given child_rel to
+ *	  the iterator from RelOptInfo.
+ *
+ * This function is expected to be called only from
+ * setup_eclass_member_iterator_with_children().
+ */
+static void
+add_eclass_child_members_to_iterator(EquivalenceMemberIterator *it,
+									 PlannerInfo *root,
+									 EquivalenceClass *ec,
+									 RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_ec != ec)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->list_is_copy)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->list_is_copy = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator_with_children(). NULL is
+ *	  returned if there are no members left.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	if (++it->index < list_length(it->ec_members))
+		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
+	return NULL;
+}
+
+/*
+ * dispose_eclass_member_iterator
+ *	  Free any memory allocated by the iterator.
+ */
+void
+dispose_eclass_member_iterator(EquivalenceMemberIterator *it)
+{
+	/*
+	 * XXX Should we use list_free()? I decided to use this style to take
+	 * advantage of speculative execution.
+	 */
+	if (unlikely(it->list_is_copy))
+		pfree(it->ec_members);
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3041,6 +3310,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3062,15 +3332,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator_with_children(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3085,8 +3354,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3304,8 +3573,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index a43ca16d683..c206f260633 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3739,7 +3739,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3756,7 +3756,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3776,9 +3777,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator_with_children(&it, root, pathkey->pk_eclass,
+												   index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3813,6 +3815,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..7791e282e9e 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,16 +119,23 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
-	/* append_rel_array is not needed if there are no AppendRelInfos */
+	/*
+	 * append_rel_array and eclass_indexes_array are not needed if there are
+	 * no AppendRelInfos.
+	 */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->eclass_indexes_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
 	 * which currently could only come from UNION ALL flattening.  We might
@@ -175,11 +182,21 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
+		Assert(root->eclass_indexes_array);
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->eclass_indexes_array =
+			repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
+		Assert(!root->eclass_indexes_array);
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		root->eclass_indexes_array =
+			palloc0_array(EquivalenceClassIndexes, new_size);
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -234,6 +251,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->join_rel_list_index = -1;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -626,6 +645,12 @@ set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 static void
 add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 {
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list);
+
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
@@ -741,6 +766,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->join_rel_list_index = -1;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +955,8 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->join_rel_list_index = -1;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1519,8 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->join_rel_list_index = -1;
+	upperrel->eclass_child_members = NIL;
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c24a1fc8514..6b3a028cc58 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -216,6 +216,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -272,6 +274,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster EquivalenceMember
+	 * lookups. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -984,6 +993,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	int			join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1403,6 +1423,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as setup_eclass_member_iterator_with_children() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1477,8 +1503,61 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* 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;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator is designed to iterate over all parent
+ * EquivalenceMembers and child members associated with the given 'ec' that
+ * are relevant to the specified 'relids'. In particular, it iterates over:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * PlannerInfo					   *root = given;
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator_with_children(&it, root, ec, rel);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_member_iterator(&it);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		list_is_copy;	/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 84a000a3ef1..6b80c8665b9 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator_with_children(EquivalenceMemberIterator *it,
+													   PlannerInfo *root,
+													   EquivalenceClass *ec,
+													   Relids relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
+extern void dispose_eclass_member_iterator(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index bfa276d2d35..c9688aa6b67 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -700,7 +700,9 @@ EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.45.2.windows.1

v35-0003-Resolve-conflict-with-commit-66c0185.patchapplication/octet-stream; name=v35-0003-Resolve-conflict-with-commit-66c0185.patchDownload
From 791e56b943ef8afa520548644c5e16712bdb0708 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH v35 3/5] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 42 +++++++++++++++++++++----
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8cce165e95d..8c54db6959b 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3006,6 +3006,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
 
 		if (tle->resjunk)
@@ -3023,12 +3024,41 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  /* parent_em, */ /* Will be fixed in the next commit*/
-					  exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		child_rel->eclass_child_members =
+			lappend(child_rel->eclass_child_members, child_em);
+
+		/*
+		 * Make an UNION parent-child relationship between parent_em and
+		 * child_rel->relid. We record this relationship in
+		 * root->append_rel_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we create a dummy AppendRelInfo object to know that the
+		 * relation of 'child_rel->relid' is a child and there are some
+		 * children because root->append_rel_array is not NULL. However this
+		 * is just a workaround. We must consider better way.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		if (root->append_rel_array == NULL)
+		{
+			root->append_rel_array = palloc0_array(AppendRelInfo *, root->simple_rel_array_size);
+			root->eclass_indexes_array = palloc0_array(EquivalenceClassIndexes, root->simple_rel_array_size);
+		}
+		root->append_rel_array[child_rel->relid] = makeNode(AppendRelInfo);
+		root->append_rel_array[child_rel->relid]->parent_relid =
+			bms_next_member(parent_em->em_relids, -1);
+		root->append_rel_array[child_rel->relid]->child_relid =
+			child_rel->relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.45.2.windows.1

v35-0004-Introduce-indexes-for-RestrictInfo.patchapplication/octet-stream; name=v35-0004-Introduce-indexes-for-RestrictInfo.patchDownload
From d2c2544edad0ccb6726bdd5a6579b677a9ca73c9 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH v35 4/5] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 517 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c | 132 ++++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/relnode.c      |  26 +-
 src/backend/optimizer/util/restrictinfo.c |   7 +
 src/backend/rewrite/rewriteManip.c        |   5 +-
 src/include/nodes/pathnodes.h             |  54 ++-
 src/include/optimizer/paths.h             |  20 +-
 12 files changed, 675 insertions(+), 99 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index f6f77b8fe19..c474c7a06af 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5848,7 +5848,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8c54db6959b..01832935211 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -319,7 +323,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -330,6 +333,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -350,8 +355,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -363,10 +370,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -377,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -394,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -411,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -420,8 +430,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -443,6 +453,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -584,6 +596,166 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -705,8 +877,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1065,7 +1237,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1158,6 +1330,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1167,9 +1340,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1228,9 +1401,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1240,7 +1413,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1307,7 +1481,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1364,11 +1538,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1409,11 +1584,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1781,12 +1956,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1798,12 +1977,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1865,10 +2044,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1879,9 +2059,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1892,9 +2075,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1967,7 +2154,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2694,16 +2881,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3050,10 +3240,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * parent_em->em_relids has two or more members?
 		 */
 		if (root->append_rel_array == NULL)
-		{
 			root->append_rel_array = palloc0_array(AppendRelInfo *, root->simple_rel_array_size);
-			root->eclass_indexes_array = palloc0_array(EquivalenceClassIndexes, root->simple_rel_array_size);
-		}
 		root->append_rel_array[child_rel->relid] = makeNode(AppendRelInfo);
 		root->append_rel_array[child_rel->relid]->parent_relid =
 			bms_next_member(parent_em->em_relids, -1);
@@ -3493,7 +3680,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3581,7 +3768,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3738,3 +3925,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 8a8d4a2af33..cc3fec7c718 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -58,7 +58,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   SpecialJoinInfo *sjinfo,
 								   int relid, int subst);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
@@ -473,7 +473,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			(sjinfo == NULL || bms_is_member(sjinfo->ojrelid, ec->ec_relids)))
-			remove_rel_from_eclass(ec, sjinfo, relid, subst);
+			remove_rel_from_eclass(root, ec, sjinfo, relid, subst);
 	}
 
 	/*
@@ -640,17 +640,25 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 static void
 remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -699,10 +707,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
-					   int relid, int subst)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
+					   SpecialJoinInfo *sjinfo, int relid, int subst)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, relid, subst);
@@ -734,9 +743,10 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (sjinfo == NULL)
 			ChangeVarNodes((Node *) rinfo, relid, subst, 0);
@@ -749,7 +759,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1504,10 +1514,12 @@ is_innerrel_unique_for(PlannerInfo *root,
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
@@ -1544,17 +1556,50 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * index.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+			Assert(bms_is_member(i, indexes->derive_indexes));
+			indexes->derive_indexes =
+				bms_del_member(indexes->derive_indexes, i);
+		}
+
+		/*
+		 * Can't delete the element because we would need to rebuild all the
+		 * eq_derives indexes. But set NULL to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach_node(RestrictInfo, rinfo, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1565,8 +1610,11 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach_node(RestrictInfo, other, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
+
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
 
@@ -1577,13 +1625,47 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Remove all references between this RestrictInfo and its
+			 * relating index.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+				Assert(bms_is_member(i, indexes->source_indexes));
+				indexes->source_indexes =
+					bms_del_member(indexes->source_indexes, i);
+			}
+
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_derives indexes. But set NULL to detect potential
+			 * problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources,
+			 * we must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/* Make sure that we didn't break EquivalenceClass indexes */
+	verify_eclass_indexes(root, ec);
+#endif
 }
 
 /*
@@ -1749,7 +1831,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 141177e7413..2b58862e4cd 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -666,6 +666,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index d131a5bbc59..15293ddbcbd 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1296,6 +1296,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 5b3dc0d8653..1e543f705a0 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -512,6 +512,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 7791e282e9e..aa37dda82b8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -120,22 +120,23 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	}
 
 	/*
-	 * append_rel_array and eclass_indexes_array are not needed if there are
-	 * no AppendRelInfos.
+	 * eclass_indexes_array is always needed, even if there are no
+	 * AppendRelInfos, because it stores indexes for RestrictInfos on base
+	 * rels.
 	 */
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
+	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
-		root->eclass_indexes_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 
-	root->eclass_indexes_array = (EquivalenceClassIndexes *)
-		palloc0(size * sizeof(EquivalenceClassIndexes));
-
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
 	 * which currently could only come from UNION ALL flattening.  We might
@@ -182,21 +183,14 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
-	{
-		Assert(root->eclass_indexes_array);
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
-		root->eclass_indexes_array =
-			repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
-	}
 	else
-	{
-		Assert(!root->eclass_indexes_array);
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
-		root->eclass_indexes_array =
-			palloc0_array(EquivalenceClassIndexes, new_size);
-	}
+
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
 
 	root->simple_rel_array_size = new_size;
 }
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..e011da4f6bc 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,11 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
+	restrictinfo->root = root;
+
 	return restrictinfo;
 }
 
@@ -394,6 +399,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 1c61085a0a7..92faa486225 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -18,6 +18,7 @@
 #include "nodes/nodeFuncs.h"
 #include "nodes/pathnodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/paths.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
@@ -649,8 +650,8 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 			expression_tree_walker((Node *) rinfo->clause, ChangeVarNodes_walker, (void *) context);
 			expression_tree_walker((Node *) rinfo->orclause, ChangeVarNodes_walker, (void *) context);
 
-			rinfo->clause_relids =
-				adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index);
+			update_clause_relids(rinfo,
+								 adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index));
 			rinfo->num_base_rels = bms_num_members(rinfo->clause_relids);
 			rinfo->left_relids =
 				adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6b3a028cc58..391d83169bb 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -346,6 +346,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1429,6 +1435,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as setup_eclass_member_iterator_with_children() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1447,8 +1471,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1548,14 +1574,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2705,7 +2737,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2823,6 +2860,13 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
+
+	/* PlannerInfo where this RestrictInfo was created */
+	PlannerInfo *root pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6b80c8665b9..fba939ed855 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -132,6 +132,7 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -168,7 +169,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +206,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.45.2.windows.1

#108David Rowley
dgrowleyml@gmail.com
In reply to: Yuya Watari (#107)
Re: [PoC] Reducing planning time when tables have many partitions

Thank you for addressing those comments.

On Mon, 24 Mar 2025 at 12:24, Yuya Watari <watari.yuya@gmail.com> wrote:

It is true that currently, indexes for EquivalenceMembers do not store
information about base rels. However, the subsequent commit (v35-0004)
introduces indexes for base rels to enable faster RestrictInfo
lookups. Therefore, if we commit the later patch as well, the comment
will remain accurate. What do you think about this?

I understand Ashutosh would like to handle the RestrictInfo speedup
another way, so there's additional review work to do there to
determine the merits of each method and figure out the best method.
I'm worried that means we don't get to fix this part for v18 and if
that happens and 0002 goes in alone, then we'd be left with a struct
with a single field. Maybe you should adjust the patch series and
only introduce the new struct in 0004 where it's required.

I agree it's challenging to redesign the union planner at this stage
of the release cycle. For now, I have proposed a workaround solution
(v35-0003, previously v34-0002). Would this workaround be acceptable
for the current release cycle?

I think something like that is probably ok. You have a problem with
your implementation as you're trying to add the AppendRelInfo once for
each child_tlist element rather than once per union child. Can you fix
this and incorporate into the 0002 patch please?

Here are some more review comments for v35-0002:

1. I don't think the header comment for eclass_member_iterator_next()
needs to mention setup_eclass_member_iterator_with_children(). The
renaming you did in v35 is meant to make it so the
eclass_member_iterator_next and dispose_eclass_member_iterator()
functions don't care about what set up the iterator. We might end up
with new ones in the future and this seems like a comment that might
not get updated when that happens.

2. You should use list_free() in the following:

/*
* XXX Should we use list_free()? I decided to use this style to take
* advantage of speculative execution.
*/
if (unlikely(it->list_is_copy))
pfree(it->ec_members);

The reason is that you're wrongly assuming that calling pfree on the
List pointer is enough to get rid of all memory used by the list. The
List may have a separately allocated elements[] array (this happens
when there's > 5 elements) which you're leaking with the current code.

I assume the speculative execution comment is there because you want
to omit the "list == NULL" check in list_free_private. Is this
measurable, performance-wise?

3. Maybe I'm missing something, but I'm confused about the need for
the eclass_indexes_array field in PlannerInfo. This array is indexed
by the relid, so why can't we get rid of the array and add a field to
RelOptInfo to store the EquivalenceClassIndexes?

4. Could you also please run another set of benchmarks against current
master with the the v36 patches: master, master + v36-0001 + 0002,
master + v36-0001 + 0002 + 0003 (0003 will be the v34-0004 patch), and
then also with v36-0004 (which is the same as v35-0005). The main
thing I'd like to understand here is if there's not enough time to get
the entire patch set committed, is there much benefit to just having
the EquivalenceMember index stuff in by itself without the
RestrictInfo changes.

David

#109Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#108)
Re: [PoC] Reducing planning time when tables have many partitions

David Rowley <dgrowleyml@gmail.com> writes:

... The main
thing I'd like to understand here is if there's not enough time to get
the entire patch set committed, is there much benefit to just having
the EquivalenceMember index stuff in by itself without the
RestrictInfo changes.

I finally made some time to look at this patchset, and I'm pretty
disappointed, because after 35 versions I'd expect to see something
that looks close to committable. This doesn't really. I like the
basic idea of taking child EC members out of ECs' main ec_members
lists, but there are too many weird details and
underexplained/overcomplicated/unmaintainable data structures.

One thing I don't love is putting the children into RelOptInfos.
That seems like an unrelated data structure. Have you thought
about instead having, in each EC that needs it, an array indexed
by RTI of per-relation child-member lists? I think this might
net out as less storage because there typically aren't that many
ECs in a query. But the main thing is to not have so many
interconnections between ECs and RelOptInfos.

Another thing I really don't like is the back-link from EMs to ECs:

+ EquivalenceClass *em_ec; /* EquivalenceClass which has this member */

That makes the data structure circular, which will cause pprint to
recurse infinitely. (The fact that you hadn't noticed that makes
me wonder how you debugged any of these data structure changes.)
We could prevent the recursion with suitable annotation on this field,
but I'd really rather not have the field in the first place. Circular
pointers are dangerous and best avoided. Also, it's bloating a node
type that you are concerned about supporting a lot of. Another point
is that I don't see any code to take care of updating these links
during an EC merge.

Some thoughts about the iterator stuff:

* setup_eclass_member_iterator_with_children is a carpal-tunnel-inducing
name. Could we drop the "_with_children" part? It doesn't seem to
add much, since there's no variant for "without children".

* The root parameter should be first; IMO there should be no
exceptions to that within the planner. Perhaps putting the target
iterator parameter last would make it read more nicely. Or you could
rely on struct assignment:

it = setup_eclass_member_iterator(root, ec, relids);

* Why did you define the iterator as possibly returning irrelevant
members? Doesn't that mean that every caller has to double-check?
Wouldn't it make for less code and fewer bugs for the iterator to
have that responsibility? If there is a good reason to do it like
that, the comments should explain why.

I don't really like the concept of 0004 at all. Putting *all*
the EC-related RelOptInfos into a root-stored list seems to be
doubling down very hard on the assumption that no performance-critical
operations will ever need to search that whole list. Is there a good
reason to do it like that, rather than say using the bitmap-index
concept separately within each EC? That might also alleviate the
problem you're having with the bitmapsets getting too big.

Given that we've only got a week left, I see little hope of getting
any of this into v18.

regards, tom lane

#110Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: David Rowley (#108)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, Mar 24, 2025 at 11:08 AM David Rowley <dgrowleyml@gmail.com> wrote:

2. You should use list_free() in the following:

/*
* XXX Should we use list_free()? I decided to use this style to take
* advantage of speculative execution.
*/
if (unlikely(it->list_is_copy))
pfree(it->ec_members);

The reason is that you're wrongly assuming that calling pfree on the
List pointer is enough to get rid of all memory used by the list. The
List may have a separately allocated elements[] array (this happens
when there's > 5 elements) which you're leaking with the current code.

I assume the speculative execution comment is there because you want
to omit the "list == NULL" check in list_free_private. Is this
measurable, performance-wise?

Here are memory consumption numbers using list_free() instead of pfree(),
using the same method as [1]/messages/by-id/CAExHW5vNk4f8VSTnizL-avREYhPgocEtH3E7MCEJgp=R33ChyQ@mail.gmail.com, using a binary without asserts and debug
info. PFA the patchset where all the patches are the same as v35 but with
an extra patch fixing memory leak. The memory leak is visible with a higher
number of joins. At a lower number of joins, I expect that the memory saved
is less than a KB or the leaked memory fits within 1 chunk of memory
context and hence not visible.

rows by number of partitions
columns by number of joins
each cell is a triplet, s = memory saving in %, mm - memory consumed
without fix, pm = memory consumed with fix
with PWJ = off
num_parts | 2 | 3
| 4 | 5
-----------+---------------------------------+---------------------------------+---------------------------------+---------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21 kB
| s=0.00%, mm=27 kB, pm=27 kB | s=0.00%, mm=34 kB, pm=34 kB
10 | s=0.00%, mm=231 kB, pm=231 kB | s=0.00%, mm=485 kB, pm=485
kB | s=0.00%, mm=924 kB, pm=924 kB | s=2.21%, mm=1901 kB, pm=1859 kB
100 | s=0.00%, mm=1965 kB, pm=1965 kB | s=0.00%, mm=4082 kB, pm=4082
kB | s=0.00%, mm=7115 kB, pm=7115 kB | s=3.35%, mm=12 MB, pm=12 MB
500 | s=0.00%, mm=10 MB, pm=10 MB | s=0.00%, mm=23 MB, pm=23 MB
| s=0.00%, mm=42 MB, pm=42 MB | s=2.58%, mm=80 MB, pm=78 MB
1000 | s=0.00%, mm=22 MB, pm=22 MB | s=0.00%, mm=55 MB, pm=55 MB
| s=0.00%, mm=107 MB, pm=107 MB | s=1.97%, mm=209 MB, pm=205 MB

without PWJ = on
num_parts | 2 | 3
| 4 | 5
-----------+---------------------------------+---------------------------------+---------------------------------+---------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21 kB
| s=0.00%, mm=27 kB, pm=27 kB | s=0.00%, mm=34 kB, pm=34 kB
10 | s=0.00%, mm=379 kB, pm=379 kB | s=0.00%, mm=1228 kB, pm=1228
kB | s=0.00%, mm=3628 kB, pm=3628 kB | s=0.40%, mm=10 MB, pm=10 MB
100 | s=0.00%, mm=3478 kB, pm=3478 kB | s=0.00%, mm=11 MB, pm=11 MB
| s=0.00%, mm=34 MB, pm=34 MB | s=0.41%, mm=99 MB, pm=99 MB
500 | s=0.00%, mm=18 MB, pm=18 MB | s=0.00%, mm=62 MB, pm=62 MB
| s=0.00%, mm=186 MB, pm=186 MB | s=0.37%, mm=564 MB, pm=562 MB
1000 | s=0.00%, mm=38 MB, pm=38 MB | s=0.00%, mm=139 MB, pm=139
MB | s=0.00%, mm=420 MB, pm=420 MB | s=0.32%, mm=1297 MB, pm=1293 MB

But overall the patches consume more memory than before as seen from
measurements below
Each cell is a triplet (s, mm, pm) where s = memory saving in % (-ve
indicates that memory consumption has increased), mm = memory consumption
with no patches applied, pm = memory consumption with all patches applied
PWJ=off
num_parts | 2 | 3
| 4 | 5

-----------+----------------------------------+----------------------------------+-----------------------------------+----------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21 kB
| s=0.00%, mm=27 kB, pm=27 kB | s=-3.03%, mm=33 kB, pm=34 kB
10 | s=-5.96%, mm=218 kB, pm=231 kB | s=-6.59%, mm=455 kB, pm=485
kB | s=-6.45%, mm=868 kB, pm=924 kB | s=-9.55%, mm=1697 kB, pm=1859 kB
100 | s=-7.73%, mm=1824 kB, pm=1965 kB | s=-9.79%, mm=3718 kB,
pm=4082 kB | s=-11.17%, mm=6400 kB, pm=7115 kB | s=-19.04%, mm=10233 kB,
pm=12 MB
500 | s=-10.91%, mm=9395 kB, pm=10 MB | s=-16.99%, mm=20 MB, pm=23
MB | s=-21.14%, mm=35 MB, pm=42 MB | s=-31.14%, mm=59 MB, pm=78 MB
1000 | s=-14.33%, mm=19 MB, pm=22 MB | s=-23.95%, mm=45 MB, pm=55
MB | s=-29.77%, mm=82 MB, pm=107 MB | s=-40.45%, mm=146 MB, pm=205 MB

PWJ=on
num_parts | 2 | 3
| 4 | 5
-----------+----------------------------------+----------------------------------+----------------------------------+----------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21 kB
| s=0.00%, mm=27 kB, pm=27 kB | s=-3.03%, mm=33 kB, pm=34 kB
10 | s=-3.84%, mm=365 kB, pm=379 kB | s=-2.50%, mm=1198 kB,
pm=1228 kB | s=-1.60%, mm=3571 kB, pm=3628 kB | s=-1.55%, mm=10 MB, pm=10 MB
100 | s=-4.23%, mm=3337 kB, pm=3478 kB | s=-3.25%, mm=11 MB, pm=11
MB | s=-2.11%, mm=33 MB, pm=34 MB | s=-1.96%, mm=97 MB, pm=99 MB
500 | s=-5.96%, mm=17 MB, pm=18 MB | s=-5.71%, mm=59 MB, pm=62
MB | s=-4.12%, mm=179 MB, pm=186 MB | s=-3.40%, mm=544 MB, pm=562 MB
1000 | s=-7.88%, mm=35 MB, pm=38 MB | s=-8.33%, mm=128 MB, pm=139
MB | s=-6.19%, mm=395 MB, pm=420 MB | s=-4.79%, mm=1234 MB, pm=1293 MB

In the case of PWJ = on, the % wise memory consumption is less because
memory consumption without fixes is huge and the patch adds on top of it.
But without PWJ, the memory consumption is high, especially at higher
number of joins and higher number of partitions.

[1]: /messages/by-id/CAExHW5vNk4f8VSTnizL-avREYhPgocEtH3E7MCEJgp=R33ChyQ@mail.gmail.com
/messages/by-id/CAExHW5vNk4f8VSTnizL-avREYhPgocEtH3E7MCEJgp=R33ChyQ@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

#111Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Ashutosh Bapat (#110)
6 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

FWIW here's patch set

On Thu, Mar 27, 2025 at 10:12 AM Ashutosh Bapat <
ashutosh.bapat.oss@gmail.com> wrote:

On Mon, Mar 24, 2025 at 11:08 AM David Rowley <dgrowleyml@gmail.com>
wrote:

2. You should use list_free() in the following:

/*
* XXX Should we use list_free()? I decided to use this style to take
* advantage of speculative execution.
*/
if (unlikely(it->list_is_copy))
pfree(it->ec_members);

The reason is that you're wrongly assuming that calling pfree on the
List pointer is enough to get rid of all memory used by the list. The
List may have a separately allocated elements[] array (this happens
when there's > 5 elements) which you're leaking with the current code.

I assume the speculative execution comment is there because you want
to omit the "list == NULL" check in list_free_private. Is this
measurable, performance-wise?

Here are memory consumption numbers using list_free() instead of pfree(),
using the same method as [1], using a binary without asserts and debug
info. PFA the patchset where all the patches are the same as v35 but with
an extra patch fixing memory leak. The memory leak is visible with a higher
number of joins. At a lower number of joins, I expect that the memory saved
is less than a KB or the leaked memory fits within 1 chunk of memory
context and hence not visible.

rows by number of partitions
columns by number of joins
each cell is a triplet, s = memory saving in %, mm - memory consumed
without fix, pm = memory consumed with fix
with PWJ = off
num_parts | 2 | 3
| 4 | 5

-----------+---------------------------------+---------------------------------+---------------------------------+---------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21 kB
| s=0.00%, mm=27 kB, pm=27 kB | s=0.00%, mm=34 kB, pm=34 kB
10 | s=0.00%, mm=231 kB, pm=231 kB | s=0.00%, mm=485 kB, pm=485
kB | s=0.00%, mm=924 kB, pm=924 kB | s=2.21%, mm=1901 kB, pm=1859 kB
100 | s=0.00%, mm=1965 kB, pm=1965 kB | s=0.00%, mm=4082 kB,
pm=4082 kB | s=0.00%, mm=7115 kB, pm=7115 kB | s=3.35%, mm=12 MB, pm=12 MB
500 | s=0.00%, mm=10 MB, pm=10 MB | s=0.00%, mm=23 MB, pm=23 MB
| s=0.00%, mm=42 MB, pm=42 MB | s=2.58%, mm=80 MB, pm=78 MB
1000 | s=0.00%, mm=22 MB, pm=22 MB | s=0.00%, mm=55 MB, pm=55 MB
| s=0.00%, mm=107 MB, pm=107 MB | s=1.97%, mm=209 MB, pm=205 MB

without PWJ = on
num_parts | 2 | 3
| 4 | 5

-----------+---------------------------------+---------------------------------+---------------------------------+---------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21 kB
| s=0.00%, mm=27 kB, pm=27 kB | s=0.00%, mm=34 kB, pm=34 kB
10 | s=0.00%, mm=379 kB, pm=379 kB | s=0.00%, mm=1228 kB,
pm=1228 kB | s=0.00%, mm=3628 kB, pm=3628 kB | s=0.40%, mm=10 MB, pm=10 MB
100 | s=0.00%, mm=3478 kB, pm=3478 kB | s=0.00%, mm=11 MB, pm=11 MB
| s=0.00%, mm=34 MB, pm=34 MB | s=0.41%, mm=99 MB, pm=99 MB
500 | s=0.00%, mm=18 MB, pm=18 MB | s=0.00%, mm=62 MB, pm=62 MB
| s=0.00%, mm=186 MB, pm=186 MB | s=0.37%, mm=564 MB, pm=562 MB
1000 | s=0.00%, mm=38 MB, pm=38 MB | s=0.00%, mm=139 MB, pm=139
MB | s=0.00%, mm=420 MB, pm=420 MB | s=0.32%, mm=1297 MB, pm=1293 MB

But overall the patches consume more memory than before as seen from
measurements below
Each cell is a triplet (s, mm, pm) where s = memory saving in % (-ve
indicates that memory consumption has increased), mm = memory consumption
with no patches applied, pm = memory consumption with all patches applied
PWJ=off
num_parts | 2 | 3
| 4 | 5

-----------+----------------------------------+----------------------------------+-----------------------------------+----------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21
kB | s=0.00%, mm=27 kB, pm=27 kB | s=-3.03%, mm=33 kB, pm=34 kB
10 | s=-5.96%, mm=218 kB, pm=231 kB | s=-6.59%, mm=455 kB,
pm=485 kB | s=-6.45%, mm=868 kB, pm=924 kB | s=-9.55%, mm=1697 kB,
pm=1859 kB
100 | s=-7.73%, mm=1824 kB, pm=1965 kB | s=-9.79%, mm=3718 kB,
pm=4082 kB | s=-11.17%, mm=6400 kB, pm=7115 kB | s=-19.04%, mm=10233 kB,
pm=12 MB
500 | s=-10.91%, mm=9395 kB, pm=10 MB | s=-16.99%, mm=20 MB, pm=23
MB | s=-21.14%, mm=35 MB, pm=42 MB | s=-31.14%, mm=59 MB, pm=78 MB
1000 | s=-14.33%, mm=19 MB, pm=22 MB | s=-23.95%, mm=45 MB, pm=55
MB | s=-29.77%, mm=82 MB, pm=107 MB | s=-40.45%, mm=146 MB, pm=205 MB

PWJ=on
num_parts | 2 | 3
| 4 | 5

-----------+----------------------------------+----------------------------------+----------------------------------+----------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB, pm=21
kB | s=0.00%, mm=27 kB, pm=27 kB | s=-3.03%, mm=33 kB, pm=34 kB
10 | s=-3.84%, mm=365 kB, pm=379 kB | s=-2.50%, mm=1198 kB,
pm=1228 kB | s=-1.60%, mm=3571 kB, pm=3628 kB | s=-1.55%, mm=10 MB, pm=10 MB
100 | s=-4.23%, mm=3337 kB, pm=3478 kB | s=-3.25%, mm=11 MB, pm=11
MB | s=-2.11%, mm=33 MB, pm=34 MB | s=-1.96%, mm=97 MB, pm=99 MB
500 | s=-5.96%, mm=17 MB, pm=18 MB | s=-5.71%, mm=59 MB, pm=62
MB | s=-4.12%, mm=179 MB, pm=186 MB | s=-3.40%, mm=544 MB, pm=562 MB
1000 | s=-7.88%, mm=35 MB, pm=38 MB | s=-8.33%, mm=128 MB,
pm=139 MB | s=-6.19%, mm=395 MB, pm=420 MB | s=-4.79%, mm=1234 MB,
pm=1293 MB

In the case of PWJ = on, the % wise memory consumption is less because
memory consumption without fixes is huge and the patch adds on top of it.
But without PWJ, the memory consumption is high, especially at higher
number of joins and higher number of partitions.

[1]
/messages/by-id/CAExHW5vNk4f8VSTnizL-avREYhPgocEtH3E7MCEJgp=R33ChyQ@mail.gmail.com

--
Best Wishes,
Ashutosh Bapat

--
Best Wishes,
Ashutosh Bapat

Attachments:

0003-Resolve-conflict-with-commit-66c0185-20250327.patchtext/x-patch; charset=US-ASCII; name=0003-Resolve-conflict-with-commit-66c0185-20250327.patchDownload
From f32a414312bbd1804099945484aaf4f71e46fbe2 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Tue, 27 Aug 2024 13:20:29 +0900
Subject: [PATCH 3/6] Resolve conflict with commit 66c0185

This commit resolves a conflict with 66c0185, which added
add_setop_child_rel_equivalences().

The function creates child EquivalenceMembers to efficiently implement
UNION queries. This commit adjusts our optimization to handle this type
of child EquivalenceMembers based on UNION parent-child relationships.
---
 src/backend/optimizer/path/equivclass.c | 42 +++++++++++++++++++++----
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8cce165e95d..8c54db6959b 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3006,6 +3006,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 	{
 		TargetEntry *tle = lfirst_node(TargetEntry, lc);
 		EquivalenceMember *parent_em;
+		EquivalenceMember *child_em;
 		PathKey    *pk;
 
 		if (tle->resjunk)
@@ -3023,12 +3024,41 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * likewise, the JoinDomain can be that of the initial member of the
 		 * Pathkey's EquivalenceClass.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  /* parent_em, */ /* Will be fixed in the next commit*/
-					  exprType((Node *) tle->expr));
+		child_em = make_eq_member(pk->pk_eclass,
+								  tle->expr,
+								  child_rel->relids,
+								  parent_em->em_jdomain,
+								  parent_em,
+								  exprType((Node *) tle->expr));
+		child_rel->eclass_child_members =
+			lappend(child_rel->eclass_child_members, child_em);
+
+		/*
+		 * Make an UNION parent-child relationship between parent_em and
+		 * child_rel->relid. We record this relationship in
+		 * root->append_rel_array, which generally has AppendRelInfo
+		 * relationships. We use the same array here to retrieve UNION child
+		 * members.
+		 *
+		 * XXX Here we create a dummy AppendRelInfo object to know that the
+		 * relation of 'child_rel->relid' is a child and there are some
+		 * children because root->append_rel_array is not NULL. However this
+		 * is just a workaround. We must consider better way.
+		 *
+		 * XXX Here we treat the first member of parent_em->em_relids as a
+		 * parent of child_rel. Is this correct? What happens if
+		 * parent_em->em_relids has two or more members?
+		 */
+		if (root->append_rel_array == NULL)
+		{
+			root->append_rel_array = palloc0_array(AppendRelInfo *, root->simple_rel_array_size);
+			root->eclass_indexes_array = palloc0_array(EquivalenceClassIndexes, root->simple_rel_array_size);
+		}
+		root->append_rel_array[child_rel->relid] = makeNode(AppendRelInfo);
+		root->append_rel_array[child_rel->relid]->parent_relid =
+			bms_next_member(parent_em->em_relids, -1);
+		root->append_rel_array[child_rel->relid]->child_relid =
+			child_rel->relid;
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
-- 
2.34.1

0004-Introduce-indexes-for-RestrictInfo-20250327.patchtext/x-patch; charset=US-ASCII; name=0004-Introduce-indexes-for-RestrictInfo-20250327.patchDownload
From 4405cc45f5d731fee43a395a9a42780c68e7fa1a Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:44:25 +0900
Subject: [PATCH 4/6] Introduce indexes for RestrictInfo

This commit adds indexes to speed up searches for RestrictInfos. When
there are many child partitions and we have to perform planning for join
operations on the tables, we have to handle a large number of
RestrictInfos, resulting in a long planning time.

This commit adds ec_[source|derive]_indexes to speed up the search. We
can use the indexes to filter out unwanted RestrictInfos, and improve
the planning performance.

Author: David Rowley <dgrowley@gmail.com> and me, and includes rebase by
Alena Rybakina <lena.ribackina@yandex.ru> [1].

[1] https://www.postgresql.org/message-id/72d292a1-06ff-432a-a803-af5053786444%40yandex.ru
---
 src/backend/nodes/outfuncs.c              |   4 +-
 src/backend/optimizer/path/costsize.c     |   3 +-
 src/backend/optimizer/path/equivclass.c   | 517 ++++++++++++++++++++--
 src/backend/optimizer/plan/analyzejoins.c | 132 ++++--
 src/backend/optimizer/plan/planner.c      |   2 +
 src/backend/optimizer/prep/prepjointree.c |   2 +
 src/backend/optimizer/util/appendinfo.c   |   2 +
 src/backend/optimizer/util/relnode.c      |  26 +-
 src/backend/optimizer/util/restrictinfo.c |   7 +
 src/backend/rewrite/rewriteManip.c        |   5 +-
 src/include/nodes/pathnodes.h             |  54 ++-
 src/include/optimizer/paths.h             |  20 +-
 12 files changed, 675 insertions(+), 99 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..206e65d157d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,8 +466,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
-	WRITE_NODE_FIELD(ec_sources);
-	WRITE_NODE_FIELD(ec_derives);
+	WRITE_BITMAPSET_FIELD(ec_source_indexes);
+	WRITE_BITMAPSET_FIELD(ec_derive_indexes);
 	WRITE_BITMAPSET_FIELD(ec_relids);
 	WRITE_BOOL_FIELD(ec_has_const);
 	WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index f6f77b8fe19..c474c7a06af 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5848,7 +5848,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root,
 				if (ec && ec->ec_has_const)
 				{
 					EquivalenceMember *em = fkinfo->fk_eclass_member[i];
-					RestrictInfo *rinfo = find_derived_clause_for_ec_member(ec,
+					RestrictInfo *rinfo = find_derived_clause_for_ec_member(root,
+																			ec,
 																			em);
 
 					if (rinfo)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 8c54db6959b..01832935211 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -42,6 +42,10 @@ static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
 										Oid datatype);
+static void add_eq_source(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
+static void add_eq_derive(PlannerInfo *root, EquivalenceClass *ec,
+						  RestrictInfo *rinfo);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -319,7 +323,6 @@ process_equivalence(PlannerInfo *root,
 		/* If case 1, nothing to do, except add to sources */
 		if (ec1 == ec2)
 		{
-			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 			ec1->ec_min_security = Min(ec1->ec_min_security,
 									   restrictinfo->security_level);
 			ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -330,6 +333,8 @@ process_equivalence(PlannerInfo *root,
 			/* mark the RI as usable with this pair of EMs */
 			restrictinfo->left_em = em1;
 			restrictinfo->right_em = em2;
+
+			add_eq_source(root, ec1, restrictinfo);
 			return true;
 		}
 
@@ -350,8 +355,10 @@ process_equivalence(PlannerInfo *root,
 		 * be found.
 		 */
 		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
-		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
-		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
+		ec1->ec_source_indexes = bms_join(ec1->ec_source_indexes,
+										  ec2->ec_source_indexes);
+		ec1->ec_derive_indexes = bms_join(ec1->ec_derive_indexes,
+										  ec2->ec_derive_indexes);
 		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
 		ec1->ec_has_const |= ec2->ec_has_const;
 		/* can't need to set has_volatile */
@@ -363,10 +370,9 @@ process_equivalence(PlannerInfo *root,
 		root->eq_classes = list_delete_nth_cell(root->eq_classes, ec2_idx);
 		/* just to avoid debugging confusion w/ dangling pointers: */
 		ec2->ec_members = NIL;
-		ec2->ec_sources = NIL;
-		ec2->ec_derives = NIL;
+		ec2->ec_source_indexes = NULL;
+		ec2->ec_derive_indexes = NULL;
 		ec2->ec_relids = NULL;
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -377,13 +383,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
 							jdomain, item2_type);
-		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
 		ec1->ec_max_security = Max(ec1->ec_max_security,
@@ -394,13 +401,14 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec1, restrictinfo);
 	}
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
 							jdomain, item1_type);
-		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
 		ec2->ec_max_security = Max(ec2->ec_max_security,
@@ -411,6 +419,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec2, restrictinfo);
 	}
 	else
 	{
@@ -420,8 +430,8 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
 		ec->ec_members = NIL;
-		ec->ec_sources = list_make1(restrictinfo);
-		ec->ec_derives = NIL;
+		ec->ec_source_indexes = NULL;
+		ec->ec_derive_indexes = NULL;
 		ec->ec_relids = NULL;
 		ec->ec_has_const = false;
 		ec->ec_has_volatile = false;
@@ -443,6 +453,8 @@ process_equivalence(PlannerInfo *root,
 		/* mark the RI as usable with this pair of EMs */
 		restrictinfo->left_em = em1;
 		restrictinfo->right_em = em2;
+
+		add_eq_source(root, ec, restrictinfo);
 	}
 
 	return true;
@@ -584,6 +596,166 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	return em;
 }
 
+/*
+ * add_eq_source - add 'rinfo' in eq_sources for this 'ec'
+ */
+static void
+add_eq_source(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			source_idx = list_length(root->eq_sources);
+	int			i;
+
+	ec->ec_source_indexes = bms_add_member(ec->ec_source_indexes, source_idx);
+	root->eq_sources = lappend(root->eq_sources, rinfo);
+	Assert(rinfo->eq_sources_index == -1);
+	rinfo->eq_sources_index = source_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->source_indexes = bms_add_member(index->source_indexes,
+											   source_idx);
+	}
+}
+
+/*
+ * add_eq_derive - add 'rinfo' in eq_derives for this 'ec'
+ */
+static void
+add_eq_derive(PlannerInfo *root, EquivalenceClass *ec, RestrictInfo *rinfo)
+{
+	int			derive_idx = list_length(root->eq_derives);
+	int			i;
+
+	ec->ec_derive_indexes = bms_add_member(ec->ec_derive_indexes, derive_idx);
+	root->eq_derives = lappend(root->eq_derives, rinfo);
+	Assert(rinfo->eq_derives_index == -1);
+	rinfo->eq_derives_index = derive_idx;
+
+	i = -1;
+	while ((i = bms_next_member(rinfo->clause_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		index->derive_indexes = bms_add_member(index->derive_indexes,
+											   derive_idx);
+	}
+}
+
+/*
+ * update_clause_relids
+ *	  Update RestrictInfo->clause_relids with adjusting the relevant indexes.
+ *	  The clause_relids field must be updated through this function, not by
+ *	  setting the field directly.
+ */
+void
+update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids)
+{
+	int			i;
+	Relids		removing_relids;
+	Relids		adding_relids;
+#ifdef USE_ASSERT_CHECKING
+	Relids		common_relids;
+#endif
+
+	/*
+	 * If there is nothing to do, we return immediately after setting the
+	 * clause_relids field.
+	 */
+	if (rinfo->eq_sources_index == -1 && rinfo->eq_derives_index == -1)
+	{
+		rinfo->clause_relids = new_clause_relids;
+		return;
+	}
+
+	/*
+	 * Remove any references between this RestrictInfo and RangeTblEntries
+	 * that are no longer relevant.
+	 */
+	removing_relids = bms_difference(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(removing_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+			index->source_indexes =
+				bms_del_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+			index->derive_indexes =
+				bms_del_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(removing_relids);
+
+	/*
+	 * Add references between this RestrictInfo and RangeTblEntries that will
+	 * be relevant.
+	 */
+	adding_relids = bms_difference(new_clause_relids, rinfo->clause_relids);
+	i = -1;
+	while ((i = bms_next_member(adding_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_sources_index,
+								  index->source_indexes));
+			index->source_indexes =
+				bms_add_member(index->source_indexes,
+							   rinfo->eq_sources_index);
+		}
+		if (rinfo->eq_derives_index != -1)
+		{
+			Assert(!bms_is_member(rinfo->eq_derives_index,
+								  index->derive_indexes));
+			index->derive_indexes =
+				bms_add_member(index->derive_indexes,
+							   rinfo->eq_derives_index);
+		}
+	}
+	bms_free(adding_relids);
+
+#ifdef USE_ASSERT_CHECKING
+
+	/*
+	 * Verify that all indexes are set for common Relids.
+	 */
+	common_relids = bms_intersect(rinfo->clause_relids, new_clause_relids);
+	i = -1;
+	while ((i = bms_next_member(common_relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &rinfo->root->eclass_indexes_array[i];
+
+		if (rinfo->eq_sources_index != -1)
+			Assert(bms_is_member(rinfo->eq_sources_index,
+								 index->source_indexes));
+		if (rinfo->eq_derives_index != -1)
+			Assert(bms_is_member(rinfo->eq_derives_index,
+								 index->derive_indexes));
+	}
+	bms_free(common_relids);
+#endif
+
+	/*
+	 * Since we have done everything to adjust the indexes, we can set the
+	 * clause_relids field.
+	 */
+	rinfo->clause_relids = new_clause_relids;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -705,8 +877,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
 	newec->ec_members = NIL;
-	newec->ec_sources = NIL;
-	newec->ec_derives = NIL;
+	newec->ec_source_indexes = NULL;
+	newec->ec_derive_indexes = NULL;
 	newec->ec_relids = NULL;
 	newec->ec_has_const = false;
 	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
@@ -1065,7 +1237,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * don't search eq_sources or eq_derives for matches.  It doesn't really
  * seem worth the trouble to do so.
  */
 void
@@ -1158,6 +1330,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 {
 	EquivalenceMember *const_em = NULL;
 	ListCell   *lc;
+	int			i;
 
 	/*
 	 * In the trivial case where we just had one "var = const" clause, push
@@ -1167,9 +1340,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	 * equivalent to the old one.
 	 */
 	if (list_length(ec->ec_members) == 2 &&
-		list_length(ec->ec_sources) == 1)
+		bms_get_singleton_member(ec->ec_source_indexes, &i))
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		distribute_restrictinfo_to_rels(root, restrictinfo);
 		return;
@@ -1228,9 +1401,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 
 		/*
 		 * If the clause didn't degenerate to a constant, fill in the correct
-		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * markings for a mergejoinable clause, and save it in eq_derives. (We
 		 * will not re-use such clauses directly, but selectivity estimation
-		 * may consult the list later.  Note that this use of ec_derives does
+		 * may consult the list later.  Note that this use of eq_derives does
 		 * not overlap with its use for join clauses, since we never generate
 		 * join clauses from an ec_has_const eclass.)
 		 */
@@ -1240,7 +1413,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			rinfo->left_ec = rinfo->right_ec = ec;
 			rinfo->left_em = cur_em;
 			rinfo->right_em = const_em;
-			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+
+			add_eq_derive(root, ec, rinfo);
 		}
 	}
 }
@@ -1307,7 +1481,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			/*
 			 * If the clause didn't degenerate to a constant, fill in the
 			 * correct markings for a mergejoinable clause.  We don't put it
-			 * in ec_derives however; we don't currently need to re-find such
+			 * in eq_derives however; we don't currently need to re-find such
 			 * clauses, and we don't want to clutter that list with non-join
 			 * clauses.
 			 */
@@ -1364,11 +1538,12 @@ static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
 										EquivalenceClass *ec)
 {
-	ListCell   *lc;
+	int			i = -1;
 
-	foreach(lc, ec->ec_sources)
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *restrictinfo = list_nth_node(RestrictInfo,
+												   root->eq_sources, i);
 
 		if (ec->ec_has_const ||
 			bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
@@ -1409,11 +1584,11 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
  * whenever we select a particular pair of EquivalenceMembers to join,
- * we check to see if the pair matches any original clause (in ec_sources)
- * or previously-built clause (in ec_derives).  This saves memory and allows
- * re-use of information cached in RestrictInfos.  We also avoid generating
- * commutative duplicates, i.e. if the algorithm selects "a.x = b.y" but
- * we already have "b.y = a.x", we return the existing clause.
+ * we check to see if the pair matches any original clause in root's
+ * eq_sources or previously-built clause (in root's eq_derives).  This saves
+ * memory and allows re-use of information cached in RestrictInfos.  We also
+ * avoid generating commutative duplicates, i.e. if the algorithm selects
+ * "a.x = b.y" but we already have "b.y = a.x", we return the existing clause.
  *
  * If we are considering an outer join, sjinfo is the associated OJ info,
  * otherwise it can be NULL.
@@ -1781,12 +1956,16 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 										Relids nominal_inner_relids,
 										RelOptInfo *inner_rel)
 {
+	Bitmapset  *matching_es;
 	List	   *result = NIL;
-	ListCell   *lc;
+	int			i;
 
-	foreach(lc, ec->ec_sources)
+	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
+	i = -1;
+	while ((i = bms_next_member(matching_es, i)) >= 0)
 	{
-		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+		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) &&
@@ -1798,12 +1977,12 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
 	 * to all the RestrictInfos at once.  This will result in returning
-	 * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+	 * RestrictInfos that are not listed in eq_derives, but there shouldn't be
 	 * any duplication, and it's a sufficiently narrow corner case that we
 	 * shouldn't sweat too much over it anyway.
 	 *
 	 * Since inner_rel might be an indirect descendant of the baserel
-	 * mentioned in the ec_sources clauses, we have to be prepared to apply
+	 * mentioned in the eq_sources clauses, we have to be prepared to apply
 	 * multiple levels of Var translation.
 	 */
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
@@ -1865,10 +2044,11 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
+	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
-	ListCell   *lc;
 	MemoryContext oldcontext;
+	int			i;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -1879,9 +2059,12 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	foreach(lc, ec->ec_sources)
+	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1892,9 +2075,13 @@ create_join_clause(PlannerInfo *root,
 			return rinfo;
 	}
 
-	foreach(lc, ec->ec_derives)
+	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
+							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
+
+	i = -1;
+	while ((i = bms_next_member(matches, i)) >= 0)
 	{
-		rinfo = (RestrictInfo *) lfirst(lc);
+		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -1967,7 +2154,7 @@ create_join_clause(PlannerInfo *root,
 	rinfo->left_em = leftem;
 	rinfo->right_em = rightem;
 	/* and save it for possible re-use */
-	ec->ec_derives = lappend(ec->ec_derives, rinfo);
+	add_eq_derive(root, ec, rinfo);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -2694,16 +2881,19 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
  * Returns NULL if no such clause can be found.
  */
 RestrictInfo *
-find_derived_clause_for_ec_member(EquivalenceClass *ec,
+find_derived_clause_for_ec_member(PlannerInfo *root, EquivalenceClass *ec,
 								  EquivalenceMember *em)
 {
-	ListCell   *lc;
+	int			i;
 
 	Assert(ec->ec_has_const);
 	Assert(!em->em_is_const);
-	foreach(lc, ec->ec_derives)
+
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
 
 		/*
 		 * generate_base_implied_equalities_const will have put non-const
@@ -3050,10 +3240,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * parent_em->em_relids has two or more members?
 		 */
 		if (root->append_rel_array == NULL)
-		{
 			root->append_rel_array = palloc0_array(AppendRelInfo *, root->simple_rel_array_size);
-			root->eclass_indexes_array = palloc0_array(EquivalenceClassIndexes, root->simple_rel_array_size);
-		}
 		root->append_rel_array[child_rel->relid] = makeNode(AppendRelInfo);
 		root->append_rel_array[child_rel->relid]->parent_relid =
 			bms_next_member(parent_em->em_relids, -1);
@@ -3493,7 +3680,7 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
 		 * surely be true if both of them overlap ec_relids.)
 		 *
 		 * Note we don't test ec_broken; if we did, we'd need a separate code
-		 * path to look through ec_sources.  Checking the membership anyway is
+		 * path to look through eq_sources.  Checking the membership anyway is
 		 * OK as a possibly-overoptimistic heuristic.
 		 *
 		 * We don't test ec_has_const either, even though a const eclass won't
@@ -3581,7 +3768,7 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
-	 * to look through ec_sources.  Checking the members anyway is OK as a
+	 * to look through eq_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
@@ -3738,3 +3925,239 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 	/* Calculate and return the common EC indexes, recycling the left input. */
 	return bms_int_members(rel1ecs, rel2ecs);
 }
+
+/*
+ * get_ec_source_indexes
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_esis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_esis = bms_add_members(rel_esis, index->source_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_esis, ec->ec_source_indexes);
+}
+
+/*
+ * get_ec_source_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_sources for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *esis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		esis = bms_intersect(ec->ec_source_indexes,
+							 index->source_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			esis = bms_int_members(esis, index->source_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(esis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return esis;
+}
+
+/*
+ * get_ec_derive_indexes
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_overlap(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ *
+ * XXX is this function even needed?
+ */
+Bitmapset *
+get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+{
+	Bitmapset  *rel_edis = NULL;
+	int			i = -1;
+
+	while ((i = bms_next_member(relids, i)) >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(rel_edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_overlap(relids, rinfo->clause_relids));
+	}
+#endif
+
+	/* bitwise-AND to leave only the ones for this EquivalenceClass */
+	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+}
+
+/*
+ * get_ec_derive_indexes_strict
+ *		Returns a Bitmapset with indexes into root->eq_derives for all
+ *		RestrictInfos in 'ec' that have
+ *		bms_is_subset(relids, rinfo->clause_relids) as true.
+ *
+ * Returns a newly allocated Bitmapset which the caller is free to modify.
+ */
+Bitmapset *
+get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
+							 Relids relids)
+{
+	Bitmapset  *edis = NULL;
+	int			i = bms_next_member(relids, -1);
+
+	if (i >= 0)
+	{
+		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+
+		/*
+		 * bms_intersect to the first relation to try to keep the resulting
+		 * Bitmapset as small as possible.  This saves having to make a
+		 * complete bms_copy() of one of them.  One may contain significantly
+		 * more words than the other.
+		 */
+		edis = bms_intersect(ec->ec_derive_indexes,
+							 index->derive_indexes);
+
+		while ((i = bms_next_member(relids, i)) >= 0)
+		{
+			index = &root->eclass_indexes_array[i];
+			edis = bms_int_members(edis, index->derive_indexes);
+		}
+	}
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the results look sane */
+	i = -1;
+	while ((i = bms_next_member(edis, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
+											i);
+
+		Assert(bms_is_subset(relids, rinfo->clause_relids));
+	}
+#endif
+
+	return edis;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * verify_eclass_indexes
+ *		Verify that there are no missing references between RestrictInfos and
+ *		EquivalenceMember's indexes, namely source_indexes and derive_indexes.
+ *		If you modify these indexes, you should check them with this function.
+ */
+void
+verify_eclass_indexes(PlannerInfo *root, EquivalenceClass *ec)
+{
+	ListCell   *lc;
+
+	/*
+	 * All RestrictInfos in root->eq_sources must have references to
+	 * source_indexes.
+	 */
+	foreach(lc, root->eq_sources)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_sources, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].source_indexes));
+		}
+	}
+
+	/*
+	 * All RestrictInfos in root->eq_derives must have references to
+	 * derive_indexes.
+	 */
+	foreach(lc, root->eq_derives)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+		int			index;
+		int			k;
+
+		/* Deleted members are marked NULL */
+		if (rinfo == NULL)
+			continue;
+
+		index = list_cell_number(root->eq_derives, lc);
+		k = -1;
+		while ((k = bms_next_member(rinfo->clause_relids, k)) >= 0)
+		{
+			/* must have a reference */
+			Assert(bms_is_member(index, root->eclass_indexes_array[k].derive_indexes));
+		}
+	}
+}
+#endif
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 8a8d4a2af33..cc3fec7c718 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -58,7 +58,7 @@ static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 										  SpecialJoinInfo *sjinfo);
 static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
 										 int relid, int ojrelid);
-static void remove_rel_from_eclass(EquivalenceClass *ec,
+static void remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
 								   SpecialJoinInfo *sjinfo,
 								   int relid, int subst);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
@@ -473,7 +473,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
 
 		if (bms_is_member(relid, ec->ec_relids) ||
 			(sjinfo == NULL || bms_is_member(sjinfo->ojrelid, ec->ec_relids)))
-			remove_rel_from_eclass(ec, sjinfo, relid, subst);
+			remove_rel_from_eclass(root, ec, sjinfo, relid, subst);
 	}
 
 	/*
@@ -640,17 +640,25 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 static void
 remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 {
+	Relids		new_clause_relids;
+
 	/*
 	 * initsplan.c is fairly cavalier about allowing RestrictInfos to share
 	 * relid sets with other RestrictInfos, and SpecialJoinInfos too.  Make
 	 * sure this RestrictInfo has its own relid sets before we modify them.
-	 * (In present usage, clause_relids is probably not shared, but
-	 * required_relids could be; let's not assume anything.)
+	 * The 'new_clause_relids' passed to update_clause_relids() must be a
+	 * different instance from the current rinfo->clause_relids, so we make a
+	 * copy of it.
+	 */
+	new_clause_relids = bms_copy(rinfo->clause_relids);
+	new_clause_relids = bms_del_member(new_clause_relids, relid);
+	new_clause_relids = bms_del_member(new_clause_relids, ojrelid);
+	update_clause_relids(rinfo, new_clause_relids);
+
+	/*
+	 * In present usage, required_relids could be shared, so we make a copy of
+	 * it.
 	 */
-	rinfo->clause_relids = bms_copy(rinfo->clause_relids);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);
-	rinfo->clause_relids = bms_del_member(rinfo->clause_relids, ojrelid);
-	/* Likewise for required_relids */
 	rinfo->required_relids = bms_copy(rinfo->required_relids);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
 	rinfo->required_relids = bms_del_member(rinfo->required_relids, ojrelid);
@@ -699,10 +707,11 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
  * level(s).
  */
 static void
-remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
-					   int relid, int subst)
+remove_rel_from_eclass(PlannerInfo *root, EquivalenceClass *ec,
+					   SpecialJoinInfo *sjinfo, int relid, int subst)
 {
 	ListCell   *lc;
+	int			i;
 
 	/* Fix up the EC's overall relids */
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, relid, subst);
@@ -734,9 +743,10 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	}
 
 	/* Fix up the source clauses, in case we can re-use them later */
-	foreach(lc, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 
 		if (sjinfo == NULL)
 			ChangeVarNodes((Node *) rinfo, relid, subst, 0);
@@ -749,7 +759,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 	 * drop them.  (At this point, any such clauses would be base restriction
 	 * clauses, which we'd not need anymore anyway.)
 	 */
-	ec->ec_derives = NIL;
+	ec->ec_derive_indexes = NULL;
 }
 
 /*
@@ -1504,10 +1514,12 @@ is_innerrel_unique_for(PlannerInfo *root,
  * delete them.
  */
 static void
-update_eclasses(EquivalenceClass *ec, int from, int to)
+update_eclasses(PlannerInfo *root, EquivalenceClass *ec, int from, int to)
 {
 	List	   *new_members = NIL;
-	List	   *new_sources = NIL;
+	Bitmapset  *new_source_indexes = NULL;
+	int			i;
+	int			j;
 
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
@@ -1544,17 +1556,50 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	list_free(ec->ec_members);
 	ec->ec_members = new_members;
 
-	list_free(ec->ec_derives);
-	ec->ec_derives = NULL;
+	i = -1;
+	while ((i = bms_next_member(ec->ec_derive_indexes, i)) >= 0)
+	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
+
+		/*
+		 * Remove all references between this RestrictInfo and its relating
+		 * index.
+		 */
+		j = -1;
+		while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+		{
+			EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+			Assert(bms_is_member(i, indexes->derive_indexes));
+			indexes->derive_indexes =
+				bms_del_member(indexes->derive_indexes, i);
+		}
+
+		/*
+		 * Can't delete the element because we would need to rebuild all the
+		 * eq_derives indexes. But set NULL to detect potential problems.
+		 */
+		list_nth_cell(root->eq_derives, i)->ptr_value = NULL;
+
+		/*
+		 * Since this RestrictInfo no longer exists in root->eq_derives, we
+		 * must reset the stored index.
+		 */
+		rinfo->eq_derives_index = -1;
+	}
+	bms_free(ec->ec_derive_indexes);
+	ec->ec_derive_indexes = NULL;
 
 	/* Update EC source expressions */
-	foreach_node(RestrictInfo, rinfo, ec->ec_sources)
+	i = -1;
+	while ((i = bms_next_member(ec->ec_source_indexes, i)) >= 0)
 	{
+		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		bool		is_redundant = false;
 
 		if (!bms_is_member(from, rinfo->required_relids))
 		{
-			new_sources = lappend(new_sources, rinfo);
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 			continue;
 		}
 
@@ -1565,8 +1610,11 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 		 * redundancy with existing ones. We don't have to check for
 		 * redundancy with derived clauses, because we've just deleted them.
 		 */
-		foreach_node(RestrictInfo, other, new_sources)
+		j = -1;
+		while ((j = bms_next_member(new_source_indexes, j)) >= 0)
 		{
+			RestrictInfo *other = list_nth_node(RestrictInfo, root->eq_sources, j);
+
 			if (!equal(rinfo->clause_relids, other->clause_relids))
 				continue;
 
@@ -1577,13 +1625,47 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 			}
 		}
 
-		if (!is_redundant)
-			new_sources = lappend(new_sources, rinfo);
+		if (is_redundant)
+		{
+			/*
+			 * Remove all references between this RestrictInfo and its
+			 * relating index.
+			 */
+			j = -1;
+			while ((j = bms_next_member(rinfo->clause_relids, j)) >= 0)
+			{
+				EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[j];
+
+				Assert(bms_is_member(i, indexes->source_indexes));
+				indexes->source_indexes =
+					bms_del_member(indexes->source_indexes, i);
+			}
+
+			/*
+			 * Can't delete the element because we would need to rebuild all
+			 * the eq_derives indexes. But set NULL to detect potential
+			 * problems.
+			 */
+			list_nth_cell(root->eq_sources, i)->ptr_value = NULL;
+
+			/*
+			 * Since this RestrictInfo no longer exists in root->eq_sources,
+			 * we must reset the stored index.
+			 */
+			rinfo->eq_sources_index = -1;
+		}
+		else
+			new_source_indexes = bms_add_member(new_source_indexes, i);
 	}
 
-	list_free(ec->ec_sources);
-	ec->ec_sources = new_sources;
+	bms_free(ec->ec_source_indexes);
+	ec->ec_source_indexes = new_source_indexes;
 	ec->ec_relids = adjust_relid_set(ec->ec_relids, from, to);
+
+#ifdef USE_ASSERT_CHECKING
+	/* Make sure that we didn't break EquivalenceClass indexes */
+	verify_eclass_indexes(root, ec);
+#endif
 }
 
 /*
@@ -1749,7 +1831,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 
-		update_eclasses(ec, toRemove->relid, toKeep->relid);
+		update_eclasses(root, ec, toRemove->relid, toKeep->relid);
 		toKeep->eclass_indexes = bms_add_member(toKeep->eclass_indexes, i);
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 566ce5b3cb4..76bd8a66ec0 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -670,6 +670,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
 	root->multiexpr_params = NIL;
 	root->join_domains = NIL;
 	root->eq_classes = NIL;
+	root->eq_sources = NIL;
+	root->eq_derives = NIL;
 	root->ec_merging_done = false;
 	root->last_rinfo_serial = 0;
 	root->all_result_relids =
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index d131a5bbc59..15293ddbcbd 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1296,6 +1296,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->multiexpr_params = NIL;
 	subroot->join_domains = NIL;
 	subroot->eq_classes = NIL;
+	subroot->eq_sources = NIL;
+	subroot->eq_derives = NIL;
 	subroot->ec_merging_done = false;
 	subroot->last_rinfo_serial = 0;
 	subroot->all_result_relids = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 5b3dc0d8653..1e543f705a0 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -512,6 +512,8 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->eq_sources_index = -1;
+		newinfo->eq_derives_index = -1;
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 7791e282e9e..aa37dda82b8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -120,22 +120,23 @@ setup_simple_rel_arrays(PlannerInfo *root)
 	}
 
 	/*
-	 * append_rel_array and eclass_indexes_array are not needed if there are
-	 * no AppendRelInfos.
+	 * eclass_indexes_array is always needed, even if there are no
+	 * AppendRelInfos, because it stores indexes for RestrictInfos on base
+	 * rels.
 	 */
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
+	/* append_rel_array is not needed if there are no AppendRelInfos */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
-		root->eclass_indexes_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 
-	root->eclass_indexes_array = (EquivalenceClassIndexes *)
-		palloc0(size * sizeof(EquivalenceClassIndexes));
-
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
 	 * which currently could only come from UNION ALL flattening.  We might
@@ -182,21 +183,14 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
-	{
-		Assert(root->eclass_indexes_array);
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
-		root->eclass_indexes_array =
-			repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
-	}
 	else
-	{
-		Assert(!root->eclass_indexes_array);
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
-		root->eclass_indexes_array =
-			palloc0_array(EquivalenceClassIndexes, new_size);
-	}
+
+	root->eclass_indexes_array =
+		repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
 
 	root->simple_rel_array_size = new_size;
 }
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..e011da4f6bc 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -238,6 +238,11 @@ make_plain_restrictinfo(PlannerInfo *root,
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
 
+	restrictinfo->eq_sources_index = -1;
+	restrictinfo->eq_derives_index = -1;
+
+	restrictinfo->root = root;
+
 	return restrictinfo;
 }
 
@@ -394,6 +399,8 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
 	result->right_mcvfreq = rinfo->left_mcvfreq;
 	result->left_hasheqoperator = InvalidOid;
 	result->right_hasheqoperator = InvalidOid;
+	result->eq_sources_index = -1;
+	result->eq_derives_index = -1;
 
 	return result;
 }
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 1c61085a0a7..92faa486225 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -18,6 +18,7 @@
 #include "nodes/nodeFuncs.h"
 #include "nodes/pathnodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/paths.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
@@ -649,8 +650,8 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 			expression_tree_walker((Node *) rinfo->clause, ChangeVarNodes_walker, (void *) context);
 			expression_tree_walker((Node *) rinfo->orclause, ChangeVarNodes_walker, (void *) context);
 
-			rinfo->clause_relids =
-				adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index);
+			update_clause_relids(rinfo,
+								 adjust_relid_set(rinfo->clause_relids, context->rt_index, context->new_index));
 			rinfo->num_base_rels = bms_num_members(rinfo->clause_relids);
 			rinfo->left_relids =
 				adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6b3a028cc58..391d83169bb 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -346,6 +346,12 @@ struct PlannerInfo
 	/* list of active EquivalenceClasses */
 	List	   *eq_classes;
 
+	/* list of source RestrictInfos used to build EquivalenceClasses */
+	List	   *eq_sources;
+
+	/* list of RestrictInfos derived from EquivalenceClasses */
+	List	   *eq_derives;
+
 	/* set true once ECs are canonical */
 	bool		ec_merging_done;
 
@@ -1429,6 +1435,24 @@ typedef struct JoinDomain
  * its comment for usage. The approach to lookup child members quickly is
  * described as setup_eclass_member_iterator_with_children() comment.
  *
+ * At various locations in the query planner, we must search for source and
+ * derived RestrictInfos regarding a given EquivalenceClass.  For the common
+ * case, an EquivalenceClass does not have a large number of RestrictInfos,
+ * however, in cases such as planning queries to partitioned tables, the
+ * number of members can become large.  To maintain planning performance, we
+ * make use of a bitmap index to allow us to quickly find RestrictInfos in a
+ * given EquivalenceClass belonging to a given relation or set of relations.
+ * This is done by storing a list of RestrictInfos belonging to all
+ * EquivalenceClasses in PlannerInfo and storing a Bitmapset for each
+ * RelOptInfo which has a bit set for each RestrictInfo in that list which
+ * relates to the given relation.  We also store a Bitmapset to mark all of
+ * the indexes in the PlannerInfo's list of RestrictInfos in the
+ * EquivalenceClass.  We can quickly find the interesting indexes into the
+ * PlannerInfo's list by performing a bit-wise AND on the RelOptInfo's
+ * Bitmapset and the EquivalenceClasses.  RestrictInfos must be looked up in
+ * PlannerInfo by this technique using the ec_source_indexes and
+ * ec_derive_indexes Bitmapsets.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1447,8 +1471,10 @@ 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_sources;		/* list of generating RestrictInfos */
-	List	   *ec_derives;		/* list of derived RestrictInfos */
+	Bitmapset  *ec_source_indexes;	/* indexes into PlannerInfo's eq_sources
+									 * list of generating RestrictInfos */
+	Bitmapset  *ec_derive_indexes;	/* indexes into PlannerInfo's eq_derives
+									 * list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -1548,14 +1574,20 @@ typedef struct
  *
  * As mentioned in the EquivalenceClass comment, we introduce a
  * bitmapset-based indexing mechanism for faster lookups of child
- * EquivalenceMembers. This struct exists for each relation and holds the
- * corresponding indexes.
+ * EquivalenceMembers and RestrictInfos. This struct exists for each relation
+ * and holds the corresponding indexes.
  */
 typedef struct EquivalenceClassIndexes
 {
 	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
 									 * list for RelOptInfos that mention this
 									 * relation */
+	Bitmapset  *source_indexes; /* Indexes in PlannerInfo's eq_sources list
+								 * for RestrictInfos that mention this
+								 * relation */
+	Bitmapset  *derive_indexes; /* Indexes in PlannerInfo's eq_derives list
+								 * for RestrictInfos that mention this
+								 * relation */
 } EquivalenceClassIndexes;
 
 /*
@@ -2705,7 +2737,12 @@ typedef struct RestrictInfo
 	/* number of base rels in clause_relids */
 	int			num_base_rels pg_node_attr(equal_ignore);
 
-	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
+	/*
+	 * The relids (varnos+varnullingrels) actually referenced in the clause.
+	 *
+	 * NOTE: This field must be updated through update_clause_relids(), not by
+	 * setting the field directly.
+	 */
 	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
@@ -2823,6 +2860,13 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* the index within root->eq_sources and root->eq_derives */
+	int			eq_sources_index pg_node_attr(equal_ignore);
+	int			eq_derives_index pg_node_attr(equal_ignore);
+
+	/* PlannerInfo where this RestrictInfo was created */
+	PlannerInfo *root pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
 } RestrictInfo;
 
 /*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6b80c8665b9..fba939ed855 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -132,6 +132,7 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 										Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern void rebuild_eclass_attr_needed(PlannerInfo *root);
+extern void update_clause_relids(RestrictInfo *rinfo, Relids new_clause_relids);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
 												  List *opfamilies,
@@ -168,7 +169,8 @@ extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2,
 extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
 														   ForeignKeyOptInfo *fkinfo,
 														   int colno);
-extern RestrictInfo *find_derived_clause_for_ec_member(EquivalenceClass *ec,
+extern RestrictInfo *find_derived_clause_for_ec_member(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   EquivalenceMember *em);
 extern void add_child_rel_equivalences(PlannerInfo *root,
 									   AppendRelInfo *appinfo,
@@ -204,6 +206,22 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
+extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
+										EquivalenceClass *ec,
+										Relids relids);
+extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
+											   EquivalenceClass *ec,
+											   Relids relids);
+#ifdef USE_ASSERT_CHECKING
+extern void verify_eclass_indexes(PlannerInfo *root,
+								  EquivalenceClass *ec);
+#endif
 
 /*
  * pathkeys.c
-- 
2.34.1

0005-Introduce-RestrictInfoIterator-to-reduce-me-20250327.patchtext/x-patch; charset=US-ASCII; name=0005-Introduce-RestrictInfoIterator-to-reduce-me-20250327.patchDownload
From 1e1393ed6af9d758c6ca6de0fda823c81437453f Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 20 Dec 2024 12:01:35 +0900
Subject: [PATCH 5/6] Introduce RestrictInfoIterator to reduce memory
 consumption

Originally, get_ec_[source|derive]_indexes[_strict]() functions created
temporary Bitmapsets each time they were called. This resulted in large
memory consumption.

This commit introduces a new iterator mechanism called
RestrictInfoIterator. This iterator iterates over RestrictInfos using
indexes without creating temporary Bitmapsets. Experimental results show
that this commit significantly reduces memory consumption.
---
 src/backend/nodes/bitmapset.c           |   3 +
 src/backend/optimizer/path/equivclass.c | 313 +++++++++++-------------
 src/include/nodes/pathnodes.h           |  38 +++
 src/include/optimizer/paths.h           |  20 +-
 src/tools/pgindent/typedefs.list        |   1 +
 5 files changed, 199 insertions(+), 176 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index bf512cf806f..5aecf96cbb7 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1301,6 +1301,9 @@ bms_join(Bitmapset *a, Bitmapset *b)
  * loop-not-started state (x == -1) from the loop-completed state (x == -2).
  * It makes no difference in simple loop usage, but complex iteration logic
  * might need such an ability.
+ *
+ * NOTE: The routine here is similar to eclass_rinfo_iterator_for_relids_next().
+ * If you change here, you should adjust the logic there.
  */
 int
 bms_next_member(const Bitmapset *a, int prevbit)
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 01832935211..1ffd2ce0a04 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1956,16 +1956,14 @@ 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 iter;
 
-	matching_es = get_ec_source_indexes(root, ec, nominal_join_relids);
-	i = -1;
-	while ((i = bms_next_member(matching_es, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, nominal_join_relids, true,
+										   false);
+	while ((restrictinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != 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) &&
@@ -1973,6 +1971,7 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 			!bms_is_subset(clause_relids, nominal_inner_relids))
 			result = lappend(result, restrictinfo);
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * If we have to translate, just brute-force apply adjust_appendrel_attrs
@@ -2044,11 +2043,10 @@ create_join_clause(PlannerInfo *root,
 				   EquivalenceMember *rightem,
 				   EquivalenceClass *parent_ec)
 {
-	Bitmapset  *matches;
 	RestrictInfo *rinfo;
 	RestrictInfo *parent_rinfo = NULL;
 	MemoryContext oldcontext;
-	int			i;
+	RestrictInfoIterator iter;
 
 	/*
 	 * Search to see if we already built a RestrictInfo for this pair of
@@ -2059,12 +2057,11 @@ create_join_clause(PlannerInfo *root,
 	 * it's not identical, it'd better have the same effects, or the operator
 	 * families we're using are broken.
 	 */
-	matches = bms_int_members(get_ec_source_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_source_indexes_strict(root, ec, rightem->em_relids));
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   true, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_sources, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2074,14 +2071,13 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
-	matches = bms_int_members(get_ec_derive_indexes_strict(root, ec, leftem->em_relids),
-							  get_ec_derive_indexes_strict(root, ec, rightem->em_relids));
-
-	i = -1;
-	while ((i = bms_next_member(matches, i)) >= 0)
+	setup_eclass_rinfo_iterator_for_relids(&iter, root, ec,
+										   bms_union(leftem->em_relids, rightem->em_relids),
+										   false, true);
+	while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
 	{
-		rinfo = list_nth_node(RestrictInfo, root->eq_derives, i);
 		if (rinfo->left_em == leftem &&
 			rinfo->right_em == rightem &&
 			rinfo->parent_ec == parent_ec)
@@ -2091,6 +2087,7 @@ create_join_clause(PlannerInfo *root,
 			rinfo->parent_ec == parent_ec)
 			return rinfo;
 	}
+	dispose_eclass_rinfo_iterator_for_relids(&iter);
 
 	/*
 	 * Not there, so build it, in planner context so we can re-use it. (Not
@@ -3927,179 +3924,167 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
 }
 
 /*
- * get_ec_source_indexes
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
+ * setup_eclass_rinfo_iterator_for_relids
+ *	  Setup a RestrictInfoIterator 'iter' to iterate over RestrictInfos
+ *	  associated with the given 'ec'. It iterates through root->eq_sources if
+ *	  'is_source' is true, or root->eq_derives otherwise. The members must be
+ *	  from 'ec' and satisfy "bms_is_subset(relids, rinfo->clause_relids)" if
+ *	  'is_strict' is true, or "bms_overlap(relids, rinfo->clause_relids)"
+ *	  otherwise.
  *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * Once used, the caller should dispose of the iterator by calling
+ * dispose_eclass_rinfo_iterator_for_relids().
  */
-Bitmapset *
-get_ec_source_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
+void
+setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter, PlannerInfo *root,
+									   EquivalenceClass *ec, Relids relids,
+									   bool is_source, bool is_strict)
 {
-	Bitmapset  *rel_esis = NULL;
-	int			i = -1;
-
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		rel_esis = bms_add_members(rel_esis, index->source_indexes);
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
-
-		Assert(bms_overlap(relids, rinfo->clause_relids));
-	}
-#endif
-
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_esis, ec->ec_source_indexes);
+	iter->root = root;
+	iter->ec = ec;
+	iter->relids = relids;
+	iter->is_source = is_source;
+	iter->is_strict = is_strict;
+	iter->last_word = 0;
+	iter->wordnum = -1;
+	iter->index = -1;
 }
 
 /*
- * get_ec_source_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_sources for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * eclass_rinfo_iterator_for_relids_next
+ *	  Get a next RestrictInfo from an RestrictInfoIterator 'iter' that was
+ *	  setup by setup_eclass_rinfo_iterator_for_relids(). NULL is returned if
+ *	  there are no members left.
  */
-Bitmapset *
-get_ec_source_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+RestrictInfo *
+eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter)
 {
-	Bitmapset  *esis = NULL;
-	int			i = bms_next_member(relids, -1);
+	RestrictInfo *rinfo;
+	bitmapword	mask;
+	bitmapword	w;
+	int			bitnum;
 
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+	if (iter->index <= -2)
+		return NULL;			/* Already finished iteration */
+
+	/*
+	 * The routine in this function is similar to bms_next_member(). If you
+	 * change its behavior, you should adjust the logic here.
+	 */
+	++(iter->index);
+	bitnum = iter->index % BITS_PER_BITMAPWORD;
+	mask = (~(bitmapword) 0) << bitnum;
+	w = iter->last_word & mask;
 
+	/*
+	 * Do we need to consume a new word?
+	 */
+	if (bitnum == 0 || w == 0)
+	{
 		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
+		 * Yes, we need a new word.
 		 */
-		esis = bms_intersect(ec->ec_source_indexes,
-							 index->source_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
+		while (true)
 		{
-			index = &root->eclass_indexes_array[i];
-			esis = bms_int_members(esis, index->source_indexes);
-		}
-	}
+			Bitmapset  *ec_members_index;
+			bitmapword	word;
+			int			i;
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(esis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_sources,
-											i);
+			iter->wordnum++;	/* Consume */
 
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
+			/*
+			 * Are there still members in ec?
+			 */
+			ec_members_index = iter->is_source ?
+				iter->ec->ec_source_indexes : iter->ec->ec_derive_indexes;
+			if (ec_members_index == NULL || iter->wordnum >= ec_members_index->nwords)
+			{
+				iter->index = -2;
+				return NULL;	/* No words left */
+			}
 
-	return esis;
-}
+			/*
+			 * We intersect the corresponding Bitmapset indexes for the
+			 * is_strict case or union them for the non-is_strict case.
+			 */
+			word = iter->is_strict ? (~(bitmapword) 0) : 0;
 
-/*
- * get_ec_derive_indexes
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_overlap(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
- *
- * XXX is this function even needed?
- */
-Bitmapset *
-get_ec_derive_indexes(PlannerInfo *root, EquivalenceClass *ec, Relids relids)
-{
-	Bitmapset  *rel_edis = NULL;
-	int			i = -1;
+			/*
+			 * Loop over 'relids' to intersect or union all indexes.
+			 */
+			i = -1;
+			while ((i = bms_next_member(iter->relids, i)) >= 0)
+			{
+				EquivalenceClassIndexes *ec_indexes =
+					&iter->root->eclass_indexes_array[i];
+				Bitmapset  *index = iter->is_source ?
+					ec_indexes->source_indexes : ec_indexes->derive_indexes;
 
-	while ((i = bms_next_member(relids, i)) >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
+				if (index == NULL || iter->wordnum >= index->nwords)
+				{
+					/* This word is zero. */
+					if (iter->is_strict)
+					{
+						word = 0;
+						break;	/* We don't need to do anything. */
+					}
+					else
+						continue;
+				}
 
-		rel_edis = bms_add_members(rel_edis, index->derive_indexes);
-	}
+				if (iter->is_strict)
+					word &= index->words[iter->wordnum];
+				else
+					word |= index->words[iter->wordnum];
+			}
 
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(rel_edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
+			/*
+			 * Leave only the members for this EquivalenceClass.
+			 */
+			word &= ec_members_index->words[iter->wordnum];
 
-		Assert(bms_overlap(relids, rinfo->clause_relids));
+			/*
+			 * Did we find new members?
+			 */
+			if (word != 0)
+			{
+				/* Yes, we find new ones. */
+				w = iter->last_word = word;
+				break;
+			}
+			/* No, we need to consume more word */
+		}
 	}
+
+	/*
+	 * Here, 'iter->last_word' or 'w' must have a new member. We get it.
+	 */
+	Assert(w != 0);
+	iter->index =
+		iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w);
+	rinfo = list_nth_node(RestrictInfo,
+						  iter->is_source ? iter->root->eq_sources : iter->root->eq_derives,
+						  iter->index);
+
+#ifdef USE_ASSERT_CHECKING
+	/* verify the result look sane */
+	if (iter->is_strict)
+		Assert(bms_is_subset(iter->relids, rinfo->clause_relids));
+	else
+		Assert(bms_overlap(iter->relids, rinfo->clause_relids));
 #endif
 
-	/* bitwise-AND to leave only the ones for this EquivalenceClass */
-	return bms_int_members(rel_edis, ec->ec_derive_indexes);
+	return rinfo;
 }
 
 /*
- * get_ec_derive_indexes_strict
- *		Returns a Bitmapset with indexes into root->eq_derives for all
- *		RestrictInfos in 'ec' that have
- *		bms_is_subset(relids, rinfo->clause_relids) as true.
- *
- * Returns a newly allocated Bitmapset which the caller is free to modify.
+ * dispose_eclass_rinfo_iterator_for_relids
+ *	  Free any memory allocated by the iterator.
  */
-Bitmapset *
-get_ec_derive_indexes_strict(PlannerInfo *root, EquivalenceClass *ec,
-							 Relids relids)
+void
+dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter)
 {
-	Bitmapset  *edis = NULL;
-	int			i = bms_next_member(relids, -1);
-
-	if (i >= 0)
-	{
-		EquivalenceClassIndexes *index = &root->eclass_indexes_array[i];
-
-		/*
-		 * bms_intersect to the first relation to try to keep the resulting
-		 * Bitmapset as small as possible.  This saves having to make a
-		 * complete bms_copy() of one of them.  One may contain significantly
-		 * more words than the other.
-		 */
-		edis = bms_intersect(ec->ec_derive_indexes,
-							 index->derive_indexes);
-
-		while ((i = bms_next_member(relids, i)) >= 0)
-		{
-			index = &root->eclass_indexes_array[i];
-			edis = bms_int_members(edis, index->derive_indexes);
-		}
-	}
-
-#ifdef USE_ASSERT_CHECKING
-	/* verify the results look sane */
-	i = -1;
-	while ((i = bms_next_member(edis, i)) >= 0)
-	{
-		RestrictInfo *rinfo = list_nth_node(RestrictInfo, root->eq_derives,
-											i);
-
-		Assert(bms_is_subset(relids, rinfo->clause_relids));
-	}
-#endif
-
-	return edis;
+	/* Do nothing */
 }
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 391d83169bb..c7cf1328d7a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1569,6 +1569,44 @@ typedef struct
 	List	   *ec_members;		/* parent and child members */
 } EquivalenceMemberIterator;
 
+/*
+ * RestrictInfoIterator
+ *
+ * This iterator provides a way to iterate over RestrictInfos in an
+ * EquivalenceClass that satisfy a certain condition. You need to first
+ * initialize an iterator, and then use move next during the iteration. You
+ * can specify the condition during initialization. For more details, see the
+ * comment in setup_eclass_rinfo_iterator_for_relids().
+ *
+ * The most common way to use this struct is as follows:
+ * -----
+ * PlannerInfo		   *root = given;
+ * EquivalenceClass	   *ec = given;
+ * Relids				relids = given;
+ * RestrictInfoIterator iter;
+ * RestrictInfo		   *rinfo;
+ *
+ * setup_eclass_rinfo_iterator_for_relids(&iter, root, ec, relids, true, true);
+ * while ((rinfo = eclass_rinfo_iterator_for_relids_next(&iter)) != NULL)
+ * {
+ *     use rinfo ...;
+ * }
+ * dispose_eclass_rinfo_iterator_for_relids(&iter);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;
+	EquivalenceClass *ec;		/* EC where we are looking now */
+	Relids		relids;			/* Relids that we are checking */
+	bool		is_source;		/* Do we iterate over root->eq_sources? */
+	bool		is_strict;		/* Do we intersect the indexes? */
+	bitmapword	last_word;		/* Bitmapword at last iteration */
+	int			wordnum;		/* Wordnum at last iteration */
+	int			index;			/* The last RestrictInfo index we returned to
+								 * the caller */
+} RestrictInfoIterator;
+
 /*
  * EquivalenceClassIndexes
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index fba939ed855..13c739965ba 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -206,18 +206,14 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
 extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
 extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
 										   List *indexclauses);
-extern Bitmapset *get_ec_source_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_source_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
-extern Bitmapset *get_ec_derive_indexes(PlannerInfo *root,
-										EquivalenceClass *ec,
-										Relids relids);
-extern Bitmapset *get_ec_derive_indexes_strict(PlannerInfo *root,
-											   EquivalenceClass *ec,
-											   Relids relids);
+extern void setup_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter,
+												   PlannerInfo *root,
+												   EquivalenceClass *ec,
+												   Relids relids,
+												   bool is_source,
+												   bool is_strict);
+extern RestrictInfo *eclass_rinfo_iterator_for_relids_next(RestrictInfoIterator *iter);
+extern void dispose_eclass_rinfo_iterator_for_relids(RestrictInfoIterator *iter);
 #ifdef USE_ASSERT_CHECKING
 extern void verify_eclass_indexes(PlannerInfo *root,
 								  EquivalenceClass *ec);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 4b613ad34aa..479568a1090 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2532,6 +2532,7 @@ ResourceReleasePriority
 RestoreOptions
 RestorePass
 RestrictInfo
+RestrictInfoIterator
 Result
 ResultRelInfo
 ResultState
-- 
2.34.1

0001-Add-the-PlannerInfo-context-to-the-paramete-20250327.patchtext/x-patch; charset=US-ASCII; name=0001-Add-the-PlannerInfo-context-to-the-paramete-20250327.patchDownload
From bb8e129506c87fde0025906f6fb10e46d2f5c6d5 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH 1/6] Add the PlannerInfo context to the parameter of
 find_ec_member_matching_expr()

---
 src/backend/optimizer/path/equivclass.c |  4 +-
 src/backend/optimizer/plan/createplan.c | 64 +++++++++++++++----------
 src/include/optimizer/paths.h           |  3 +-
 3 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..7fe7cdff468 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -760,7 +760,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
@@ -939,7 +939,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 75e2b0b9036..163923072df 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -294,7 +298,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1281,7 +1285,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1325,7 +1329,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1466,7 +1470,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1497,7 +1501,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1979,7 +1983,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2193,7 +2197,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2217,7 +2221,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2286,7 +2290,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4544,7 +4548,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4553,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4578,7 +4584,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6195,6 +6201,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the plan node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' identifies the child relation being sorted, if any
@@ -6228,7 +6235,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6295,7 +6302,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6323,7 +6330,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6339,7 +6346,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6405,12 +6412,14 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6419,7 +6428,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6439,14 +6448,16 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  * make_incrementalsort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6455,7 +6466,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6812,7 +6823,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6875,7 +6887,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..84a000a3ef1 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,

base-commit: 44fe6ceb51f001689048c19ea3ea53fbf2572581
-- 
2.34.1

0002-Speed-up-searches-for-child-EquivalenceMemb-20250327.patchtext/x-patch; charset=US-ASCII; name=0002-Speed-up-searches-for-child-EquivalenceMemb-20250327.patchDownload
From 1cced8ea42e7750f6b619ec93bdbf53830762926 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH 2/6] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/optimizer/path/equivclass.c | 435 +++++++++++++++++++-----
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/backend/optimizer/util/relnode.c    |  33 +-
 src/include/nodes/pathnodes.h           |  79 +++++
 src/include/optimizer/paths.h           |   6 +
 src/tools/pgindent/typedefs.list        |   2 +
 8 files changed, 493 insertions(+), 101 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index ac14c06c715..f959c5ff893 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7865,6 +7864,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -7918,9 +7918,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fe7cdff468..8cce165e95d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,10 +33,14 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
@@ -68,6 +72,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 static bool reconsider_full_join_clause(PlannerInfo *root,
 										OuterJoinClauseInfo *ojcinfo);
 static JoinDomain *find_join_domain(PlannerInfo *root, Relids relids);
+static void add_eclass_child_members_to_iterator(EquivalenceMemberIterator *it,
+												 PlannerInfo *root,
+												 EquivalenceClass *ec,
+												 RelOptInfo *child_rel);
 static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
 												Relids relids);
 static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
@@ -268,7 +276,8 @@ process_equivalence(PlannerInfo *root,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -373,7 +382,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -390,7 +399,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -422,9 +431,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +519,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -525,6 +539,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	em->em_datatype = datatype;
 	em->em_jdomain = jdomain;
 	em->em_parent = parent;
+	em->em_ec = ec;
 
 	if (bms_is_empty(relids))
 	{
@@ -545,11 +560,30 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 	{
 		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
 
+/*
+ * add_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(),
+ * add_child_join_rel_equivalences() and add_setop_child_rel_equivalences()
+ * to see how to add child members.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	return em;
+}
+
 
 /*
  * get_eclass_for_sort_expr
@@ -616,7 +650,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +666,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator_with_children(&it, root, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +687,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -690,7 +725,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -764,15 +799,16 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +835,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +878,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -855,9 +893,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -901,6 +939,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -1162,7 +1201,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1231,7 +1271,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1564,7 +1605,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1575,10 +1617,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator_with_children(&it, root, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1635,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1611,6 +1653,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1685,6 +1728,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1692,7 +1736,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2181,7 +2225,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2308,7 +2353,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2526,8 +2572,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2598,8 +2644,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2696,6 +2742,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2708,7 +2755,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,29 +2767,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2759,6 +2791,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* OK, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2788,9 +2821,11 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_rel->eclass_child_members =
+					lappend(child_rel->eclass_child_members, child_em);
 
 				/* Record this EC index for the child rel */
 				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
@@ -2840,7 +2875,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,25 +2888,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2886,6 +2911,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
+				EquivalenceMember *child_em;
+				int			j;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2916,9 +2943,38 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				child_em = make_eq_member(cur_ec, child_expr, new_relids,
+										  cur_em->em_jdomain,
+										  cur_em, cur_em->em_datatype);
+				child_joinrel->eclass_child_members =
+					lappend(child_joinrel->eclass_child_members, child_em);
+
+				/*
+				 * Update the corresponding inverted indexes.
+				 */
+				j = -1;
+				while ((j = bms_next_member(child_joinrel->relids, j)) >= 0)
+				{
+					EquivalenceClassIndexes *indexes =
+						&root->eclass_indexes_array[j];
+
+					/*
+					 * We do not need to update the inverted index of the top
+					 * parent relations. This is because EquivalenceMembers
+					 * 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
+					 * EquivalenceMemberIterator. Since the iterator needs
+					 * EquivalenceMembers whose em_relids has child relations,
+					 * skipping the update of this inverted index allows for
+					 * faster iteration.
+					 */
+					if (root->append_rel_array[j] == NULL)
+						continue;
+					indexes->joinrel_indexes =
+						bms_add_member(indexes->joinrel_indexes,
+									   child_joinrel->join_rel_list_index);
+				}
 			}
 		}
 	}
@@ -2971,7 +3027,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 					  tle->expr,
 					  child_rel->relids,
 					  parent_em->em_jdomain,
-					  parent_em,
+					  /* parent_em, */ /* Will be fixed in the next commit*/
 					  exprType((Node *) tle->expr));
 
 		lc2 = lnext(setop_pathkeys, lc2);
@@ -2987,6 +3043,219 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator_with_children
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members associated with the given 'ec' that
+ *	  are relevant to the specified 'relids'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *	- Once used, the caller should dispose of the iterator by calling
+ *	  dispose_eclass_member_iterator().
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	relids - The Relids used to filter for relevant child members.
+ */
+void
+setup_eclass_member_iterator_with_children(EquivalenceMemberIterator *it,
+										   PlannerInfo *root,
+										   EquivalenceClass *ec,
+										   Relids relids)
+{
+	Bitmapset  *matching_indexes;
+	int			i;
+
+	/*
+	 * Initialize the iterator.
+	 */
+	it->index = -1;
+	it->list_is_copy = false;
+	it->ec_members = ec->ec_members;
+
+	/*
+	 * If there are no child relations, there is nothing to do. This
+	 * effectively avoids regression for non-partitioned cases.
+	 */
+	if (root->append_rel_array == NULL)
+		return;
+
+	/*
+	 * 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 '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 '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.
+	 */
+
+	/*
+	 * 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->append_rel_array[i] == NULL)
+			continue;
+
+		/*
+		 * Add child members that mention this relation to the iterator.
+		 */
+		child_rel = root->simple_rel_array[i];
+		if (child_rel != NULL)
+			add_eclass_child_members_to_iterator(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_eclass_child_members_to_iterator(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);
+}
+
+/*
+ * add_eclass_child_members_to_iterator
+ *	  Add a child EquivalenceMember referencing the given child_rel to
+ *	  the iterator from RelOptInfo.
+ *
+ * This function is expected to be called only from
+ * setup_eclass_member_iterator_with_children().
+ */
+static void
+add_eclass_child_members_to_iterator(EquivalenceMemberIterator *it,
+									 PlannerInfo *root,
+									 EquivalenceClass *ec,
+									 RelOptInfo *child_rel)
+{
+	ListCell   *lc;
+
+	foreach(lc, child_rel->eclass_child_members)
+	{
+		EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc);
+
+		/* Skip unwanted EquivalenceMembers */
+		if (child_em->em_ec != ec)
+			continue;
+
+		/*
+		 * If this is the first time the iterator's list has been modified, we
+		 * need to make a copy of it.
+		 */
+		if (!it->list_is_copy)
+		{
+			it->ec_members = list_copy(it->ec_members);
+			it->list_is_copy = true;
+		}
+
+		/* Add this child EquivalenceMember to the list */
+		it->ec_members = lappend(it->ec_members, child_em);
+	}
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator_with_children(). NULL is
+ *	  returned if there are no members left.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	if (++it->index < list_length(it->ec_members))
+		return list_nth_node(EquivalenceMember, it->ec_members, it->index);
+	return NULL;
+}
+
+/*
+ * dispose_eclass_member_iterator
+ *	  Free any memory allocated by the iterator.
+ */
+void
+dispose_eclass_member_iterator(EquivalenceMemberIterator *it)
+{
+	/*
+	 * XXX Should we use list_free()? I decided to use this style to take
+	 * advantage of speculative execution.
+	 */
+	if (unlikely(it->list_is_copy))
+		pfree(it->ec_members);
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3041,6 +3310,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3062,15 +3332,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator_with_children(&it, root, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3085,8 +3354,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3304,8 +3573,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index a43ca16d683..c206f260633 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3739,7 +3739,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3756,7 +3756,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3776,9 +3777,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator_with_children(&it, root, pathkey->pk_eclass,
+												   index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3813,6 +3815,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ff507331a06..7791e282e9e 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -119,16 +119,23 @@ setup_simple_rel_arrays(PlannerInfo *root)
 		root->simple_rte_array[rti++] = rte;
 	}
 
-	/* append_rel_array is not needed if there are no AppendRelInfos */
+	/*
+	 * append_rel_array and eclass_indexes_array are not needed if there are
+	 * no AppendRelInfos.
+	 */
 	if (root->append_rel_list == NIL)
 	{
 		root->append_rel_array = NULL;
+		root->eclass_indexes_array = NULL;
 		return;
 	}
 
 	root->append_rel_array = (AppendRelInfo **)
 		palloc0(size * sizeof(AppendRelInfo *));
 
+	root->eclass_indexes_array = (EquivalenceClassIndexes *)
+		palloc0(size * sizeof(EquivalenceClassIndexes));
+
 	/*
 	 * append_rel_array is filled with any already-existing AppendRelInfos,
 	 * which currently could only come from UNION ALL flattening.  We might
@@ -175,11 +182,21 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
+	{
+		Assert(root->eclass_indexes_array);
 		root->append_rel_array =
 			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
+		root->eclass_indexes_array =
+			repalloc0_array(root->eclass_indexes_array, EquivalenceClassIndexes, root->simple_rel_array_size, new_size);
+	}
 	else
+	{
+		Assert(!root->eclass_indexes_array);
 		root->append_rel_array =
 			palloc0_array(AppendRelInfo *, new_size);
+		root->eclass_indexes_array =
+			palloc0_array(EquivalenceClassIndexes, new_size);
+	}
 
 	root->simple_rel_array_size = new_size;
 }
@@ -234,6 +251,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->subplan_params = NIL;
 	rel->rel_parallel_workers = -1; /* set up in get_relation_info */
 	rel->amflags = 0;
+	rel->join_rel_list_index = -1;
+	rel->eclass_child_members = NIL;
 	rel->serverid = InvalidOid;
 	if (rte->rtekind == RTE_RELATION)
 	{
@@ -626,6 +645,12 @@ set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 static void
 add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 {
+	/*
+	 * Store the index of this joinrel to use in
+	 * add_child_join_rel_equivalences().
+	 */
+	joinrel->join_rel_list_index = list_length(root->join_rel_list);
+
 	/* GEQO requires us to append the new joinrel to the end of the list! */
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
@@ -741,6 +766,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->subplan_params = NIL;
 	joinrel->rel_parallel_workers = -1;
 	joinrel->amflags = 0;
+	joinrel->join_rel_list_index = -1;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -928,6 +955,8 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->subroot = NULL;
 	joinrel->subplan_params = NIL;
 	joinrel->amflags = 0;
+	joinrel->join_rel_list_index = -1;
+	joinrel->eclass_child_members = NIL;
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
@@ -1490,6 +1519,8 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->cheapest_total_path = NULL;
 	upperrel->cheapest_unique_path = NULL;
 	upperrel->cheapest_parameterized_paths = NIL;
+	upperrel->join_rel_list_index = -1;
+	upperrel->eclass_child_members = NIL;
 
 	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c24a1fc8514..6b3a028cc58 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -216,6 +216,8 @@ typedef struct PlannerInfo PlannerInfo;
 #define HAVE_PLANNERINFO_TYPEDEF 1
 #endif
 
+struct EquivalenceClassIndexes;
+
 struct PlannerInfo
 {
 	pg_node_attr(no_copy_equal, no_read, no_query_jumble)
@@ -272,6 +274,13 @@ struct PlannerInfo
 	 */
 	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);
 
+	/*
+	 * eclass_indexes_array is the same length as simple_rel_array and holds
+	 * the indexes of the corresponding rels for faster EquivalenceMember
+	 * lookups. See the EquivalenceClass comment for more details.
+	 */
+	struct EquivalenceClassIndexes *eclass_indexes_array pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_baserels is a Relids set of all base relids (but not joins or
 	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
@@ -984,6 +993,17 @@ typedef struct RelOptInfo
 	/* Bitmask of optional features supported by the table AM */
 	uint32		amflags;
 
+	/*
+	 * information about a join rel
+	 */
+	/* index in root->join_rel_list of this rel */
+	int			join_rel_list_index;
+
+	/*
+	 * EquivalenceMembers referencing this rel
+	 */
+	List	   *eclass_child_members;
+
 	/*
 	 * Information about foreign tables and foreign joins
 	 */
@@ -1403,6 +1423,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * EquivalenceClass->ec_members can only have parent members, and child members
+ * are stored in RelOptInfos, from which those child members are translated. To
+ * lookup child EquivalenceMembers, we use EquivalenceMemberIterator. See
+ * its comment for usage. The approach to lookup child members quickly is
+ * described as setup_eclass_member_iterator_with_children() comment.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1477,8 +1503,61 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* 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;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator is designed to iterate over all parent
+ * EquivalenceMembers and child members associated with the given 'ec' that
+ * are relevant to the specified 'relids'. In particular, it iterates over:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * PlannerInfo					   *root = given;
+ * EquivalenceClass				   *ec = given;
+ * Relids							rel = given;
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator_with_children(&it, root, ec, rel);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_member_iterator(&it);
+ * -----
+ */
+typedef struct
+{
+	int			index;			/* current index within 'ec_members'. */
+	bool		list_is_copy;	/* is 'ec_members' a newly allocated one? */
+	List	   *ec_members;		/* parent and child members */
+} EquivalenceMemberIterator;
+
+/*
+ * EquivalenceClassIndexes
+ *
+ * As mentioned in the EquivalenceClass comment, we introduce a
+ * bitmapset-based indexing mechanism for faster lookups of child
+ * EquivalenceMembers. This struct exists for each relation and holds the
+ * corresponding indexes.
+ */
+typedef struct EquivalenceClassIndexes
+{
+	Bitmapset  *joinrel_indexes;	/* Indexes in PlannerInfo's join_rel_list
+									 * list for RelOptInfos that mention this
+									 * relation */
+} EquivalenceClassIndexes;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 84a000a3ef1..6b80c8665b9 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator_with_children(EquivalenceMemberIterator *it,
+													   PlannerInfo *root,
+													   EquivalenceClass *ec,
+													   Relids relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
+extern void dispose_eclass_member_iterator(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9442a4841aa..4b613ad34aa 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -701,7 +701,9 @@ EphemeralNamedRelationData
 EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
+EquivalenceClassIndexes
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.34.1

0006-Use-list_free-instead-of-pfree-20250327.patchtext/x-patch; charset=US-ASCII; name=0006-Use-list_free-instead-of-pfree-20250327.patchDownload
From f2efdaee99942b10b26c131836c16fb57375d5e1 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Date: Wed, 26 Mar 2025 18:26:14 +0530
Subject: [PATCH 6/6] Use list_free() instead of pfree().

---
 src/backend/optimizer/path/equivclass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 1ffd2ce0a04..823eaf6d43e 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -3467,7 +3467,7 @@ dispose_eclass_member_iterator(EquivalenceMemberIterator *it)
 	 * advantage of speculative execution.
 	 */
 	if (unlikely(it->list_is_copy))
-		pfree(it->ec_members);
+		list_free(it->ec_members);
 }
 
 
-- 
2.34.1

#112Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#108)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

Thank you for your prompt reply, and apologies for my late response.

On Mon, Mar 24, 2025 at 2:38 PM David Rowley <dgrowleyml@gmail.com> wrote:

I understand Ashutosh would like to handle the RestrictInfo speedup
another way, so there's additional review work to do there to
determine the merits of each method and figure out the best method.
I'm worried that means we don't get to fix this part for v18 and if
that happens and 0002 goes in alone, then we'd be left with a struct
with a single field. Maybe you should adjust the patch series and
only introduce the new struct in 0004 where it's required.

Thank you for your advice. I agree that introducing a struct with only
one field is not a good design, so adjusting the patch series to avoid
this issue is necessary.

I think something like that is probably ok. You have a problem with
your implementation as you're trying to add the AppendRelInfo once for
each child_tlist element rather than once per union child. Can you fix
this and incorporate into the 0002 patch please?

Thank you for pointing this out. This was indeed my mistake, and I
will correct it in the next version of the patch series.

1. I don't think the header comment for eclass_member_iterator_next()
needs to mention setup_eclass_member_iterator_with_children(). The
renaming you did in v35 is meant to make it so the
eclass_member_iterator_next and dispose_eclass_member_iterator()
functions don't care about what set up the iterator. We might end up
with new ones in the future and this seems like a comment that might
not get updated when that happens.

I agree. I will fix this comment in the next version.

2. You should use list_free() in the following:

/*
* XXX Should we use list_free()? I decided to use this style to take
* advantage of speculative execution.
*/
if (unlikely(it->list_is_copy))
pfree(it->ec_members);

The reason is that you're wrongly assuming that calling pfree on the
List pointer is enough to get rid of all memory used by the list. The
List may have a separately allocated elements[] array (this happens
when there's > 5 elements) which you're leaking with the current code.

I assume the speculative execution comment is there because you want
to omit the "list == NULL" check in list_free_private. Is this
measurable, performance-wise?

Thank you for clarifying this. It was my oversight. Regarding
speculative execution, I have never measured its impact. I added
"unlikely" based on an assumption that non-partitioned cases would be
common. However, whether this assumption is correct needs to be
discussed.

3. Maybe I'm missing something, but I'm confused about the need for
the eclass_indexes_array field in PlannerInfo. This array is indexed
by the relid, so why can't we get rid of the array and add a field to
RelOptInfo to store the EquivalenceClassIndexes?

The reason is that some RelOptInfos can be NULL. Further details were
explained in [1]/messages/by-id/CAJ2pMkYR_X-=pq+39-W5kc0OG7q9u5YUwDBCHnkPur17DXnxuQ@mail.gmail.com. To be honest, I don't fully understand the
architectural details. Initially, I addressed this by moving the
indexes into RangeTblEntry, but this was not an ideal solution.
Therefore, I moved them into PlannerInfo by introducing a new struct,
"EquivalenceClassIndexes".

4. Could you also please run another set of benchmarks against current
master with the the v36 patches: master, master + v36-0001 + 0002,
master + v36-0001 + 0002 + 0003 (0003 will be the v34-0004 patch), and
then also with v36-0004 (which is the same as v35-0005). The main
thing I'd like to understand here is if there's not enough time to get
the entire patch set committed, is there much benefit to just having
the EquivalenceMember index stuff in by itself without the
RestrictInfo changes.

Thank you for your suggestion. Running the benchmarks themselves
should be possible, but given Tom's feedback and the limited time
remaining before feature freeze, it is unlikely that even a partial
integration into v18 is realistic, and a detailed evaluation will
likely need to be deferred until v19. I apologize again for my slow
progress. Given this situation, I plan to carefully reconsider the
overall design and propose a refined patch set for v19. What do you
think about this approach?

Thank you again for your extensive contributions to this patch so far.
I'm sorry that I couldn't get it ready in time for v18.

[1]: /messages/by-id/CAJ2pMkYR_X-=pq+39-W5kc0OG7q9u5YUwDBCHnkPur17DXnxuQ@mail.gmail.com

--
Best regards,
Yuya Watari

#113Yuya Watari
watari.yuya@gmail.com
In reply to: Tom Lane (#109)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Tom,

Thank you for your detailed review, and apologies for my late response.

On Tue, Mar 25, 2025 at 2:49 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

One thing I don't love is putting the children into RelOptInfos.
That seems like an unrelated data structure. Have you thought
about instead having, in each EC that needs it, an array indexed
by RTI of per-relation child-member lists? I think this might
net out as less storage because there typically aren't that many
ECs in a query. But the main thing is to not have so many
interconnections between ECs and RelOptInfos.

Thank you for your suggestion. Storing EquivalenceMembers in
RelOptInfos indeed complicates the data structures involved. In the
next version, I will explore alternative approaches, including the one
you have suggested.

Another thing I really don't like is the back-link from EMs to ECs:

+ EquivalenceClass *em_ec; /* EquivalenceClass which has this member */

That makes the data structure circular, which will cause pprint to
recurse infinitely. (The fact that you hadn't noticed that makes
me wonder how you debugged any of these data structure changes.)
We could prevent the recursion with suitable annotation on this field,
but I'd really rather not have the field in the first place. Circular
pointers are dangerous and best avoided. Also, it's bloating a node
type that you are concerned about supporting a lot of. Another point
is that I don't see any code to take care of updating these links
during an EC merge.

I apologize for missing this critical point. It is clear that avoiding
circular dependencies would be preferable, so I will reconsider this
aspect of the design.

* setup_eclass_member_iterator_with_children is a carpal-tunnel-inducing
name. Could we drop the "_with_children" part? It doesn't seem to
add much, since there's no variant for "without children".

Thank you for this suggestion. I will remove "_with_children" in the
next version.

* The root parameter should be first; IMO there should be no
exceptions to that within the planner. Perhaps putting the target
iterator parameter last would make it read more nicely. Or you could
rely on struct assignment:

it = setup_eclass_member_iterator(root, ec, relids);

I agree with your point. I will adjust the parameter order in the next
version to match your suggestion.

* Why did you define the iterator as possibly returning irrelevant
members? Doesn't that mean that every caller has to double-check?
Wouldn't it make for less code and fewer bugs for the iterator to
have that responsibility? If there is a good reason to do it like
that, the comments should explain why.

This design was chosen for performance reasons. If the iterator always
filtered out irrelevant members, it would need to repeatedly check
each element against "bms_is_subset". However, some callers require
stricter conditions, such as "bms_equals", resulting in redundant
checks. Therefore, the iterator intentionally returns some false
positives, leaving it to callers to perform additional checks for the
exact conditions they require. As you pointed out, I failed to clearly
document this, and I will fix this oversight in the next version.

I don't really like the concept of 0004 at all. Putting *all*
the EC-related RelOptInfos into a root-stored list seems to be
doubling down very hard on the assumption that no performance-critical
operations will ever need to search that whole list. Is there a good
reason to do it like that, rather than say using the bitmap-index
concept separately within each EC? That might also alleviate the
problem you're having with the bitmapsets getting too big.

Thank you for this suggestion. The patch series indeed has issues with
memory consumption. Your suggestion to manage bitmap indexes
separately within each EC seems worth exploring, and I will
investigate this approach further.

Given that we've only got a week left, I see little hope of getting
any of this into v18.

I agree that addressing these issues within the remaining time is
challenging. The design clearly needs reconsideration. Therefore, I
will postpone these changes and submit a fully revised version for
v19. Would this approach be acceptable to you?

--
Best regards,
Yuya Watari

#114Yuya Watari
watari.yuya@gmail.com
In reply to: Ashutosh Bapat (#110)
Re: [PoC] Reducing planning time when tables have many partitions

Hello Ashutosh,

Thank you for your detailed review, and apologies for my delayed response.

On Thu, Mar 27, 2025 at 1:42 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

Here are memory consumption numbers using list_free() instead of pfree(), using the same method as [1], using a binary without asserts and debug info. PFA the patchset where all the patches are the same as v35 but with an extra patch fixing memory leak. The memory leak is visible with a higher number of joins. At a lower number of joins, I expect that the memory saved is less than a KB or the leaked memory fits within 1 chunk of memory context and hence not visible.

Thank you for conducting your benchmarks. Your results clearly show
increased memory consumption with my patches. As Tom also suggested,
we may reduce memory usage by adopting a different design. I will
reconsider alternative approaches and compare the memory usage to the
current version.

Thank you once again for conducting the benchmarks.

--
Best regards,
Yuya Watari

#115David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#109)
2 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, 25 Mar 2025 at 06:49, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I finally made some time to look at this patchset, and I'm pretty
disappointed, because after 35 versions I'd expect to see something
that looks close to committable. This doesn't really. I like the
basic idea of taking child EC members out of ECs' main ec_members
lists, but there are too many weird details and
underexplained/overcomplicated/unmaintainable data structures.

One thing I don't love is putting the children into RelOptInfos.
That seems like an unrelated data structure. Have you thought
about instead having, in each EC that needs it, an array indexed
by RTI of per-relation child-member lists? I think this might
net out as less storage because there typically aren't that many
ECs in a query. But the main thing is to not have so many
interconnections between ECs and RelOptInfos.

I think that's quite a good idea. One drawback of that method is that
we'd need to duplicate the EquivalenceMembers into each relid making
up the joinrels in add_child_join_rel_equivalences(). That could mean
finding the same EM multiple times when iterating over the set. I
don't think that causes issues other than wasted effort.

Another thing I really don't like is the back-link from EMs to ECs:

+ EquivalenceClass *em_ec; /* EquivalenceClass which has this member */

That makes the data structure circular, which will cause pprint to
recurse infinitely. (The fact that you hadn't noticed that makes
me wonder how you debugged any of these data structure changes.)
We could prevent the recursion with suitable annotation on this field,
but I'd really rather not have the field in the first place. Circular
pointers are dangerous and best avoided. Also, it's bloating a node
type that you are concerned about supporting a lot of. Another point
is that I don't see any code to take care of updating these links
during an EC merge.

Some thoughts about the iterator stuff:

* setup_eclass_member_iterator_with_children is a carpal-tunnel-inducing
name. Could we drop the "_with_children" part? It doesn't seem to
add much, since there's no variant for "without children".

* The root parameter should be first; IMO there should be no
exceptions to that within the planner. Perhaps putting the target
iterator parameter last would make it read more nicely. Or you could
rely on struct assignment:

it = setup_eclass_member_iterator(root, ec, relids);

* Why did you define the iterator as possibly returning irrelevant
members? Doesn't that mean that every caller has to double-check?
Wouldn't it make for less code and fewer bugs for the iterator to
have that responsibility? If there is a good reason to do it like
that, the comments should explain why.

I've attached 2 patches, which I think addresses most of this, aside
from the last point.

These do need more work. I've just attached what I have so far before
I head off for the day. I am planning on running some performance
tests tomorrow and doing a round on the comments.

I don't really like the concept of 0004 at all. Putting *all*
the EC-related RelOptInfos into a root-stored list seems to be
doubling down very hard on the assumption that no performance-critical
operations will ever need to search that whole list. Is there a good
reason to do it like that, rather than say using the bitmap-index
concept separately within each EC? That might also alleviate the
problem you're having with the bitmapsets getting too big.

I've dropped this patch out of the set for now. There's other work
going on that might solve the issue that patch was aiming to solve.

Given that we've only got a week left, I see little hope of getting
any of this into v18.

I am keen on not giving up quite yet. I'd very much value any further
input you have. It doesn't seem excessively complex to have quite a
large impact on the performance of the planner here.

David

Attachments:

v36-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchapplication/octet-stream; name=v36-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchDownload
From b945ba2c102df304112947acaf8b0382a69b897f Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v36 1/2] Add the PlannerInfo context to the parameter of
 find_ec_member_matching_expr()

---
 src/backend/optimizer/path/equivclass.c |  4 +-
 src/backend/optimizer/plan/createplan.c | 64 +++++++++++++++----------
 src/include/optimizer/paths.h           |  3 +-
 3 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..7fe7cdff468 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -760,7 +760,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
@@ -939,7 +939,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 75e2b0b9036..163923072df 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -294,7 +298,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1281,7 +1285,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1325,7 +1329,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1466,7 +1470,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1497,7 +1501,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1979,7 +1983,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2193,7 +2197,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2217,7 +2221,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2286,7 +2290,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4544,7 +4548,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4553,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4578,7 +4584,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6195,6 +6201,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the plan node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' identifies the child relation being sorted, if any
@@ -6228,7 +6235,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6295,7 +6302,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6323,7 +6330,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6339,7 +6346,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6405,12 +6412,14 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6419,7 +6428,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6439,14 +6448,16 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  * make_incrementalsort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6455,7 +6466,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6812,7 +6823,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6875,7 +6887,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..84a000a3ef1 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
-- 
2.43.0

v36-0002-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v36-0002-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 91280a30af4f4b8fe98d21c669306176048c8cd0 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v36 2/2] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  15 +-
 src/backend/nodes/outfuncs.c            |   1 +
 src/backend/optimizer/path/equivclass.c | 353 +++++++++++++++++-------
 src/backend/optimizer/path/indxpath.c   |  15 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/include/nodes/pathnodes.h           |  51 ++++
 src/include/optimizer/paths.h           |   6 +
 src/tools/pgindent/typedefs.list        |   1 +
 8 files changed, 340 insertions(+), 111 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index ac14c06c715..90c7a212379 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7865,6 +7864,7 @@ find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 			is_foreign_expr(root, rel, em->em_expr))
 			return em;
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -7918,9 +7918,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb9bdd67192..306df44f293 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,6 +466,7 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	/* XXX ec_childmembers? */
 	WRITE_NODE_FIELD(ec_sources);
 	WRITE_NODE_FIELD(ec_derives);
 	WRITE_BITMAPSET_FIELD(ec_relids);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fe7cdff468..9d36194fccf 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -33,11 +33,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Relids child_relids);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -268,7 +280,8 @@ process_equivalence(PlannerInfo *root,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -373,7 +386,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -390,7 +403,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -422,9 +435,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -510,11 +523,16 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *
+ * Build a new EquivalenceMember without adding it to an EC. If 'parent'
+ * parameter is NULL, the result will be a parent member, otherwise a child
+ * member. Note that child EquivalenceMembers should not be added to its
+ * parent EquivalenceClass.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -541,11 +559,60 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	ec->ec_members = lappend(ec->ec_members, em);
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Relids child_relids)
+{
+	EquivalenceMember *em;
+	int			relid;
+
+	Assert(parent_em != NULL);
+
+	if (ec->ec_childmembers == NULL)
+		ec->ec_childmembers = (List **) palloc0(root->simple_rel_array_size * sizeof(List *));
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	relid = -1;
+	while ((relid = bms_next_member(child_relids, relid)) >= 0)
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+		ec->ec_childmembers[relid] = lappend(ec->ec_childmembers[relid], em);
+
+		/* Record this EC index for the child rel */
+		if (ec_index >= 0)
+		{
+			RelOptInfo *child_rel = root->simple_rel_array[relid];
+
+			child_rel->eclass_indexes =
+				bms_add_member(child_rel->eclass_indexes, ec_index);
+		}
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -616,7 +683,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -631,10 +699,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(root, &it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -653,6 +720,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 				equal(expr, cur_em->em_expr))
 				return cur_ec;	/* Match! */
 		}
+		dispose_eclass_member_iterator(&it);
 	}
 
 	/* No match; does caller want a NULL result? */
@@ -690,7 +758,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -764,15 +832,16 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -799,6 +868,7 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 		if (equal(emexpr, expr))
 			return em;
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -841,7 +911,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -855,9 +926,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -901,6 +972,7 @@ find_computable_ec_member(PlannerInfo *root,
 
 		return em;				/* found usable expression */
 	}
+	dispose_eclass_member_iterator(&it);
 
 	return NULL;
 }
@@ -1162,7 +1234,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1231,7 +1304,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1564,7 +1638,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1575,10 +1650,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1594,6 +1668,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		else
 			new_members = lappend(new_members, cur_em);
 	}
+	dispose_eclass_member_iterator(&it);
 
 	/*
 	 * First, select the joinclause if needed.  We can equate any one outer
@@ -1611,6 +1686,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1685,6 +1761,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1692,7 +1769,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2181,7 +2258,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2308,7 +2386,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2526,8 +2605,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2598,8 +2677,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2696,6 +2775,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2708,7 +2788,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2721,29 +2800,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2788,12 +2853,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relids);
 			}
 		}
 	}
@@ -2840,7 +2908,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2853,25 +2921,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2916,9 +2974,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_joinrel->relids);
 			}
 		}
 	}
@@ -2965,14 +3029,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relids);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -2987,6 +3055,103 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members associated with the given 'ec' that
+ *	  are relevant to the specified 'relids'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *	- Once used, the caller should dispose of the iterator by calling
+ *	  dispose_eclass_member_iterator().
+ *	- The given 'relids' must remain allocated and not be changed for the
+ *	  lifetime of the iterator.
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	it - A pointer to the iterator to set up.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	relids - The Relids used to filter for relevant child members.
+ */
+void
+setup_eclass_member_iterator(PlannerInfo *root, EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids relids)
+{
+	it->root = root;
+	it->ec = ec;
+	it->relids = ec->ec_childmembers ? relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator(). NULL is
+ *	  returned if there are no members left, in which case callers must not
+ *	  call eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	EquivalenceMember *em = NULL;
+
+	while (it->current_list != NULL)
+	{
+nextcell:
+		while (it->current_cell != NULL)
+		{
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			goto end;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->relids, it->current_relid)) > 0)
+		{
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/*
+			 * If there are members in this list, use it, this will exclude
+			 * RELOPT_BASERELs as ec_childmembers[] are not populated for
+			 * those.
+			 */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+
+		/*
+		 * XXX because we don't NULLify current_list here, the iterator is not
+		 * safe to call again after it returns NULL.  Is that worth doing?
+		 */
+		goto end;
+	}
+
+end:
+	return em;
+}
+
+/*
+ * dispose_eclass_member_iterator
+ *	  Free any memory allocated by the iterator.
+ */
+void
+dispose_eclass_member_iterator(EquivalenceMemberIterator *it)
+{
+}
+
 
 /*
  * generate_implied_equalities_for_column
@@ -3041,6 +3206,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
+		EquivalenceMemberIterator it;
 
 		/* Sanity check eclass_indexes only contain ECs for rel */
 		Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
@@ -3062,15 +3228,14 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(root, &it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
+		dispose_eclass_member_iterator(&it);
 
 		if (!cur_em)
 			continue;
@@ -3085,8 +3250,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3304,8 +3469,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6386ce82253..275695b07c0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3796,7 +3796,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3813,7 +3813,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3833,9 +3834,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(root, &it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
@@ -3870,6 +3872,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 			if (found)			/* don't want to look at remaining members */
 				break;
 		}
+		dispose_eclass_member_iterator(&it);
 
 		/*
 		 * Return the matches found so far when this pathkey couldn't be
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index ac3af528bc6..34b2510741b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1402,6 +1402,12 @@ typedef struct JoinDomain
  * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
  * So we record the SortGroupRef of the originating sort clause.
  *
+ * 'ec_members' is a List of all EquivalenceMembers belonging to
+ * RELOPT_BASERELs.  EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and
+ * RELOPT_OTHER_JOINREL relations are stored in the 'ec_childmembers' array in
+ * the index corresponding to the relid.  'ec_childmembers' may be NULL if the
+ * class has no child EquivalenceMembers.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1420,6 +1426,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_childmembers;	/* array of Lists of child
+									 * EquivalenceMembers */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
@@ -1478,6 +1486,49 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator is designed to iterate over all parent
+ * EquivalenceMembers and child members associated with the given 'ec' that
+ * are relevant to the specified 'relids'. In particular, it iterates over:
+ *	- All parent members stored directly in ec->ec_members.
+ *	- The child members whose em_relids is a subset of the given 'relids'.
+ *
+ * Note:
+ *	- The iterator may return false positives, i.e., child members whose
+ *	  em_relids is not a subset. So the caller must check that they satisfy
+ *	  the desired condition.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * PlannerInfo					   *root = given;
+ * EquivalenceMemberIterator		it;
+ * EquivalenceClass				   *ec = given;
+ * Relids							relids = given;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(root, &it, ec, relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *     use em ...;
+ * }
+ * dispose_eclass_member_iterator(&it);
+ * -----
+ */
+typedef struct
+{
+	PlannerInfo *root;			/* The PlannerInfo where 'ec' belongs */
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		relids;			/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 84a000a3ef1..82e51fef742 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,12 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(PlannerInfo *root,
+										 EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
+extern void dispose_eclass_member_iterator(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8f28d8ff28e..ba964a0d17d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -709,6 +709,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.43.0

#116David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#115)
4 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Fri, 4 Apr 2025 at 00:34, David Rowley <dgrowleyml@gmail.com> wrote:

I've attached 2 patches, which I think addresses most of this, aside
from the last point.

These do need more work. I've just attached what I have so far before
I head off for the day. I am planning on running some performance
tests tomorrow and doing a round on the comments.

I've done some further work on this, mostly relating to the code
comments. I also removed the now-empty
dispose_eclass_member_iterator() function.

A couple of things which I'm still uncertain of:

1. How to handle the ec_childmembers array in _outEquivalenceClass().
There's no field to know the size of the array. Maybe I should add one
and then print out the non-empty lists.
2. When processing RELOPT_OTHER_JOINREL in add_child_eq_member(), I'm
adding the member to each List for all individual relid mentioned in
child_relids. This will result in the member going on multiple Lists
and cause the iterator to possibly return the member multiple times.
That might matter in a few places, e.g.
generate_join_implied_equalities_normal() keeps some scoring based on
the number of members.

For #2, Yuya's Bitmapset approach didn't suffer from this issue as the
Bitmapsets would be unioned to get the non-duplicative members. I
wondered about doing list_append_unique() instead of lappend() in
generate_join_implied_equalities_normal(). Unsure. The only other
thing I can think of is to do something else with members for
RELOPT_OTHER_JOINREL and store them elsewhere.

I also did some benchmarking using the attached script. I've attached
the results of running that on my AMD Zen2 machine. See the end of the
script for the CREATE TABLE statement for loading that into postgres.

The results look pretty good. v37 came out slightly faster than v36,
either noise or because of dispose_eclass_member_iterator() removal.

-- overall plan time.
select testname,sum(plan_time)::int as plan_ms from bench_results
group by 1 order by 2;
testname | plan_ms
------------------+---------
v37_patch | 6806
v36_patch | 6891
v35_patch | 6917
master_1aff1dc8d | 21113

-- plan time by number of joins for 1024 parts
select testname,joins,sum(plan_time)::int as "plan_ms" from
bench_results where parts=1024 group by 1,2 order by 2,1;
testname | joins | plan_ms
------------------+-------+---------
master_1aff1dc8d | 0 | 239
v35_patch | 0 | 120
v36_patch | 0 | 120
v37_patch | 0 | 119
master_1aff1dc8d | 1 | 485
v35_patch | 1 | 181
v36_patch | 1 | 184
v37_patch | 1 | 180
master_1aff1dc8d | 2 | 832
v35_patch | 2 | 252
v36_patch | 2 | 253
v37_patch | 2 | 249
master_1aff1dc8d | 3 | 1284
v35_patch | 3 | 342
v36_patch | 3 | 338
v37_patch | 3 | 337
master_1aff1dc8d | 4 | 1909
v35_patch | 4 | 427
v36_patch | 4 | 435
v37_patch | 4 | 435
master_1aff1dc8d | 5 | 2830
v35_patch | 5 | 530
v36_patch | 5 | 540
v37_patch | 5 | 535
master_1aff1dc8d | 6 | 4759
v35_patch | 6 | 685
v36_patch | 6 | 691
v37_patch | 6 | 681

-- The memory used is about the same as before:
select testname,joins,sum(mem_alloc)::int as mem_alloc from
bench_results group by 1,2 order by 2,1;
testname | joins | mem_alloc
------------------+-------+-----------
master_1aff1dc8d | 0 | 231110
v35_patch | 0 | 233662
v36_patch | 0 | 233662
v37_patch | 0 | 233662
master_1aff1dc8d | 1 | 432685
v35_patch | 1 | 435369
v36_patch | 1 | 435369
v37_patch | 1 | 435369
master_1aff1dc8d | 2 | 476916
v35_patch | 2 | 476300
v36_patch | 2 | 476300
v37_patch | 2 | 476300
master_1aff1dc8d | 3 | 801834
v35_patch | 3 | 801372
v36_patch | 3 | 801372
v37_patch | 3 | 801372
master_1aff1dc8d | 4 | 917312
v35_patch | 4 | 917015
v36_patch | 4 | 917015
v37_patch | 4 | 917015
master_1aff1dc8d | 5 | 1460833
v35_patch | 5 | 1460701
v36_patch | 5 | 1460701
v37_patch | 5 | 1460701
master_1aff1dc8d | 6 | 2550570
v35_patch | 6 | 2639395
v36_patch | 6 | 2639395
v37_patch | 6 | 2639395

David

Attachments:

partbench.sh.txttext/plain; charset=US-ASCII; name=partbench.sh.txtDownload
bench_results_2025-04-04.csv.bz2application/x-compressed; name=bench_results_2025-04-04.csv.bz2Download
BZh91AY&SY���Ky@��0��B]`��@	��o�[�
�[���K�aa�6H�uT���&�Xc��J�\�0�yJR�h;������<:o��k���|{�*�(�\���U���06��P(P�����2�B
�L���A�>DB(��}��7Yku���zRU�w����Y�������tHI��vS\��:7:�da���{g��v�����y�/���5^���j_\���K-��-]��>]
��A]��PZ��M�
���4�v������@l��*�u�����rBGgu�T+���JTb0L �U4��R��!�
�J��MU�(�)�Tb0Q
$�J��S��}<)�i�p��'8��q��q�8�p��)�i�p��'����S30��rnM=$���u�]��q�s'q�
cn�WON)���N8��q8s�<<�+��a���>Yfz��0e,�8���4��8q��{,��������nf3�Q������w������O�q�8������K=��Y��3�L�,����q�8�����,��Y��Y[������i�p��'����_1e���2SJzzq�q��8�;����b��,��,���N��9��4���q�8������qf�,�fM<)��q�8����������!jH����@	�(T((B�J��)�"�*�fQ�k�5�H
	�"b&b	��dS(���Fl�X�6dL���e��l���%fm����6���AI@PR���D
"�h�TIS&��Y2c$�I3FfL$2����#d���t�svl�n���l��1&�#�,b�l��1�&L���Yj��fi�m��lP
���44�m�"6&Y�Fbf�i�f3-nmb�m��b�e�cfPA��1R������-Z����S�8��8��Nq�8��8��q���S�8��8��<�p�S����w�w��������������3��hxA�v��4��8q�
��;l�~���<��0��d��)fa�||=)�i�p������l`����g�����0��d�e����Jq�q�8���nK=���d����&OO�OO�8��8��q�����Yg�V�d���OJq�q�8����J�{Y�1�������4��8q�;���fQfdu�3t��7g2xz=4��q�q��8a��if�,�d�i��8��8��q���a\u����YV�,V
�L����7V�[���:l���LV]�QTE	�`DDFf$vmmrm-���35N����������s�Z&��&hR�B�S @)�C1��3
��m����vK����)�Y�m��ri��w��������������3��r���1��007����
�8��d��s��(&w3���hk�����;�m���G1�`�:�sY�&z�1��q��1���v��Y�L$N@Nbl����8��1x����3�����Q;���"�)612�ca��Z6�bj���Ei#@I2�����L~��g�2NX��������{����D�4MD�DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDT�N{����������TD$r�%GB��r�H�!f�su�l[����u�[nA�9��	�bE�L0*���!����I6��A|o5����v�f�mw���3����{��j���[f�}��c]�����c:kf5\-7
 �2(� �F}�ECK���xn����o�s�����}{����Xm�begn�;U4�]1SV��������]�F�����*&��$;�������9x�D+'
�F�Y<�i���/�No{�=��|�GX��FP`f��3���CEdz&��Y{��E��g<On���s����]�yZu��d�2Kx'm�y|����������%�NfR!Hm����sXM@�4dPKG!�
����@6���DU�D���^���w��}�V_)�I1T�>o��r�s��u�2��SN[�i6g�Z���h���=i��;���y�;Z��)�Jm;%Ww.������������i�q����|��~����<�������M4�M4�����������������������������������\�306@����x�����I����Op���6j�4��oKW�sZ:����I���vq��J�K�aWf������{8�cz��Z4�V�>�&bf&`���������������������������������i��i��OJ������y������*���m���j��c������Z���
�Y�N�,	�H�\�x��d��AM$����`]�n$�E��g�����xeM���6_n��d���W-n^��@���������;�E6v�8���v�������������t�H�j���9�M����0�NQo���9�Q����'���[= ��n<X�w����&d	&D���YL<{L�{|�r��������������i<����bq����0��R���z���{8����y�����_~�DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD_~�k����|~�\��N�����nes]��[�lmf��\������@��x�P
���R����
s)�����(��6 v��-';�%�>�\��*b�Qm�vVv�Zp�9��+Wnx��D=�u��v�V�m�V����$��7/9�mS���m�����](>-e���O�t��M�e�������Lwv�+o ��D�x��H�i$�p�z�Nh��rM��K�K���]���zp��{]�uv����N8��8����Np��8��;��q��8��8���,�q������)�Y�m��ri�����f��.��u���d)��c@������8���,�q��g�G~``n3��s8rq��'0��q�wYd�������l`����g�����agaf�,��]�||�q�q�e��8�M�g�U�����2d���4�|8��8���'q��e�,���+{2d������q��Y8��;����,�<2c����8��8����y�,�X�2�3#����N��9�������q��Y8��6S�,�e�l��<8��8���'q�v�����Ef5����Z���Y5����-�}v�� �b������������������������������������������131313�s����K)������$��TR���3$����d�(��T.�32u�A�|�'/�C���:�rN�����A��Q
xf`���$�M�*
*�z,����}��{�^���-����3����o^��5U4C�I�&��E!3HU%�ux��}Hv��L�����qb��_!��Z�:6K<\���"f:����QM�gY��7&�<����)�����:"dL�0�X�i5���������u���+2��r�kq���v�rT�5��j��Rm>��Of���\�l�i��D�|�{��z��/3�x���|���4�y���*�^�X��m��)�|F�����k>o}x�����b"""""""""""""""""""""""""""""""""""""/����������~����q�5�Ir���I����;��h��D7uz�Z��<m������o�]}��u����������X��7�m����`13"fdL�h��w@E@f��l�o�6�����7��m��DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDE�{������_>�R���N���T����M<{��[�%/��<��O\���a#p6�;O��8�n�%���;���>	49�f�:����I	�
M�+�$���muQ��8����Y�P�@�ee<�nw�{�e�j'����P��4\T�KIe���d��%�Oz������E�]�<[���KI���s��6J�Ku�/�&y#�E�:�9�\���������:�������i��i��L�;���?}��s,3FNw*�u��b�h�����;.���kL���J��-��U�6�s8�����)��}��[�|����s��[>)M�l�q�2�Kx��[8qJl�e8��8���[�:���Sg[)�q�L������~b�`f�f����v}�z���u��:�N�2��1�A��=������q�q�-t��yg������
�p:�gnN8�f��'��G���S�8�p8�k�n���q���gs1�:�6����`N�;�m�C��|���S�8���k��7��3�q�s$�1��:{��8Cg[)�q�L������^�,��������{=�z8�6u��q�t�]-�����_1e���<�'[=R�:�N8��8�k�[�I���O��2wM���xd�g��Sg[)�q�L����l��Y��2��t����Sg[)�q�L��q�;M�;��,�C�I4���3@6fj"
�
r%<At�4�f�;���W1������iqj�3�2�]^�\�7�d��������\x���������������)�[�����4Z�eb)4!Z�M���)��&�A���Hn����v�h�s;�O&l���M���uKV�v�FRm��U\�{��bh��z�i�S����7����,�{g������D�4MD�4DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD�L��y�}���{�[x�l��(���J��v�������������\�l�)���$�i��[�S�V��{{�y�v��t�[����G��jb�6:�KlZJm�NYy]|�67h�/[��'9�%��f�gy&d@D������\Hn]���r�E����'�iv�Q[���/����nOu����W��sg��I��t��x�C��U�]�[R�"O�~�����������������������������������������������?��=�y���3�2����v�������w���[�Z���`��_���@���Lm-\e<����o6�Z�H��+^O�gp'^�(���<Ai�g�'��2"t?O�8��8��=�fOL��%�����2[-��zza�q�q�����-���l���������q�q�4�>y/���f�f����v~��u�]��q�s'q�
g{����}���}0��8��8���d����cM<��y=��1�����u����q�2	�{�M>�N>L8��8��=�a�=���1��u�=g���(&w3���hk�	��3�f��%������>�q�q���f=�cOA��O���������9�g����4��x��a�q�q�����-��4�8�a����>����f8��l�~>�q�q���f=�cOA��O���^������2c	�����q�q��0��/)�*��������d����&zza�q�p�L0�K)�m���i���q�q�8�8�{W��[Ql�e&6������kx,�����#�����yY�4�����l�7/=�x}�313�!�����Z��Owo&��M�M���o3��I��u	$��M
J(�C�C6<cYy���=��s����S�;YHvp���L�3s1�uw.��VWJ��F�Cho�o�[��6n��l�������;sfJN�^me��{���������Q������W��T�55�j��m��/5�J�4��kU����53�TdU�m��L�#�;��9��LU!�9Za��=E��6
>��������3<+,����D�"j�S0&`j{����6�"n�m�L=-"�}���7�����h�;����u�����������v��v9�--���}131bf&bf&bf&a���������E�]��]��f���.���:��������wt��3��s`$u�X�z���������|�~�DDDDDDDDDDDDDDDlDDDDDb""""""""""""""""������{}��;Y�Rh9�E����;|�k�m��'w.�=�O:J�����i�mk6������Sk��|���,
8��8q�q��i�q�q�8��8��4�N8��8��q�~t�{�)����f��7&��y��������������4�l�8�a����8��q�i�Y��e{��0��a��,�oY�&���G>�q�p��8��=�O|�&K�;����q�5����i��e�<>|=8��8��q�o�2Y���3%,g��2S������q�8��8�����Yg��eofL�OL==8��8��q�y��+%�Y�d�==0���8��q�q��`��b��,��,���N��9��OL4���8��q�h�O8�m�e�
q�q�p��8��>���-�i����'2�%�Znu��ijy��I���)?MYm������k<�~�h�&��h�&����������������������������������������������b""""""""""""""""""""""""""""""""""""".�N��`"[�2I
I+"��]���vH����3���I��X�����04�������0�7lY&���K���s#D��z�t�Q�$�BX�$E4�	`�9������Mu9���l��������v:�Y��f�Y�*
�&'��I��+h��*�(6R��1k
1�2;��J����h-�:3�R�fFQE����wn�9�C�f��GiH���Z��1�3M6�{�io,�z���o~w��s��V[��p�v=�&�s������b&"�7��*���n393"j�UN�2������M��e4
��I
�Q8Rbs�w������T���N�YHZ����fK�Ge���F��m�w�Sw�������h�&��h�&�"""""""""""""""""""""""""""""""""��>�������5��B8���Sg�j���Nle3\�k$�n�Wn��6��X��lUp�V)�gk3+]����`��������SG�
>q�q�q�q�L4��8��8��8�N4i��q�q�q�}�t��)�����w�O<�]u�B�q�g\��fB���;��<
N8��8��8����6����
�p:�gnN8���O�
=8��8��8���u�60PL�f3�Q�������m�oK.�4�|0���8��8��8�<���}���)c>d���>�|8��8��8��=�,���xy�V�d��N�i��q�q�q������Yfzd�G�xq�q�q�u�@L$N@Nbu>f�c�;���G�xq�q�q�i��qf�,�f�`�
<8��8��8��>���tr�J)
�
e�W���""""""""""""""""""""""""""""""""!��i��i�������~�O:���~��w7����y/�V�c�
E��8�D��i
���|S���V�8�z�����������_??}���b"""""""""""""""""""""""""""""""""""""/XwV����Q�/�m��:Jz+t�/�}=oS�����G��m��sk���oS���R��;l���=�'�.�
�Z]������F�SGz��k�mo=����c{���l���6��q2�r[����q>L�i>���zt�OQ�(�3�3y.��%��S30��rnM<*{�z���[��:�N�2��1�{l�;���g��p6����N`s�sl`����g�����0'�����v{�w��s$����m����Lo3k1�na3��8``I���=��c�;�^0ga;L�=��P0
cD	����u�7[	�����<N�6��x
i�4Y-��������m�����kyW��k#y��#$��W�D
{��}���o]���Mv�-��o$���w��7e,E=����w�����}i)���Z5%od�3��ZY2�\�4�[�_&S�H�`���ob7�v�OF�}���q\�i�%��r�[e'h��[w4q����z������;m�\���V����k��3�sy>ri�$�8R�V9I���[���]Zh�kx�K�>�@�M���>�O���%��waE���>R��y��=�V�o��u��l�����K������'����L�X�����Y)S���sF������yK���5]���U�����2^"S���y6���}13130DDDDDDDE��t�0`f�f����~	=��]u�B�q�g\��fB���=�"""""��{���������u����q�2	�{�"""""�x����N�L�f3�Q�����	�gx��y~x���=2������]~��DDDD^$���v��7�=�^�DDDD4���c�&s���`�"rsg��y�u����'I�H���@��L����{/���f��t���6�������
*�v������v�g3��t���',g.>{�`�K�fFY9�n�XF�V�|��E;t](C �T�HA
"�cI�#p�7t+7o�xf�m�PmP,�(�@�A�(��.��Ya��GM��p$��.��R#.�-!q2�v�$|����@��h������\"��!���Su+�X�l^�se��1Z��X�\��Yj[�/H��2m��p����%�&���%$����V�eET�T�'�o�z�j`�Mb|'���
�*� T�����x�]o"y�eWqM�$;r	��z� & (	���Q��\[���5Y:�c��E��%�����wo����>��[F����{���%�e��N�g�����(���I&�+��f�/Z������G�yD $�f�;���'~�����u��3�d�3!L�s����s��
�p:�gnN8��1��1��
	���z�8��y�;L��g�gy#1�L�q�f���;���ug_��[9�h�]l�' '16{����w^�`���������P0
QQ�|K|w�����Qx�:����$�-Mg4�=U��N]5z�6�ms�)����V��N�^��'z�_6���g���\IL!����c��7�Y����.s���fR���io����\��"F��'x�Is��<���I
�Y����H�ELsF� ���u�_ntq��������h�[��,��L�r�" �����Z�7�c��@p�\C�->��;��-+�-����m��v�s�����"Q�2�4���=���;������7��j���&F�!�14��CDb|�&v���x�gH	7�aN��f�����q�6��;N�������n���7.�4�x�D:F��$v9��������-q�l��	j"�;����k��R
���\���;l��6.;��oe���j��M��R�%�Wl�-�����$r�+"}��ye����{2=h�A�8v,(�<nr�b��S,���
�����1�$=a*,�w$��2FvaJOR�Q���%���M�������L�v�]�k�����J��!��E��d�������CnKZ�|f�����q��������By�!��B�.�1�
nK���:�a�^��}��
�zm�O8L��rqw�N������r��H��)77}����j	��y��Q��{1�t'����'	fM���f���"�v5�n�yr�H�-I%��C��S�y�U��\�����
�x��.��Y��+�� w_nEjn�h#����[c�u u�Ev��i�k���d��g:��%D��z�v�M�`��� Y�&�xr��f��WA��C��� ;��q:j"�-��s��\���xL����(����{{��j��p�������a��`���p�2����'������M<����3�����]��EJ�!e�)iZ�����p����=�������B��E�����U�x7�����{p+o��d���B�J�
�DwiI�� ��2�P�:�X��5��mW%v$/�$�'��g^1�A���!��ZR�����B!j�#X��4����v�`�(;r�bFv�a7}�E�A]nwi%s�������'��w��q�W�5'\I�GZF���3+��I$���\�/%��'��m��'G�n5�������4������n��G���9wI}����w�9wc��l[<�$���Xc����	]���\|�G��^�]�(�t,�J���]�Am��R��p�:�kdi�76��3��Y�h�v�7`tJ[��aY��8va��uj=xA���Y���Zs)������'{�J�K�y 2i�1t�d�49������z9�s9�&K�6q^����
�w��vxY�Sf��W/;���b�����gS�-���Yx�$�(�q�Z�;$�7�'�����jG9�niAVXW��)7��]�������W�j��-r�n�#A
��#{�B�u�&eo&��W��'.b&�t=v�unw���I����9�>�w�V
��S�0�\�Y4������� ��F�d��M[������Y�B�����]{�IOFl,�����.z��h��I�2���AW\�d"��o���-9�]{�U�Y���G��N��������n����n�{��q�d��B4�
��/����{��p�r�]�A�W�gE��z��	���7��0�,/�V���4!1���X����hY�q�H���gL�9�����On������$�|�69j���l�sY����=�����p�J�]�Qr�G��`s`��������i��/�n����q��*�`�Q
*��a��IA�	�[���qY
�%gu�u�n���i��1�JP_4�^�Ws!��ZI�����.�f��Qd���m�W��m�5�x��0e����U�m,��q�o���1d:f���w-B=�Os�v����9�<!���� ��F��1�����
�k`��}�kc��0�9I����v���;w�r��4^.��(�m�'�i����Rm����:����x���qdXu<�{yg7��L��|��2��@AC2�DrA��p/���`��7�:���o��V����'m=���F����H:�p��|�:a�%�Ym7xl��'�M� A�FC�8����sB[����v�N]��������
�Qn���s1MW���;��&�����p}�\��t��Enk*,ZI�vs6���6���p����XTHx�"G���D��_��'t�3,�����g��37L�8/����e�2�������s�=	��D3q^�#	$��B�1��aVV��8��p��(��S�;��i6�Kd�z��M����XY���r��6�E�Uj�b<U���,Tz9%���,M������Ckb%!26��A����r������[�5!];���z�	��vC7/p�9�`��XW{�&��	�<�i$���&��l���8Y�I�,�F���g;� E����[��u��-����[-�Cz��7dye�r��sk���s���0C�(����D-�y�:s�#�[jl��I��N��wwE�3N�b�;�q���!��w#�"k������68q�&�\������v��dkE�3���B�"�s���Yb������k
l�,^�rl�*�-Y��S����"���W�)��z�gb�����{���h���a)�c>��:�d�y�
��-"�I����%��3MR��n[�3h(:n����&��g�2h�j�p���){<���:g����<�}���[�k��y�a����E=L���"&�A��1����)@���(�h]����8	3��/ ������)&�#X����Z�D4QP�t�c�HEA��PoZ[�M��h3�%�-R�����82s1���v j����3��a���Hh��;���t��~#�����c���$H]�cD5M�����i�ncV�g��V+.�k�{
�|�U�s�P��U�C��G�**Th�8����J�C@���/�yH.�)A�,������_'�7\i�dr���e���v�3,���6��|�k+��J&���(�0�:�R��S3��[~��u��Yi��������G��v�_;�C��4�Ys���R�fr�Yc���5*#-�4�#��#��v�,�l�����H� �\�P�I�
i����Z��5dS#[�����wr��bQ�Z8�	�n��8��'Xb_bc�#'a��;��`2�2���{�������-2������Si$�g�8�F����C�({`u�d=Ji4:�`����N�����a%��8��E=n-%;&���
��yu<j^� ~��u�X��F,���{O{&�7�<�y�W3o�������������Vi��<���(���*���*��4��nee�5���g36�D�� x�"�N�T�B�(� Y�r�T�I�"E@�!��J�i���1�i��e$F�7d��F	�$�H3�:'�	n�go5K����������/J��w]^a�����w��&��8h[�9�91�a�O;�:��Fa�{!�;���V����NNadg���{�|8u�EswW'$��.bd�FW����%����h1 3]v��� ���"�#H�Eiq��i�mR���f��&�h"H%������n�hd��DOx��W �
|������
��;��J99���4�9%�C��p����W\��13k�L����N�6��Y%`W&���P9�H�0�(��	�Z�"T����t	���H4�TAA7
=lY4J(�`�lB-R��i���V�)u2AA�(��eU�U�����J�hIE	��s�9�5��,��E�FEd{�4�� �%��KJ@���&����'s�������,s0��
��9�uu���ck��[�sT+&#k�������vv"�=��	�,=�6�����w��������<�S8��g[����<���kjj�����>��u�h��^�H$3v� ��#^i�9Ri!Z��!�	��LB��D_Y>�0�����"���N���d�1���]������1f=y�O#37p��p�-�i�����N���������1��j�$3��IP�@ ���������)LyvH��d��&ia��-/HN�a$2�������#�5id���I�� C���Xt�-�a�.X���IAH��HZC
��E(�AOGMvJh��!FnR[��V��ly9��O7KH��(�;B�9�QZ���.&�+��bP��4L�j�����x�EL%���9���UMR9��F��N��8��a�Zc�ZP0�a	X���1J3 ��� x�f\i:jF�.@�{�nX���6�$�;6��4�(���X�h%J�h2=��� ��NIB$.�����y�`��;fJ�
���,��Y�4,���Ex�5E�A2UZ�L��-�(��@���~�J��~� AYY�3��b�����5��m@��H�!��<��t�p]7 �~��P�Q�TK4�0!���v#��1�O'���y�tT�2�)�F!��n�|��H�������v�(������*#q�ui�n�z����(�c����v�V�H1����ya�� "��4���&���q�,�A�V�!��~a�a�Ii���i�i��i��i��i��4�L0�u$M0�0�d�4�CM4�4�M4�M4�M4�Mi�
�Q$�EQ|#����'�&)�&����]��QC3;m��0�3����3330�3�n�30���31����0[l|���3��e���j�n��0�:H�M44�M#M4�M4�M4�M4���a�����t��3
�&`ff33�ffg[l��0U[mU[m����0�0�#M4��M4�4�M4�M4�M4�F�i�7m��Z����`�33�c�3�C�31��3�������L0�0�#M4��M4�4�M4�M4�M4�F�i�n���m�l�j������M�!��fUj�m�m��U���a�l���hi��F�i��i��i��i�M4�
��M�2�I Q%P������R9�o]Q,3�a0fc&M�o��0�
�4�M
4�H�M4�M4�M4�M4i��a������m�j���L�30������333&00�03���305������������&z�������30�2c�[[�m�������;*FH�An(9���%��(��� ���J�l���<�)�h�Q�\�%L��Cd@�H���(�R�H�R�dH���fb���F�������m�6�6����6fb��m2�l�����3���y���cld�f0Y3"l1�l�v��F���3,C��0��M0�/�����1�4��6-�0�m�bY#�G@���?����
`DRos�~��z���&
D< �n���+��uP J�!@z"�
b)�T$:������z���	]Xc��]\�#b�@�b���Jv���|�p���s�W���!7,=���hU��R�>������������Tn)|�j�K�Hv�@��v��jL������t���P$C����*�������MA������s0h�����G��	�8���pz}��������I�%P	�T��L@8�ws��������*��ow:O���q�4A����!��}�HX$l��Fh���/mD(�6�>7�9�~�;y�?�� C�%H@;Q�*�x��g'|o����9=m*��������y�� �jz�z�@;Y��y��xj��5 ���syW���W�x� H$�%!?
+�'B����wQe��	�:zD�QH�$Hd
�#��6~����H�M��$����@x���?4TQ)H|���i"C64&3��P�=S��t�S�E��:�����{���$@���\E�)��rL����'
����'������z:E��=;��'L�,��\%�0mt 9�@��21�X�Y#Fi}�����A4�nv�k&ZQ&~���
�x�:\���X����vn�x1a��z��Z��^[���a�gx5t���<���^�u�p4���c��k���ZBV�3G��3��a/�Q��G��q��wF1���[��+��l���P�UxC�z�/'dp�J��Ds�c��[��dyfX/��Up�g'XYg�(�)�!���	a3��m����$���5����Ffl�`cc\��5�6X�[3s�����2��"�3L�cflXf��6e��McF����
a�3���2�Q��	�3o�2660��2%���f�6a6f&�#!�xt�1�l�f��5�,ca�,b6���h�Q���*
5��f1��4���e����331�f!�4�C^I��bn���Y��2��r��u�[V��p���d�nc���d�Q�QVXXV��H��)
� /�DFA|L�@e	��I2y#`}H�`_�?�}��0v	�_Z�.�����g������ ���)�)��)����FD!IBU��
@0e$0F~��)JR��)JR��)b��)JR��)JR��)�#)JR��)JR��)JS��4���)JR��)JR���)JR��)JR��)MFR��)JR��)JR��a�)JR��)JR��)JX�7�������d��H��x��`�~����:�Dc�x����$�@��1"�R*)���D�x�
�~O��j�>@:S��}?s�����;s�k�������x�������h�@������ 
b��b��h�I"l��$R���4�|�4txx1	'�[:�0���������{�$6G�,�E,
,�Rm(��xn�<�:z.r�d��<�����:��^�R$FS�L�H$P�N*�� "��B:
J@)���"����>�S�* ��|/@^�b��y�w>����*R���

U?�S$T�MuQ�1F$P��8�H��z����`e���8��>��u1b��+���u���Yy�$P�ae'�Lf>H�d
+�b	d��2�<(M�H:M]�L��v�0�4<*��}�!:���e>���4	�*�,�� S�?�*�������d@�~5r�hc��/�I��9��>�����6I,����2&�����������M��)�0���K4��af�����)��f����Jit�iM.��)��,��_�F��
SJaL)�0�a�4�a�4�&SMiM4a�4�a�4�a�4�0��cR#�#�l�����HT������HA�O�0���i0��l0��l�
)��4��0��i0��l0��l�4 ��
8�x)��SUSUT)�v#),���&�-�)e�-�I���0���SI�Sa�Sd�a�4i����SI�Sa�Sd�
)�hN
�$��0��a�0�SM0�SM0�%4�
�L4SM0�SM0�SM0�)���c�Y�?$|����0��Mi����$���4i��i����
i�h�L��<G�/�8�����0a��F�l4i��4i��i�F�i4i��F�l�4�dH5$�eF@��]PU@Ug�t�`_}����[�����@��lp�?g�W�.��G�|c�/@`o�gNsm��C�f/q#�.�n{9����c�;�HGevA���Yo�%W �	��;,wd�S��]��J2�S,���������c�ch��E�L�:�}4���
���q��mj��s^K�
�4�{�xWW�g�8+��q�� f4�\�������.6t�Z:�k]�3�'�h�3����z]-]�����OAcx^��F�T�.x7,	fP���	W�;�oLj7��\��
����N�z���OL���������f*�.���Fh��[eRR�rL�w?�������E���$$��&����2��k��fb��"�N�:Q�HT�%$R(}��G�G�>@�t����) (��>)����|�����G�*z�\�G*���K�lIC�_������	�x H��S�"&� �)���j��;����~����}Z�`������;����
"1j�����|���� $)��*����2�����)���9�y��zu�g����}����]V2��W`Nrk���S��RU���T�,�~�)JR��)JR��(�)JR��)JR��)Jl�0�)JR��)JR��)JR��)JR��)JR��?`S
R��)JR��)JR��)JR��)JR��)JQJR��)JR��)JR�����7����=�h��r�`����h����B 7�c'��� l
���E���%$	Id��(*���G�=����#�l�3��}�;���7�^t2.�X�0��Z�;{������W�	<S��"TP��"RC��L����[���~Y��Q��v��M\f8����{��������*H)�����b���S�&@?�����)������u����xw���'}g�5���8�7�����3���&H��`X��R��)JR��)JR�R��)JR��)JR��)JR��)JR��)JR��)JR��)JR��)JQJR��)JR��)JR�����)JR��)JR��)�0���)JR��)JR��;����z�������A����U���}��w�����)2E���E�B� d@1P�{����}��X���r=�9�����K�NsV�7 : 8��R	�%0��MD�L���%T�HpM@�?�y#�}��R���������`T��X�����O�����z�����)�qO�H�x����x�
���Jq~i���1�i�%���p��"dJ��7�p�6��%��P�����oo
C�����{1q�������g�z�-��l�����Y3O�i�����d�F�����+���a�<�X�+h��X,'UG���z��`�����������!���xx*1y�i������6��bg\t����u��g9�%�0A�G{�wH����
`C�q���<��W��lR���zN�[R���<��pr'o�0
��cG<;�@;�6U�����V�GYdt��\	��rh�]��7��W�
����h��(hVr����iH���'�����3-����
�2Ez�;����X�rL��()�^�c��i
�ii�(@���;/ �66������w����)�
��D���)�%1V��*=��	�)d}�x��������1*(-����������z�����@z�N�����,�#"C��"RERG$}�����#:��~�:w��T����;����/�~>~y����?`H��Y#� ���RR��)���C�P���������9�2���j����3�����1#b���#)#�)��j��� T9������^�������yCGJ���������X�O�C`=�D�5�&|d$��Tn}����b��;���v��}@ $@��?��H�$�I�
H�H�${�>@�o����}�/h�~�g�E��_��#Hb���w��������}O�H'�2��+�dO��O?{}��������{�����t��}�?�>��\�LS��Y?�I����`@47?V���h���*7&��(>���v?���@�2E��!���/�x��x�~@8|g�c��^?�o�n��_^�%��K��:����w�<� )�b$�������4�F�i�SM4���M4�4�Mi����i�M4���iE4�M)��I�L4���h�M$�4�F�4��M4���4i��4�Mi����,DmXh�E130��bf�e�f�
�(ZJ@(�pQ1N��HS�8"����?�$�����?4��a�4�D�R�i����i�)��iM4�JSM0��i���MR�i�H��1�#�t"��I�)&S�0��i����i�4�M0��i��L4��i��L4��a�4���SL7�\D���a�4�
)��i$��a��i��i�SM0�M4�Ji�SM0�SM0���2O�K��-�xi��,�L6�f�I0�e�h�m�i�
�Y��m��4a��4�
�Y��m��4�m��4�A�<F]$��8��Ji�SM4I�)��iJi��R�i���M4�4�
)��iM4��)��?Y �
^�	=R�%��u���t�k.e��Ft��}	,�+���	�.X�����m�`N��!��n����X���e��������S���J�x�HBj�u�&���T<�cC�0��fz��XG��!7:Z�'�v��+�%�|:����TC}�����0�]�;t���u�������\�	�� C`m�3x��7����������-�q����a�S��=}34��y��NLY��#t�^%��`y�	\_c=8=������w��6�z]i�����HK$��KH���}������w������*���
(������vxT[F,yvX�W6���r�b��[|��%O�6AO��@�@�x�
)��/��v�� ~@;S�'�t)��
Js�3����/�P7���E���2q!�#�"#@�H�I	�	O������u��6l}}7�\�t���������}��R%RRBT��`T������]���vn�����g8�:7<}�UY�H�=��@	d�"���(l@�G�0�`RG?r���������{����
_e��*_
��8�.��N}k�)<SBRC�z�&�\R@$�* R~7Y�{xfg����g������e=s�N� � _AN)�)���1:��������B�5�Qtt�~Z1}& 
�D�S�����_pu��o���2p�4>��;�4@�	���H��d)��� 1�~Z��Ns�����/v�th7���� �:RC�n�R������6�]�p���A�>7���3.s������b	����&�+�,> ;��E4x�A��95!3P5R�DZ'���HeII�*H�����#�g�c�C�����)?y�E��+��p�1�$�H���G�?��2f�r���V��L%Xed�*���\�\�d2G�z�0�d{�����M�4vF��=�
�
w��1�w<��"����bv��Q[[����g��'�|t���,;'�����#G^�]�U�u�������>��s}5��v[�(Pk:�>���g*8j�9[T���sVJ}9�8X�n���#B�#>L=�^^�����Q����D(1�'c�=6�����VI�T#!RM�w9"�^������+j���8{Iqr���;:�����������
��N(���,�x�l����s+�iX������?���L�p~�fX�v_o�� �s�N�1
20�kU�z����_���b\3�(7L;��,6����N�*~S�<VH�"��=��F�?�����.|=��v��3����=CC�$�����^y��?�b�!�N�c�*O�0�'�(�����z
��"��,�fvDqk,�9���������_�@�:��0�R�OT�%;��}�Y��{g�O|�O��Y6�{�%��"���E'�,����b����P�_�&
��Cr0@W�]}_+hX=&F�3P���]b+���`���}������_��:�3������V'<�`�B��l},�$a�)JR��)JR��)J|BFR��)JR��)JR��@>H�JR��)JR��)JR��aJR��)JR��)JR��)JR��)JR��)JR��)JR��)JR��)�E0�)JR��)JR��)Mu�� ��nxf�� ,���T	�S_?;�`Y���u�Q�BRT���K���_x�h%�����s�������pG���;5_�@�/�� �������T �����<p;�t�,}�����|}��v�����l��H?)�MU�H��}��u���!y��aI����;������"!H��0���S
a�4�
)�0��a�0��i���F�iMa�4�H���)�0���SM0��i����SM0��S
i4�Ji#L4��0��X�$�SA�
H�d�y������b$��XJ32c6GV[36�VTH��
`(Y
��e����f�h0	��A)��6{�Q�Ta��S
aL)�4�
)��iL)�4�
)�0��L4��4�Jh�
)����8��z����R�<S��!���Ke[%\$�4�`�a��4�F�`�
4i�0�h�M$h�M4�d�0�F`�i��4�F`�M4a�
&�h�F�h���4�K"��# Y$�`�i��4�F`�M4a�
&�h�F�h���4�(���#�\�a�>UE����0��iM4�Ji�0�Ji�0�IM4�I�L4SM0�$��m��3_��c;�n����"
@�N'��M5����A�&��,)n�2F�������!��&X����A:M|�g��>S�����^Kb���p���I6���Q�<��{6��<�y]��T
`�����%����5l�����
�*��:�\�+����������+@�w1���vb6�(�������,������,Z�=������H�s�wV�?�f�����5�����������0��w�M���.�����J�����>-XR��4�8P����g
�]�%�����LC���0#��{�" �����znN��;Zj
��(�bd�0@?l<�SdXt
����
j.��Xg<�{h����&��D���4�Fy#9������D	%D	)�Hb����H�����]���\h�Wnl|�K�<~��������)
G���j��H�)���tx}����3�.��7���}�p���ns�y���@�Ie \R�EV?���(� ��SH�Ow���������>����l*]���`e��v��X����>H����=���y���S��o�9���$�@a %1LuI%!
S�w���������dmx%{���������������aIa�MS�EL�����:G�#��?M�{~���(Sq$��6����	;��<�s�}�
~�N�
SQ�@$%:���>��Yo�������eAK��\z�-�zB�"�� �B�@q@��_{w�}�<����~|���$��e��0>��<��"�!(����d�{"��J�6DrF��J@ ��	�\�	��T�����=P�"��f�*���z�T@AcCJF1��Pdj=L1��G���11zt��DQ���A]fW��t�v�'j@R���,S�:���������P���J}3,��O?{
$V��)I������.y��x�=y��l�0_f����Ic��}i��u'�#�f(���M�
78�<���`�����#,fm`�Y���^uc���<X�3���-��KE1����.��l�mv����f������;����271���5��-�o����s�`����cA�!,�U�f�n
��=f�������sp�����d�����T��a{��`���.�l�����J�g��]�d������K,��^Y�4���Y�0y�U1Dm�sL�*��M@9���J~P���1��!x}���}y�_r��O��!�������E.8��
��@�� Q ���)��u����xo�y�_����^�<��f~0>������x�BT@����2 g��L{��"J�._�U�ps*c(y0�<��* ��F^U?{�@nu�����D}b��A�>@���,
l��A�T.����|%�
��^N����p�����L�n�
@��$& �&�>���)��)�����v���}be\������7f= 
I��@>���66E%�I`~�z�) ���|�����W�����{�A�rH�2Y�	�@��0& }	:H�G�<������������@((�*��_�:���?��~�II�������<@������${���V�����s����]�z1�qy����4���2E�(�E�I�~�����������������.�.�.�.�I���JaL)�0���S
aL)�0���S
aL)�0��}G��"���M�
j��.��=)OJaL)�0���S
aL)�0���S
aL)�0��G�/�? t#�(?G�-���2�$��
aL)�0���S
aL)�0���S
aL)�0�L�#)�0���S
aL)�0���S
aL)�0������^�l��1�V�^�����H
L����T�P��S
aL)�0���S
aL)�0���S
aL)�0~��}��A���S
aL)�0���S
aL)�0���S
aO�I�B���y�����/�|�|������w�3���z�K���*��Y��T��������ys���]SFM��g�7��1,��;w
��������{H��;��3�Uh��$
 �>�+C��t��`�d�x=�"��5]�����(�{6��
�����
��$2.����
eWE=wl,�o�QBR����L������6EUd��:^�x��Jo/`����s#��4I]��v�r��fU��:{��'u��!�i�7��{�Ow�~P���fM�	"����Q��[Y�}�]�a�������)=��}D|�X�T4G��*��o���h�b� uO���$ '�q�N����~���Q�����H���-�k�����y�{�H�����dzh��qU���PK*B*�T�^���/���u���QT�H	��B������k?���>������f���k����~~��v�/��?�dY"bFH�$Q,��?d~��>W��W�P����E��X�U
[Y��O�t���H<RS��:Sd�$}��_m����>�*}a&a��Y}��~~y������0��D	�`6b>�_��?����%]H��(r����������q@P�@Q.^�B[7$m���3���������C����?)����'@����^4�|O��W�������������z|�j0������� ����9�S��E]V���V�Y����O�`d�"��b�0��"�+�<)
S��B����
'?	B��� �O����>���)�v&bS�:J �j���(Y~"h�����/�20�+,i�3��d��:S�����y��5J������@�	�o���������>�!�,����R��&�x�;�Ei����wy�3��xKI$�D��X���u>�����h���}�!��tS��qU��>����:��\�&���%��)���M��������;E��I�Qd!-�I�]���g��P99�{��|o=|����j��X��Nz,���I�K�	�Db��fXe
������Noe������c}i.��r�v��k����/��y�21�'",S��vZj�x�I��v���%�d�"�l���~��A���\��_��3]��x��d2�eS ��
�v�:G@�����$� X�����HM�w-��T����6;�L<w���@|���D
 E?)�) )���:��w����o�:����R88-�����'���I?)!��O� ;@=�;����_���f����X�}ayXw��6U�N))����x{�>�������/K��J,d�>������DDC��
 *YS��@�T��!P%0���A����?)���@1����jJy�>��?�oh���9"�����lP�/~��z����������N����������Mt
��S�����G����A��������5 G�`D@p
����$|�G�Nu���<�r��c�j;`��H���=@L����.��x���m�����Z}��!���!��,�s�Q�w"crv�����������n���j�A�"�F�Q"��D�F@� @���" �"0 D@��R0i�& D��&��H�!(�"DL�""(�SQ&��4���@�
#��0i�D�ED@� DL@�01@D�@4�
`��!��0B$@"LL @���SDD@@")�"" 	� @"
`L��SH@D@  "S00b����(�)�D@@��)�i�b��4����"AH�" 
h�D"@")�(�"i�A� �@0�@
0 @@��4�LLED&�DE� ""�LBi�(�Db�Ph�D�)���JF	���0�@����@ "@�LM1H�"�0"iJ$B&�c@��� D")�"" ��m��� ��� ���	��?�����d� !��8 �	�������N$!����
v37-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchapplication/octet-stream; name=v37-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchDownload
From 6bc406fbe6129afcd5d35cb75ed9178acf7b959a Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v37 1/2] Add the PlannerInfo context to the parameter of
 find_ec_member_matching_expr()

---
 src/backend/optimizer/path/equivclass.c |  4 +-
 src/backend/optimizer/plan/createplan.c | 64 +++++++++++++++----------
 src/include/optimizer/paths.h           |  3 +-
 3 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9cd54c573a8..6eceaaae1c2 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -817,7 +817,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
@@ -996,7 +996,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 75e2b0b9036..163923072df 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -294,7 +298,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1281,7 +1285,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1325,7 +1329,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1466,7 +1470,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1497,7 +1501,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1979,7 +1983,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2193,7 +2197,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2217,7 +2221,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2286,7 +2290,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4544,7 +4548,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4553,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4578,7 +4584,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6195,6 +6201,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the plan node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' identifies the child relation being sorted, if any
@@ -6228,7 +6235,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6295,7 +6302,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6323,7 +6330,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6339,7 +6346,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6405,12 +6412,14 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6419,7 +6428,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6439,14 +6448,16 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  * make_incrementalsort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6455,7 +6466,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6812,7 +6823,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6875,7 +6887,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 16dc4d5ee82..96884f89f5d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
-- 
2.43.0

v37-0002-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v37-0002-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 192199db178206c2aa6b9a94bc5e80abdace5e0e Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v37 2/2] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c     |  14 +-
 src/backend/nodes/outfuncs.c            |   1 +
 src/backend/optimizer/path/equivclass.c | 347 +++++++++++++++++-------
 src/backend/optimizer/path/indxpath.c   |  14 +-
 src/backend/optimizer/path/pathkeys.c   |   9 +-
 src/include/nodes/pathnodes.h           |  83 ++++++
 src/include/optimizer/paths.h           |   5 +
 src/tools/pgindent/typedefs.list        |   1 +
 8 files changed, 363 insertions(+), 111 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index ac14c06c715..38891e74ce7 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7918,9 +7917,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 557f06e344f..678d7169823 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -466,6 +466,7 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
 	WRITE_NODE_FIELD(ec_members);
+	/* XXX ec_childmembers? */
 	WRITE_NODE_FIELD(ec_sources);
 	/* Only ec_derives_list is written; hash is not serialized. */
 	WRITE_NODE_FIELD(ec_derives_list);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 6eceaaae1c2..4fe62653b20 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,11 +34,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Relids child_relids);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -318,7 +330,8 @@ process_equivalence(PlannerInfo *root,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -428,7 +441,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -445,7 +458,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -478,9 +491,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -566,11 +579,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *		Build a new EquivalenceMember without adding it to an EC.  If 'parent'
+ *		is NULL, the result will be a parent member, otherwise a child member.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -597,11 +612,80 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	/* add to the members list */
+	ec->ec_members = lappend(ec->ec_members, em);
+
+	/* record the relids for parent members */
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ *
+ * 'root' the PlannerInfo that 'ec' belongs to.
+ * 'ec' the EquivalenceClass to add the child member to.
+ * 'ec_index' the index of 'ec' within root->eq_classes, or -1 if maintaining
+ * the RelOptInfo.eclass_indexes isn't needed.
+ * 'expr' the em_expr for the new member.
+ * 'relids' the 'em_relids' for the new member.
+ * 'jdomain' the 'em_jdomain' for the new member.
+ * 'parent_em' the parent member of the child to create.
+ * 'datatype' the em_datatype of the new member.
+ * 'child_relids' defines which elements of ec_childmembers to add this member
+ * to.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Relids child_relids)
+{
+	EquivalenceMember *em;
+	int			relid;
+
+	Assert(parent_em != NULL);
+
+	/*
+	 * Allocate member to store children.  An array of Lists indexed by relid.
+	 */
+	if (ec->ec_childmembers == NULL)
+		ec->ec_childmembers = (List **) palloc0(root->simple_rel_array_size * sizeof(List *));
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	/* Record this member in the ec_childmembers Lists for each relid */
+	relid = -1;
+	while ((relid = bms_next_member(child_relids, relid)) >= 0)
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+		ec->ec_childmembers[relid] = lappend(ec->ec_childmembers[relid], em);
+
+		/* Record this EC index for the child rel */
+		if (ec_index >= 0)
+		{
+			RelOptInfo *child_rel = root->simple_rel_array[relid];
+
+			child_rel->eclass_indexes =
+				bms_add_member(child_rel->eclass_indexes, ec_index);
+		}
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -672,7 +756,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -687,10 +772,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(root, &it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -747,7 +831,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -821,15 +905,16 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -898,7 +983,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -912,9 +998,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -1219,7 +1305,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1288,7 +1375,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1621,7 +1709,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1632,10 +1721,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1668,6 +1756,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1742,6 +1831,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1749,7 +1839,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2206,7 +2296,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2333,7 +2424,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2551,8 +2643,8 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2623,8 +2715,8 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2710,6 +2802,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2722,7 +2815,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2735,29 +2827,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2802,12 +2880,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relids);
 			}
 		}
 	}
@@ -2854,7 +2935,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2867,25 +2948,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2930,9 +3001,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_joinrel->relids);
 			}
 		}
 	}
@@ -2979,14 +3056,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relids);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3001,6 +3082,85 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members belonging to the given 'ec'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec_members for 'ec', and;
+ *	- Any child member added to the given ec by add_child_eq_member() where
+ *	  the child_relids specified in the add_child_eq_member() overlap with
+ *	  the child_relids in the setup_eclass_member_iterator() call.
+ *
+ * Note:
+ *	- The given 'child_relids' must remain allocated and not be changed for
+ *	  the lifetime of the iterator.
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	it - A pointer to the iterator to set up.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	child_relids - The relids to return child members for.
+ */
+void
+setup_eclass_member_iterator(PlannerInfo *root, EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids child_relids)
+{
+	it->root = root;
+	it->ec = ec;
+	/* no need to set this if the class has no child members */
+	it->child_relids = ec->ec_childmembers ? child_relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator(). NULL is
+ *	  returned if there are no members left, in which case callers must not
+ *	  call eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	EquivalenceMember *em = NULL;
+
+	while (it->current_list != NULL)
+	{
+nextcell:
+		while (it->current_cell != NULL)
+		{
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			goto end;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->child_relids, it->current_relid)) > 0)
+		{
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/*
+			 * If there are members in this list, use it, this will exclude
+			 * RELOPT_BASERELs as ec_childmembers[] are not populated for
+			 * those.
+			 */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+		goto end;
+	}
+
+end:
+	return em;
+}
 
 /*
  * generate_implied_equalities_for_column
@@ -3053,6 +3213,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
 
@@ -3076,14 +3237,12 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(root, &it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
 
 		if (!cur_em)
@@ -3099,8 +3258,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3318,8 +3477,8 @@ eclass_useful_for_merging(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6386ce82253..5c6410e0631 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3796,7 +3796,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
@@ -3813,7 +3813,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3833,9 +3834,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(root, &it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a9419d37e2f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1151,8 +1151,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1709,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6e034960896..ead5ffa45d1 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1414,6 +1414,22 @@ typedef struct JoinDomain
  * In contrast, ec_sources holds equality clauses that appear directly in the
  * query. These are typically few and do not require a hash table for lookup.
  *
+ * 'ec_members' is a List of all EquivalenceMembers belonging to
+ * RELOPT_BASERELs.  EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and
+ * RELOPT_OTHER_JOINREL relations are stored in the 'ec_childmembers' array in
+ * the index corresponding to the relid.  'ec_childmembers' may be NULL if the
+ * class has no child EquivalenceMembers.
+ *
+ * For code wishing to look at EquivalenceMembers, if only parent-level
+ * members are needed, then a simple foreach loop over ec_members is
+ * sufficient.  When child members are also required, it is best to use the
+ * functionality provided by EquivalenceMemberIterator.  The reason for this
+ * is because large numbers of child EquivalenceMembers can exist in queries
+ * to partitioned tables with many partitions.  The functionality provided by
+ * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
+ * which belong to specific child relids.  See the header comments for
+ * EquivalenceMemberIterator below for further details.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1432,6 +1448,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_childmembers;	/* array of Lists of child
+									 * EquivalenceMembers */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1493,6 +1511,71 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator allows efficient access to sets of
+ * EquivalenceMembers for callers which require access to child members.
+ * Because partitioning workloads can result in large numbers of child
+ * members, the child members are not stored in the EquivalenceClass's
+ * ec_members List.  Instead, these are stored in the EquivalenceClass's
+ * ec_childmembers array of Lists.  The functionality provided by
+ * EquivalenceMemberIterator aims to provide efficient access to parent
+ * members and child members belonging to specific child relids.
+ *
+ * Currently, there is only one way to initialize and iterate over an
+ * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
+ * and eclass_member_iterator_next functions.  The iterator object is
+ * generally a local variable which is passed by address to
+ * setup_eclass_member_iterator.  The calling function defines which
+ * EquivalenceClass the iterator should be looking at and which child
+ * relids to also include the members for.  child_relids can be passed as NULL
+ * but the caller may as well just perform a foreach loop over ec_members as
+ * only parent-level members will be returned in that case.
+ *
+ * When calling the next function on an EquivalenceMemberIterator, all
+ * parent-level EquivalenceMembers are returned first, followed by any
+ * child members for the relids specified by the child_relids parameter as
+ * specified when calling setup_eclass_member_iterator.  The child members
+ * returned are members which have any of the relids mentioned in
+ * child_relids.  That's not to be confused with returning members which
+ * contain *all* of the child relids specified when calling
+ * setup_eclass_member_iterator.  It is up to the calling function to ensure
+ * that the returned member matches what is required for the purpose.
+ *
+ * It is also important to note that when dealing with child
+ * EquivalenceMembers for RELOPT_OTHER_JOINRELs that it's possible for the
+ * same EquivalenceMembers to be returned more than once by the next function.
+ * This is currently not seen to be a problem, but some callers may want to be
+ * aware of it.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(root, &it, ec, child_relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *		...
+ * }
+ * -----
+ * It is not valid to call eclass_member_iterator_next() after it has returned
+ * NULL for any given EquivalenceMemberIterator.
+ */
+typedef struct
+{
+	PlannerInfo *root;			/* The PlannerInfo where 'ec' belongs */
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		child_relids;	/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 96884f89f5d..901f50b0182 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -184,6 +184,11 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(PlannerInfo *root,
+										 EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids child_relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index c3f05796a7c..1513b247d9d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -711,6 +711,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.43.0

#117Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#116)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

Thank you very much for your continuous contributions to this patch
series, and especially for providing these new patches despite the
time constraints.

On Fri, Apr 4, 2025 at 3:04 PM David Rowley <dgrowleyml@gmail.com> wrote:

On Fri, 4 Apr 2025 at 00:34, David Rowley <dgrowleyml@gmail.com> wrote:

I've attached 2 patches, which I think addresses most of this, aside
from the last point.

These do need more work. I've just attached what I have so far before
I head off for the day. I am planning on running some performance
tests tomorrow and doing a round on the comments.

I've done some further work on this, mostly relating to the code
comments. I also removed the now-empty
dispose_eclass_member_iterator() function.

I agree with this new approach. It significantly simplifies the
overall architecture of the patch series while still maintaining
excellent performance. Thank you again for your effort here.

A couple of things which I'm still uncertain of:

1. How to handle the ec_childmembers array in _outEquivalenceClass().
There's no field to know the size of the array. Maybe I should add one
and then print out the non-empty lists.

I'm also not certain about the best solution here. As you suggested,
adding a field representing the array size to EquivalenceClass seems
like a reasonable approach.

2. When processing RELOPT_OTHER_JOINREL in add_child_eq_member(), I'm
adding the member to each List for all individual relid mentioned in
child_relids. This will result in the member going on multiple Lists
and cause the iterator to possibly return the member multiple times.
That might matter in a few places, e.g.
generate_join_implied_equalities_normal() keeps some scoring based on
the number of members.

For #2, Yuya's Bitmapset approach didn't suffer from this issue as the
Bitmapsets would be unioned to get the non-duplicative members. I
wondered about doing list_append_unique() instead of lappend() in
generate_join_implied_equalities_normal(). Unsure. The only other
thing I can think of is to do something else with members for
RELOPT_OTHER_JOINREL and store them elsewhere.

Another approach I have in mind is adding an iterator pointer to each
EquivalenceMember to track the iterator that last returned each
member. When the iterator is about to return a member, it would first
check if that member's iterator pointer matches the current iterator.
If it does, we know this member has already been returned, so we skip
it. However, this approach does not work when iterators are called
recursively and leads to increased complexity in the data structure.
Your proposed solution using list_append_unique() instead of lappend()
seems practical since the number of EquivalenceMembers handled in
generate_join_implied_equalities_normal() is usually limited.

I also did some benchmarking using the attached script. I've attached
the results of running that on my AMD Zen2 machine. See the end of the
script for the CREATE TABLE statement for loading that into postgres.

The results look pretty good. v37 came out slightly faster than v36,
either noise or because of dispose_eclass_member_iterator() removal.

Thank you for running your benchmarks as well. Your results look
promising, demonstrating both reduced planning time and lower memory
consumption.

I have also conducted benchmarks using queries A and B, which I have
used previously and are in [1]/messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com. Here is a quick summary:

* The new patch (v37) shows better performance improvements compared
to previous versions (v35 and v36).
* The performance gains are significant and worth committing.
* Performance regressions are negligible or non-existent, even with a
small number of partitions.
* Memory usage in v37 is lower than v35 and almost identical to the master.

Detailed results are as follows:

The following tables and attached figures indicate that v37 achieves
up to 415.4% and 280.3% speedups for queries A and B, respectively.
These improvements are better than those seen in v35 and v36.

Importantly, v37 does not appear to introduce any regressions. Its
speedups exceeded 100% in all tested cases except for the one with two
partitions in query A. Even in that case, the performance remained at
99.9% of the master, demonstrating that the regression is negligible.

Moreover, Table 5 and the attached figure show v37 consumes no
additional memory compared to the master.

Table 1: Planning time for query A (ms)
-------------------------------------------
n | Master | v35 | v36 | v37
-------------------------------------------
1 | 0.274 | 0.273 | 0.274 | 0.270
2 | 0.285 | 0.288 | 0.286 | 0.286
4 | 0.381 | 0.378 | 0.368 | 0.372
8 | 0.477 | 0.468 | 0.471 | 0.471
16 | 0.698 | 0.671 | 0.667 | 0.650
32 | 1.251 | 1.190 | 1.169 | 1.149
64 | 2.848 | 2.550 | 2.463 | 2.444
128 | 6.051 | 4.692 | 4.669 | 4.588
256 | 16.812 | 10.851 | 10.784 | 10.742
384 | 30.985 | 16.640 | 16.354 | 16.243
512 | 50.548 | 23.174 | 22.981 | 22.940
640 | 72.046 | 28.725 | 28.679 | 28.296
768 | 102.668 | 34.975 | 34.759 | 34.280
896 | 150.563 | 46.764 | 46.313 | 46.006
1024 | 197.559 | 48.243 | 47.777 | 47.553
-------------------------------------------

Table 2: Speedup of query A (higher is better)
---------------------------------
n | v35 | v36 | v37
---------------------------------
1 | 100.6% | 100.2% | 101.5%
2 | 99.2% | 99.9% | 99.9%
4 | 100.6% | 103.3% | 102.3%
8 | 101.8% | 101.2% | 101.2%
16 | 104.0% | 104.6% | 107.4%
32 | 105.1% | 107.0% | 108.9%
64 | 111.7% | 115.6% | 116.5%
128 | 129.0% | 129.6% | 131.9%
256 | 154.9% | 155.9% | 156.5%
384 | 186.2% | 189.5% | 190.8%
512 | 218.1% | 220.0% | 220.4%
640 | 250.8% | 251.2% | 254.6%
768 | 293.5% | 295.4% | 299.5%
896 | 322.0% | 325.1% | 327.3%
1024 | 409.5% | 413.5% | 415.4%
---------------------------------

Table 3: Planning time for query B (ms)
------------------------------------------
n | Master | v35 | v36 | v37
------------------------------------------
1 | 12.300 | 12.419 | 12.219 | 12.209
2 | 11.741 | 11.761 | 11.652 | 11.639
4 | 12.573 | 12.376 | 12.390 | 12.418
8 | 13.653 | 13.242 | 13.074 | 13.081
16 | 15.693 | 14.717 | 14.503 | 14.416
32 | 20.957 | 17.890 | 17.732 | 17.675
64 | 35.914 | 25.772 | 25.633 | 25.495
128 | 79.154 | 42.826 | 42.441 | 42.407
256 | 243.880 | 88.246 | 87.626 | 87.011
------------------------------------------

Table 4: Speedup of query B (higher is better)
--------------------------------
n | v35 | v36 | v37
--------------------------------
1 | 99.0% | 100.7% | 100.7%
2 | 99.8% | 100.8% | 100.9%
4 | 101.6% | 101.5% | 101.2%
8 | 103.1% | 104.4% | 104.4%
16 | 106.6% | 108.2% | 108.9%
32 | 117.1% | 118.2% | 118.6%
64 | 139.4% | 140.1% | 140.9%
128 | 184.8% | 186.5% | 186.7%
256 | 276.4% | 278.3% | 280.3%
--------------------------------

Table 5: Memory usage (MB)
(n: number of partitions per table; PWJ: partition-wise join)
----------------------------------------------------------------
Query | n | PWJ | Master | v35 | v36 | v37
----------------------------------------------------------------
A | 1024 | OFF | 48.138 | 49.606 | 48.341 | 48.341
A | 1024 | ON | 127.483 | 128.952 | 127.687 | 127.687
B | 256 | OFF | 92.507 | 96.882 | 92.632 | 92.632
B | 256 | ON | 5803.316 | 5807.691 | 5803.441 | 5803.441
----------------------------------------------------------------

Again, I greatly appreciate your taking the time to significantly
improve this patch. I'd also like to thank Tom once again for his
valuable feedback, which greatly contributed to these improvements.

[1]: /messages/by-id/CAJ2pMkYcKHFBD_OMUSVyhYSQU0-j9T6NZ0pL6pwbZsUCohWc7Q@mail.gmail.com

--
Best regards,
Yuya Watari

Attachments:

figure.pngimage/png; name=figure.pngDownload
#118Amit Langote
amitlangote09@gmail.com
In reply to: David Rowley (#116)
Re: [PoC] Reducing planning time when tables have many partitions

Hi David,

Impressive results!

On Fri, Apr 4, 2025 at 3:05 PM David Rowley <dgrowleyml@gmail.com> wrote:

I've done some further work on this, mostly relating to the code
comments.

It looks to me like the following hunks in 0002 probably belong in
0001, unless you’re planning to commit the patches together anyway:

diff --git a/src/backend/optimizer/path/indxpath.c
b/src/backend/optimizer/path/indxpath.c
index 6386ce82253..5c6410e0631 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -190,7 +190,7 @@ static IndexClause
*expand_indexqual_rowcompare(PlannerInfo *root,
                                                 IndexOptInfo *index,
                                                 Oid expr_op,
                                                 bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo
*index, List *pathkeys,
                                     List **orderby_clauses_p,
                                     List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -934,7 +934,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
          * query_pathkeys will allow an incremental sort to be considered on
          * the index's partially sorted results.
          */
-        match_pathkeys_to_index(index, root->query_pathkeys,
+        match_pathkeys_to_index(root, index, root->query_pathkeys,
                                 &orderbyclauses,
                                 &orderbyclausecols);
         if (list_length(root->query_pathkeys) == list_length(orderbyclauses))

The comment on EquivalenceMember might benefit from a mention of how
ec_childmembers now fits into the picture -- do you think it’s worth
updating?

/*
* EquivalenceMember - one member expression of an EquivalenceClass
*
* em_is_child signifies that this element was built by transposing a member
* for an appendrel parent relation to represent the corresponding expression
* for an appendrel child.
...

+ /* XXX ec_childmembers? */

Maybe we don’t need to print these, since the comment on em_is_child
suggests they aren’t really full-fledged EC members and are meant to
be ignored by most operations?

--
Thanks, Amit Langote

#119Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: David Rowley (#116)
Re: [PoC] Reducing planning time when tables have many partitions

Hi David,

On Fri, Apr 4, 2025 at 11:34 AM David Rowley <dgrowleyml@gmail.com> wrote:

I also did some benchmarking using the attached script. I've attached
the results of running that on my AMD Zen2 machine. See the end of the
script for the CREATE TABLE statement for loading that into postgres.

The results look pretty good. v37 came out slightly faster than v36,
either noise or because of dispose_eclass_member_iterator() removal.

Here are my benchmarking results
Planning time: columns correspond to number of joins, rows to number
of partitions, each cell is a triplet (s, md, pd) where
s is improvement as percentage of planning time without patch (higher
the better)
md and pd are standard deviation in planning time with and without
patch respectively as % of respective averages.

planning time improvement with PWJ=off
num_parts | 2 | 3
| 4 | 5
-----------+------------------------------+----------------------------+----------------------------+----------------------------
0 | s=-4.43% md=16.72% pd=16.41% | s=-2.93% md=5.33% pd=5.27%
| s=-0.10% md=4.28% pd=4.49% | s=-2.60% md=4.80% pd=4.28%
10 | s=1.32% md=9.93% pd=9.13% | s=2.70% md=1.90% pd=1.90%
| s=4.53% md=1.55% pd=1.59% | s=4.96% md=0.99% pd=0.94%
100 | s=29.15% md=3.96% pd=4.66% | s=38.11% md=0.43% pd=1.22%
| s=44.17% md=1.19% pd=1.21% | s=43.97% md=0.37% pd=0.27%
500 | s=63.12% md=1.39% pd=3.80% | s=69.57% md=1.76% pd=0.73%
| s=71.73% md=0.88% pd=0.81% | s=66.08% md=0.72% pd=0.57%
1000 | s=76.33% md=0.82% pd=1.72% | s=80.37% md=0.30% pd=0.82%
| s=75.30% md=1.23% pd=0.64% | s=67.06% md=0.83% pd=0.19%
(5 rows)

planning time improvement with PWJ=on
num_parts | 2 | 3 |
4 | 5
-----------+----------------------------+----------------------------+----------------------------+----------------------------
0 | s=-2.08% md=5.87% pd=6.16% | s=-2.22% md=4.70% pd=5.29% |
s=-1.77% md=5.40% pd=4.23% | s=-3.96% md=3.96% pd=3.89%
10 | s=-0.93% md=3.34% pd=2.89% | s=0.06% md=0.96% pd=0.52% |
s=2.09% md=0.43% pd=0.60% | s=2.03% md=0.39% pd=0.66%
100 | s=20.31% md=1.70% pd=1.19% | s=16.98% md=1.28% pd=1.70% |
s=13.35% md=0.32% pd=0.77% | s=14.12% md=1.19% pd=0.43%
500 | s=51.98% md=3.12% pd=4.25% | s=50.85% md=0.45% pd=0.48% |
s=47.27% md=0.16% pd=0.82% | s=40.60% md=0.30% pd=0.57%
1000 | s=67.34% md=1.67% pd=1.16% | s=69.54% md=0.20% pd=0.44% |
s=61.31% md=1.13% pd=0.63% | s=54.66% md=0.38% pd=0.57%
(5 rows)

The deviations are mostly within noise range so the results are
reliable. There are some cells, corresponding to lower number of
partitions and join, which show regression in planning time but that's
within noise range. I think that can be ignored. For a higher number
of partitions and joins the improvements are impressive.

planning memory improvement with PWJ=off
num_parts | 2 | 3
| 4 | 5
-----------+----------------------------------+----------------------------------+----------------------------------+----------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB,
pm=21 kB | s=0.00%, mm=27 kB, pm=27 kB | s=0.00%, mm=33 kB,
pm=33 kB
10 | s=-0.46%, mm=218 kB, pm=219 kB | s=-0.44%, mm=455 kB,
pm=457 kB | s=-0.35%, mm=868 kB, pm=871 kB | s=-0.24%, mm=1697 kB,
pm=1701 kB
100 | s=-0.88%, mm=1824 kB, pm=1840 kB | s=-0.62%, mm=3718 kB,
pm=3741 kB | s=-0.50%, mm=6400 kB, pm=6432 kB | s=-0.38%, mm=10233 kB,
pm=10 MB
500 | s=-0.83%, mm=9395 kB, pm=9473 kB | s=-0.56%, mm=20 MB,
pm=20 MB | s=-0.44%, mm=35 MB, pm=35 MB | s=-0.30%, mm=59 MB,
pm=60 MB
1000 | s=-0.79%, mm=19 MB, pm=20 MB | s=-0.49%, mm=45 MB,
pm=45 MB | s=-0.37%, mm=82 MB, pm=83 MB | s=-0.24%, mm=146 MB,
pm=147 MB
(5 rows)

planning memory improvement with PWJ=on
num_parts | 2 | 3
| 4 | 5
-----------+----------------------------------+----------------------------------+----------------------------------+----------------------------------
0 | s=0.00%, mm=15 kB, pm=15 kB | s=0.00%, mm=21 kB,
pm=21 kB | s=0.00%, mm=27 kB, pm=27 kB | s=0.00%, mm=33 kB,
pm=33 kB
10 | s=-0.55%, mm=365 kB, pm=367 kB | s=-0.25%, mm=1198 kB,
pm=1201 kB | s=-0.08%, mm=3571 kB, pm=3574 kB | s=-0.04%, mm=10 MB,
pm=10 MB
100 | s=-0.48%, mm=3337 kB, pm=3353 kB | s=-0.20%, mm=11 MB,
pm=11 MB | s=-0.09%, mm=33 MB, pm=33 MB | s=-0.04%, mm=97 MB,
pm=97 MB
500 | s=-0.45%, mm=17 MB, pm=17 MB | s=-0.19%, mm=59 MB,
pm=59 MB | s=-0.08%, mm=179 MB, pm=179 MB | s=-0.03%, mm=544 MB,
pm=544 MB
1000 | s=-0.43%, mm=35 MB, pm=35 MB | s=-0.17%, mm=128 MB,
pm=129 MB | s=-0.08%, mm=395 MB, pm=396 MB | s=-0.03%, mm=1234 MB,
pm=1234 MB
(5 rows)

The memory profile too is impressive. There's almost no impact on
memory consumption. The increase in memory consumption is acceptable
given the significant improvements in planning time.

I have not reviewed patches though.

I haven't measured if the patches improve performance of simple scans
with thousands of partitions. Have you tried measuring that?

--
Best Wishes,
Ashutosh Bapat

#120David Rowley
dgrowleyml@gmail.com
In reply to: Ashutosh Bapat (#119)
Re: [PoC] Reducing planning time when tables have many partitions

On Sat, 5 Apr 2025 at 02:54, Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

I haven't measured if the patches improve performance of simple scans
with thousands of partitions. Have you tried measuring that?

I just tried 10k partitions on my Zen4 laptop.

create table lp (a int) partition by list(a);
select 'create table lp'||x||' partition of lp for values
in('||x||');' from generate_Series(1,10000)x;
\gexec

create index on lp(a);
explain (summary on) select * from lp order by a;

master:

Planning Time: 2296.227 ms
Planning Time: 2142.999 ms
Planning Time: 2089.924 ms
Memory: used=84701kB allocated=85292kB

59.34% postgres [.] bms_is_subset
17.09% postgres [.] find_ec_member_matching_expr
11.55% postgres [.] bms_equal
3.41% postgres [.] get_eclass_for_sort_expr
2.08% postgres [.] add_child_rel_equivalences
0.59% postgres [.] SearchCatCacheInternal
0.52% postgres [.] hash_search_with_hash_value
0.45% libc.so.6 [.] __memmove_avx512_unaligned_erms
0.23% postgres [.] AllocSetAlloc
0.16% postgres [.] ResourceOwnerForget
0.13% postgres [.] add_paths_to_append_rel
0.12% postgres [.] RelationIdGetRelation
0.11% postgres [.] create_scan_plan
0.11% libc.so.6 [.] __memset_avx512_unaligned_erms
0.10% postgres [.] uint32_hash
0.10% libc.so.6 [.] __memcmp_evex_movbe
0.10% postgres [.] lappend

patched:

Planning Time: 118.346 ms
Planning Time: 122.706 ms
Planning Time: 120.424 ms
Memory: used=77677kB allocated=84752kB

9.58% postgres [.] hash_search_with_hash_value
7.58% libc.so.6 [.] __memmove_avx512_unaligned_erms
6.41% postgres [.] SearchCatCacheInternal
3.35% postgres [.] AllocSetAlloc
3.15% postgres [.] bms_next_member
2.79% postgres [.] ResourceOwnerForget
2.07% postgres [.] RelationIdGetRelation
1.86% libc.so.6 [.] __memcmp_evex_movbe
1.78% postgres [.] add_paths_to_append_rel
1.57% postgres [.] LockAcquireExtended
1.35% postgres [.] uint32_hash
1.29% libc.so.6 [.] __memset_avx512_unaligned_erms

David

#121David Rowley
dgrowleyml@gmail.com
In reply to: Amit Langote (#118)
2 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

Thank you for having a look at this.

On Fri, 4 Apr 2025 at 21:47, Amit Langote <amitlangote09@gmail.com> wrote:

It looks to me like the following hunks in 0002 probably belong in
0001, unless you’re planning to commit the patches together anyway:

Ah, yeah. Unsure about that as yet, but I've moved it over.

The comment on EquivalenceMember might benefit from a mention of how
ec_childmembers now fits into the picture -- do you think it’s worth
updating?

/*
* EquivalenceMember - one member expression of an EquivalenceClass
*
* em_is_child signifies that this element was built by transposing a member
* for an appendrel parent relation to represent the corresponding expression
* for an appendrel child.
...

I've adjusted that a bit in the attached.

+ /* XXX ec_childmembers? */

Maybe we don’t need to print these, since the comment on em_is_child
suggests they aren’t really full-fledged EC members and are meant to
be ignored by most operations?

It is marked with pg_node_attr no_read, so I guess that means the
writing is just for debugging since there's nothing else to read it.
In the attached I added a field for the array length and am calling
WRITE_NODE_ARRAY on it.

I spent more time going over all the usages of ec_members. A few
functions do something different to what they did before;

1) print_pathkeys() maybe this should also loop over all child members
too. However, it doesn't seem too important since those are just or
debugging.
2) in convert_subquery_pathkeys() there's some code doing "score =
list_length(outer_ec->ec_members) - 1;", I think this might have
become more correct now that the child members are not contributing to
the score.

I also added a series of Asserts in some places where child members
are not expected yet. analyzejoins.c is doing some fiddling with the
ec_members list, but that's always done before the children are added,
so the Assert is there to make sure that remains true. I didn't see
the sense in writing dead code to remove the child members. I'd feel
more inclined to do that if that code was in equivclass.c

I've attached the updated set of patches. I'm still uncertain what to
do about the EquivalenceMemberIterator returning duplicate members for
child join rels. I'll need to spend more time to see if this is an
actual problem.

David

Attachments:

v38-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchapplication/octet-stream; name=v38-0001-Add-the-PlannerInfo-context-to-the-parameter-of-.patchDownload
From c4c6ab328a86df079ef554df2d9db747815ba82d Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v38 1/2] Add the PlannerInfo context to the parameter of
 find_ec_member_matching_expr()

---
 src/backend/optimizer/path/equivclass.c |  4 +-
 src/backend/optimizer/path/indxpath.c   |  6 +--
 src/backend/optimizer/plan/createplan.c | 64 +++++++++++++++----------
 src/include/optimizer/paths.h           |  3 +-
 4 files changed, 45 insertions(+), 32 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9cd54c573a8..6eceaaae1c2 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -817,7 +817,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
  * Child EC members are ignored unless they belong to given 'relids'.
  */
 EquivalenceMember *
-find_ec_member_matching_expr(EquivalenceClass *ec,
+find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
@@ -996,7 +996,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel,
 	{
 		Expr	   *targetexpr = (Expr *) lfirst(lc);
 
-		em = find_ec_member_matching_expr(ec, targetexpr, rel->relids);
+		em = find_ec_member_matching_expr(root, ec, targetexpr, rel->relids);
 		if (!em)
 			continue;
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4cabb358abc..ca4f83f13a5 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -188,7 +188,7 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												IndexOptInfo *index,
 												Oid expr_op,
 												bool var_on_left);
-static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+static void match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
 									List **clause_columns_p);
 static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
@@ -932,7 +932,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		 * query_pathkeys will allow an incremental sort to be considered on
 		 * the index's partially sorted results.
 		 */
-		match_pathkeys_to_index(index, root->query_pathkeys,
+		match_pathkeys_to_index(root, index, root->query_pathkeys,
 								&orderbyclauses,
 								&orderbyclausecols);
 		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
@@ -3738,7 +3738,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
  * item in the given 'pathkeys' list.
  */
 static void
-match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p)
 {
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 359db4ba9dd..e67aea3a838 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -264,7 +264,9 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
 											 int numCols, int nPresortedCols,
 											 AttrNumber *sortColIdx, Oid *sortOperators,
 											 Oid *collations, bool *nullsFirst);
-static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+										Plan *lefttree,
+										List *pathkeys,
 										Relids relids,
 										const AttrNumber *reqColIdx,
 										bool adjust_tlist_in_place,
@@ -273,9 +275,11 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 										Oid **p_sortOperators,
 										Oid **p_collations,
 										bool **p_nullsFirst);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+static Sort *make_sort_from_pathkeys(PlannerInfo *root,
+									 Plan *lefttree,
+									 List *pathkeys,
 									 Relids relids);
-static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
+static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 														   List *pathkeys, Relids relids, int nPresortedCols);
 static Sort *make_sort_from_groupcols(List *groupcls,
 									  AttrNumber *grpColIdx,
@@ -294,7 +298,7 @@ static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
 						 Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
-static Unique *make_unique_from_pathkeys(Plan *lefttree,
+static Unique *make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 										 List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
 						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
@@ -1281,7 +1285,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		 * function result; it must be the same plan node.  However, we then
 		 * need to detect whether any tlist entries were added.
 		 */
-		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+		(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
 										  best_path->path.parent->relids,
 										  NULL,
 										  true,
@@ -1325,7 +1329,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 			 * don't need an explicit sort, to make sure they are returning
 			 * the same sort key columns the Append expects.
 			 */
-			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+			subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 												 subpath->parent->relids,
 												 nodeSortColIdx,
 												 false,
@@ -1466,7 +1470,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 	 * function result; it must be the same plan node.  However, we then need
 	 * to detect whether any tlist entries were added.
 	 */
-	(void) prepare_sort_from_pathkeys(plan, pathkeys,
+	(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
 									  best_path->path.parent->relids,
 									  NULL,
 									  true,
@@ -1497,7 +1501,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
 		/* Compute sort column info, and adjust subplan's tlist as needed */
-		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+		subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 											 subpath->parent->relids,
 											 node->sortColIdx,
 											 false,
@@ -1979,7 +1983,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
 	Assert(pathkeys != NIL);
 
 	/* Compute sort column info, and adjust subplan's tlist as needed */
-	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+	subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
 										 best_path->subpath->parent->relids,
 										 gm_plan->sortColIdx,
 										 false,
@@ -2193,7 +2197,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	 * relids. Thus, if this sort path is based on a child relation, we must
 	 * pass its relids.
 	 */
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+	plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
 								   IS_OTHER_REL(best_path->subpath->parent) ?
 								   best_path->path.parent->relids : NULL);
 
@@ -2217,7 +2221,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
 	/* See comments in create_sort_plan() above */
 	subplan = create_plan_recurse(root, best_path->spath.subpath,
 								  flags | CP_SMALL_TLIST);
-	plan = make_incrementalsort_from_pathkeys(subplan,
+	plan = make_incrementalsort_from_pathkeys(root, subplan,
 											  best_path->spath.path.pathkeys,
 											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
 											  best_path->spath.path.parent->relids : NULL,
@@ -2286,7 +2290,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_LABEL_TLIST);
 
-	plan = make_unique_from_pathkeys(subplan,
+	plan = make_unique_from_pathkeys(root, subplan,
 									 best_path->path.pathkeys,
 									 best_path->numkeys);
 
@@ -4544,7 +4548,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		if (!use_incremental_sort)
 		{
 			sort_plan = (Plan *)
-				make_sort_from_pathkeys(outer_plan,
+				make_sort_from_pathkeys(root,
+										outer_plan,
 										best_path->outersortkeys,
 										outer_relids);
 
@@ -4553,7 +4558,8 @@ create_mergejoin_plan(PlannerInfo *root,
 		else
 		{
 			sort_plan = (Plan *)
-				make_incrementalsort_from_pathkeys(outer_plan,
+				make_incrementalsort_from_pathkeys(root,
+												   outer_plan,
 												   best_path->outersortkeys,
 												   outer_relids,
 												   presorted_keys);
@@ -4578,7 +4584,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 */
 
 		Relids		inner_relids = inner_path->parent->relids;
-		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
+		Sort	   *sort = make_sort_from_pathkeys(root, inner_plan,
 												   best_path->innersortkeys,
 												   inner_relids);
 
@@ -6195,6 +6201,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the plan node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' identifies the child relation being sorted, if any
@@ -6228,7 +6235,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 						   Relids relids,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
@@ -6295,7 +6302,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
 			if (tle)
 			{
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr at right place in tlist */
@@ -6323,7 +6330,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			foreach(j, tlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, relids);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, relids);
 				if (em)
 				{
 					/* found expr already in tlist */
@@ -6339,7 +6346,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 			/*
 			 * No matching tlist item; look for a computable expression.
 			 */
-			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
+			em = find_computable_ec_member(root, ec, tlist, relids, false);
 			if (!em)
 				elog(ERROR, "could not find pathkey item to sort");
 			pk_datatype = em->em_datatype;
@@ -6405,12 +6412,14 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
+make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6419,7 +6428,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6439,14 +6448,16 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
  * make_incrementalsort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
+ *	  'root' is the PlannerInfo context
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  *	  'nPresortedCols' is the number of presorted columns in input tuples
  */
 static IncrementalSort *
-make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
-								   Relids relids, int nPresortedCols)
+make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
+								   List *pathkeys, Relids relids,
+								   int nPresortedCols)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
@@ -6455,7 +6466,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
-	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
+	lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
 										  relids,
 										  NULL,
 										  false,
@@ -6812,7 +6823,8 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
  * as above, but use pathkeys to identify the sort columns and semantics
  */
 static Unique *
-make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+make_unique_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+						  int numCols)
 {
 	Unique	   *node = makeNode(Unique);
 	Plan	   *plan = &node->plan;
@@ -6875,7 +6887,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 			foreach(j, plan->targetlist)
 			{
 				tle = (TargetEntry *) lfirst(j);
-				em = find_ec_member_matching_expr(ec, tle->expr, NULL);
+				em = find_ec_member_matching_expr(root, ec, tle->expr, NULL);
 				if (em)
 				{
 					/* found expr already in tlist */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b1a76816442..2a01389019f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -140,7 +140,8 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Index sortref,
 												  Relids rel,
 												  bool create_it);
-extern EquivalenceMember *find_ec_member_matching_expr(EquivalenceClass *ec,
+extern EquivalenceMember *find_ec_member_matching_expr(PlannerInfo *root,
+													   EquivalenceClass *ec,
 													   Expr *expr,
 													   Relids relids);
 extern EquivalenceMember *find_computable_ec_member(PlannerInfo *root,
-- 
2.43.0

v38-0002-Speed-up-searches-for-child-EquivalenceMembers.patchapplication/octet-stream; name=v38-0002-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
From 9da713a344fa1674994d5ef81f3bf2a6d60a6b0a Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 25 Aug 2023 10:43:36 +0900
Subject: [PATCH v38 2/2] Speed up searches for child EquivalenceMembers

Traditionally, child EquivalenceMembers were in the
EquivalenceClass->ec_members. When we wanted to find some child members
matching a request, we had to perform a linear search. This search
became heavy when tables had many partitions, leading to much planning
time.

After this commit, child EquivalenceMembers no longer exist in
ec_members. Instead, RelOptInfos have them. This change demonstrates a
significant performance improvement in planning time.
---
 contrib/postgres_fdw/postgres_fdw.c       |  19 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 385 ++++++++++++++++------
 src/backend/optimizer/path/indxpath.c     |   8 +-
 src/backend/optimizer/path/pathkeys.c     |  10 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +
 src/include/nodes/pathnodes.h             |  97 +++++-
 src/include/optimizer/paths.h             |   5 +
 src/tools/pgindent/typedefs.list          |   1 +
 9 files changed, 425 insertions(+), 116 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b4e0e60928b..b8dc0afa4cc 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7908,7 +7907,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
-		/* Locate an EquivalenceClass member matching this expr, if any */
+		/*
+		 * Locate an EquivalenceClass member matching this expr, if any.
+		 * Ignore child members.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
@@ -7918,9 +7920,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 557f06e344f..ceac3fd8620 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,7 +465,9 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
+	WRITE_INT_FIELD(ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_sources);
 	/* Only ec_derives_list is written; hash is not serialized. */
 	WRITE_NODE_FIELD(ec_derives_list);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 6eceaaae1c2..fc34fabe2bc 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,11 +34,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Relids child_relids);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -314,11 +326,15 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		foreach(lc2, cur_ec->ec_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -428,7 +444,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -445,7 +461,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -465,7 +481,9 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
+		ec->ec_childmembers_size = 0;
 		ec->ec_members = NIL;
+		ec->ec_childmembers = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives_list = NIL;
 		ec->ec_derives_hash = NULL;
@@ -478,9 +496,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -566,11 +584,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *		Build a new EquivalenceMember without adding it to an EC.  If 'parent'
+ *		is NULL, the result will be a parent member, otherwise a child member.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -597,11 +617,84 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	/* add to the members list */
+	ec->ec_members = lappend(ec->ec_members, em);
+
+	/* record the relids for parent members */
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ *
+ * 'root' the PlannerInfo that 'ec' belongs to.
+ * 'ec' the EquivalenceClass to add the child member to.
+ * 'ec_index' the index of 'ec' within root->eq_classes, or -1 if maintaining
+ * the RelOptInfo.eclass_indexes isn't needed.
+ * 'expr' the em_expr for the new member.
+ * 'relids' the 'em_relids' for the new member.
+ * 'jdomain' the 'em_jdomain' for the new member.
+ * 'parent_em' the parent member of the child to create.
+ * 'datatype' the em_datatype of the new member.
+ * 'child_relids' defines which elements of ec_childmembers to add this member
+ * to.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Relids child_relids)
+{
+	EquivalenceMember *em;
+	int			relid;
+
+	Assert(parent_em != NULL);
+
+	/*
+	 * Allocate member to store children.  An array of Lists indexed by relid.
+	 */
+	if (ec->ec_childmembers == NULL)
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_childmembers = (List **) palloc0(root->simple_rel_array_size *
+												sizeof(List *));
+		ec->ec_childmembers_size = root->simple_rel_array_size;
+	}
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	/* Record this member in the ec_childmembers Lists for each relid */
+	relid = -1;
+	while ((relid = bms_next_member(child_relids, relid)) >= 0)
+	{
+
+		ec->ec_childmembers[relid] = lappend(ec->ec_childmembers[relid], em);
+
+		/* Record this EC index for the child rel */
+		if (ec_index >= 0)
+		{
+			RelOptInfo *child_rel = root->simple_rel_array[relid];
+
+			child_rel->eclass_indexes =
+				bms_add_member(child_rel->eclass_indexes, ec_index);
+		}
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -672,7 +765,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -687,10 +781,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(root, &it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -725,7 +818,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
+	newec->ec_childmembers_size = 0;
 	newec->ec_members = NIL;
+	newec->ec_childmembers = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives_list = NIL;
 	newec->ec_derives_hash = NULL;
@@ -747,7 +842,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -821,15 +916,16 @@ find_ec_member_matching_expr(PlannerInfo *root, EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -898,7 +994,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -912,9 +1009,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -1193,6 +1290,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		return;
 	}
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Find the constant member to use.  We prefer an actual constant to
 	 * pseudo-constants (such as Params), because the constraint exclusion
@@ -1219,7 +1319,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1283,12 +1384,16 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach(lc, ec->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1621,7 +1726,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1632,10 +1738,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(root, &it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1668,6 +1773,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1742,6 +1848,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1749,7 +1856,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2189,6 +2296,9 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		bool		match;
 		ListCell   *lc2;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2206,7 +2316,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2304,6 +2415,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		ListCell   *lc2;
 		int			coal_idx = -1;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2333,7 +2447,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2462,6 +2577,9 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
 
+		/* We don't expect any children yet */
+		Assert(ec->ec_childmembers == NULL);
+
 		/* Need do anything only for a multi-member, no-const EC. */
 		if (list_length(ec->ec_members) > 1 && !ec->ec_has_const)
 		{
@@ -2547,12 +2665,13 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 			!list_member_oid(ec->ec_opfamilies, opfamily))
 			continue;
 
+		/* Ignore children here */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2616,15 +2735,18 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
-		/* It's okay to consider "broken" ECs here, see exprs_known_equal */
 
+		/*
+		 * It's okay to consider "broken" ECs here, see exprs_known_equal.
+		 * Ignore children here.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2710,6 +2832,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2722,7 +2845,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2735,29 +2857,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2802,12 +2910,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relids);
 			}
 		}
 	}
@@ -2854,7 +2965,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2867,25 +2978,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2930,9 +3031,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_joinrel->relids);
 			}
 		}
 	}
@@ -2979,14 +3086,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relids);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3001,6 +3112,85 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members belonging to the given 'ec'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec_members for 'ec', and;
+ *	- Any child member added to the given ec by add_child_eq_member() where
+ *	  the child_relids specified in the add_child_eq_member() overlap with
+ *	  the child_relids in the setup_eclass_member_iterator() call.
+ *
+ * Note:
+ *	- The given 'child_relids' must remain allocated and not be changed for
+ *	  the lifetime of the iterator.
+ *
+ * Parameters:
+ *	root - The PlannerInfo context.
+ *	it - A pointer to the iterator to set up.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	child_relids - The relids to return child members for.
+ */
+void
+setup_eclass_member_iterator(PlannerInfo *root, EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids child_relids)
+{
+	it->root = root;
+	it->ec = ec;
+	/* no need to set this if the class has no child members */
+	it->child_relids = ec->ec_childmembers ? child_relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator(). NULL is
+ *	  returned if there are no members left, in which case callers must not
+ *	  call eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	EquivalenceMember *em = NULL;
+
+	while (it->current_list != NULL)
+	{
+nextcell:
+		while (it->current_cell != NULL)
+		{
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			goto end;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->child_relids, it->current_relid)) > 0)
+		{
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/*
+			 * If there are members in this list, use it, this will exclude
+			 * RELOPT_BASERELs as ec_childmembers[] are not populated for
+			 * those.
+			 */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+		goto end;
+	}
+
+end:
+	return em;
+}
 
 /*
  * generate_implied_equalities_for_column
@@ -3053,6 +3243,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
 
@@ -3076,14 +3267,12 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(root, &it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
 
 		if (!cur_em)
@@ -3091,7 +3280,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 
 		/*
 		 * Found our match.  Scan the other EC members and attempt to generate
-		 * joinclauses.
+		 * joinclauses.  Ignore children here.
 		 */
 		foreach(lc2, cur_ec->ec_members)
 		{
@@ -3099,8 +3288,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3313,13 +3502,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
+	/*
+	 * To join, we need a member not in the given rel.  Ignore children here.
+	 */
 	foreach(lc, eclass->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index ca4f83f13a5..25eb490b75e 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3755,7 +3755,8 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3774,9 +3775,10 @@ match_pathkeys_to_index(PlannerInfo *root, IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(root, &it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 6fac08cb0d9..4e8923e383e 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1143,6 +1143,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			int			best_score = -1;
 			ListCell   *j;
 
+			/* Ignore children here */
 			foreach(j, sub_eclass->ec_members)
 			{
 				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
@@ -1151,8 +1152,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1710,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index ae20691ca91..c9f3b7f08ef 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -710,6 +710,13 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 		ec->ec_relids = adjust_relid_set(ec->ec_relids,
 										 sjinfo->ojrelid, subst);
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Fix up the member expressions.  Any non-const member that ends with
 	 * empty em_relids must be a Var or PHV of the removed relation.  We don't
@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
 		bool		is_redundant = false;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 4c466f76778..b2c658df8a2 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1414,6 +1414,22 @@ typedef struct JoinDomain
  * In contrast, ec_sources holds equality clauses that appear directly in the
  * query. These are typically few and do not require a hash table for lookup.
  *
+ * 'ec_members' is a List of all EquivalenceMembers belonging to
+ * RELOPT_BASERELs.  EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and
+ * RELOPT_OTHER_JOINREL relations are stored in the 'ec_childmembers' array in
+ * the index corresponding to the relid.  'ec_childmembers' may be NULL if the
+ * class has no child EquivalenceMembers.
+ *
+ * For code wishing to look at EquivalenceMembers, if only parent-level
+ * members are needed, then a simple foreach loop over ec_members is
+ * sufficient.  When child members are also required, it is best to use the
+ * functionality provided by EquivalenceMemberIterator.  The reason for this
+ * is because large numbers of child EquivalenceMembers can exist in queries
+ * to partitioned tables with many partitions.  The functionality provided by
+ * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
+ * which belong to specific child relids.  See the header comments for
+ * EquivalenceMemberIterator below for further details.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1431,7 +1447,10 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
+	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	List	  **ec_childmembers;	/* array of Lists of child
+									 * EquivalenceMembers */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1465,12 +1484,17 @@ typedef struct EquivalenceClass
  * child when necessary to build a MergeAppend path for the whole appendrel
  * tree.  An em_is_child member has no impact on the properties of the EC as a
  * whole; in particular the EC's ec_relids field does NOT include the child
- * relation.  An em_is_child member should never be marked em_is_const nor
- * cause ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
+ * relation.  em_is_child members aren't stored in the ec_members List of the
+ * EC and instead they're stored and indexed by the relids of the child
+ * relation(s) they represent equivalence for in ec_childmembers.  An
+ * em_is_child member should never be marked em_is_const nor cause
+ * ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
  * members are not really full-fledged members of the EC, but just reflections
  * or doppelgangers of real members.  Most operations on EquivalenceClasses
- * should ignore em_is_child members, and those that don't should test
- * em_relids to make sure they only consider relevant members.
+ * should ignore em_is_child members by only inspecting members in the
+ * ec_members list.  Callers that require inspecting child members should do
+ * so using an EquivalenceMemberIterator and should test em_relids to make
+ * sure they only consider relevant members.
  *
  * em_datatype is usually the same as exprType(em_expr), but can be
  * different when dealing with a binary-compatible opfamily; in particular
@@ -1493,6 +1517,71 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator allows efficient access to sets of
+ * EquivalenceMembers for callers which require access to child members.
+ * Because partitioning workloads can result in large numbers of child
+ * members, the child members are not stored in the EquivalenceClass's
+ * ec_members List.  Instead, these are stored in the EquivalenceClass's
+ * ec_childmembers array of Lists.  The functionality provided by
+ * EquivalenceMemberIterator aims to provide efficient access to parent
+ * members and child members belonging to specific child relids.
+ *
+ * Currently, there is only one way to initialize and iterate over an
+ * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
+ * and eclass_member_iterator_next functions.  The iterator object is
+ * generally a local variable which is passed by address to
+ * setup_eclass_member_iterator.  The calling function defines which
+ * EquivalenceClass the iterator should be looking at and which child
+ * relids to also include the members for.  child_relids can be passed as NULL
+ * but the caller may as well just perform a foreach loop over ec_members as
+ * only parent-level members will be returned in that case.
+ *
+ * When calling the next function on an EquivalenceMemberIterator, all
+ * parent-level EquivalenceMembers are returned first, followed by any
+ * child members for the relids specified by the child_relids parameter as
+ * specified when calling setup_eclass_member_iterator.  The child members
+ * returned are members which have any of the relids mentioned in
+ * child_relids.  That's not to be confused with returning members which
+ * contain *all* of the child relids specified when calling
+ * setup_eclass_member_iterator.  It is up to the calling function to ensure
+ * that the returned member matches what is required for the purpose.
+ *
+ * It is also important to note that when dealing with child
+ * EquivalenceMembers for RELOPT_OTHER_JOINRELs that it's possible for the
+ * same EquivalenceMembers to be returned more than once by the next function.
+ * This is currently not seen to be a problem, but some callers may want to be
+ * aware of it.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(root, &it, ec, child_relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *		...
+ * }
+ * -----
+ * It is not valid to call eclass_member_iterator_next() after it has returned
+ * NULL for any given EquivalenceMemberIterator.
+ */
+typedef struct
+{
+	PlannerInfo *root;			/* The PlannerInfo where 'ec' belongs */
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		child_relids;	/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 2a01389019f..e849a423cb3 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -184,6 +184,11 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(PlannerInfo *root,
+										 EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids child_relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index c3f05796a7c..1513b247d9d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -711,6 +711,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.43.0

#122Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#121)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

David Rowley <dgrowleyml@gmail.com> writes:

I've attached the updated set of patches.

This patchset has a distinct whiff of unseemly haste.

1. The commit message for 0002 still claims that child EC members
are kept in RelOptInfos, precisely the point I objected to upthread.
I see that in fact that's untrue, but it'd be nice if the commit log
had some connection to what's being committed.

2. Because there is no longer any need to find RelOptInfos, the
EquivalenceMemberIterator stuff doesn't need a "root" pointer,
either in the struct or as an setup_eclass_member_iterator argument.

3. Because of #2, the 0001 patch is useless code churn and should
be dropped.

See attached (just a hasty root-ectomy, I've not really read much
else).

I do note that add_child_eq_member seems to have a considerable
amount of faith that root->simple_rel_array_size can't increase
after we start adding child members. That seems rather unsafe,
though the fact that it hasn't crashed in light testing suggests
that maybe there's something I'm missing. I would be much
happier if there were provision to expand the array at need.

regards, tom lane

Attachments:

v39-Speed-up-searches-for-child-EquivalenceMembers.patchtext/x-diff; charset=us-ascii; name=v39-Speed-up-searches-for-child-EquivalenceMembers.patchDownload
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b4e0e60928b..a7e0cc9f323 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7908,7 +7907,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
-		/* Locate an EquivalenceClass member matching this expr, if any */
+		/*
+		 * Locate an EquivalenceClass member matching this expr, if any.
+		 * Ignore child members.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
@@ -7918,9 +7920,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 557f06e344f..ceac3fd8620 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,7 +465,9 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
+	WRITE_INT_FIELD(ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_sources);
 	/* Only ec_derives_list is written; hash is not serialized. */
 	WRITE_NODE_FIELD(ec_derives_list);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9cd54c573a8..089f196c958 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,11 +34,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Relids child_relids);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -314,11 +326,15 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		foreach(lc2, cur_ec->ec_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -428,7 +444,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -445,7 +461,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -465,7 +481,9 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
+		ec->ec_childmembers_size = 0;
 		ec->ec_members = NIL;
+		ec->ec_childmembers = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives_list = NIL;
 		ec->ec_derives_hash = NULL;
@@ -478,9 +496,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -566,11 +584,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *		Build a new EquivalenceMember without adding it to an EC.  If 'parent'
+ *		is NULL, the result will be a parent member, otherwise a child member.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -597,11 +617,84 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	/* add to the members list */
+	ec->ec_members = lappend(ec->ec_members, em);
+
+	/* record the relids for parent members */
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ *
+ * 'root' the PlannerInfo that 'ec' belongs to.
+ * 'ec' the EquivalenceClass to add the child member to.
+ * 'ec_index' the index of 'ec' within root->eq_classes, or -1 if maintaining
+ * the RelOptInfo.eclass_indexes isn't needed.
+ * 'expr' the em_expr for the new member.
+ * 'relids' the 'em_relids' for the new member.
+ * 'jdomain' the 'em_jdomain' for the new member.
+ * 'parent_em' the parent member of the child to create.
+ * 'datatype' the em_datatype of the new member.
+ * 'child_relids' defines which elements of ec_childmembers to add this member
+ * to.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Relids child_relids)
+{
+	EquivalenceMember *em;
+	int			relid;
+
+	Assert(parent_em != NULL);
+
+	/*
+	 * Allocate member to store children.  An array of Lists indexed by relid.
+	 */
+	if (ec->ec_childmembers == NULL)
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		ec->ec_childmembers = (List **) palloc0(root->simple_rel_array_size *
+												sizeof(List *));
+		ec->ec_childmembers_size = root->simple_rel_array_size;
+	}
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	/* Record this member in the ec_childmembers Lists for each relid */
+	relid = -1;
+	while ((relid = bms_next_member(child_relids, relid)) >= 0)
+	{
+
+		ec->ec_childmembers[relid] = lappend(ec->ec_childmembers[relid], em);
+
+		/* Record this EC index for the child rel */
+		if (ec_index >= 0)
+		{
+			RelOptInfo *child_rel = root->simple_rel_array[relid];
+
+			child_rel->eclass_indexes =
+				bms_add_member(child_rel->eclass_indexes, ec_index);
+		}
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -672,7 +765,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -687,10 +781,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -725,7 +818,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
+	newec->ec_childmembers_size = 0;
 	newec->ec_members = NIL;
+	newec->ec_childmembers = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives_list = NIL;
 	newec->ec_derives_hash = NULL;
@@ -747,7 +842,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -821,15 +916,16 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -898,7 +994,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -912,9 +1009,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -1193,6 +1290,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		return;
 	}
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Find the constant member to use.  We prefer an actual constant to
 	 * pseudo-constants (such as Params), because the constraint exclusion
@@ -1219,7 +1319,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1283,12 +1384,16 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach(lc, ec->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1621,7 +1726,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1632,10 +1738,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1668,6 +1773,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1742,6 +1848,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1749,7 +1856,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2189,6 +2296,9 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		bool		match;
 		ListCell   *lc2;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2206,7 +2316,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2304,6 +2415,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		ListCell   *lc2;
 		int			coal_idx = -1;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2333,7 +2447,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2462,6 +2577,9 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
 
+		/* We don't expect any children yet */
+		Assert(ec->ec_childmembers == NULL);
+
 		/* Need do anything only for a multi-member, no-const EC. */
 		if (list_length(ec->ec_members) > 1 && !ec->ec_has_const)
 		{
@@ -2547,12 +2665,13 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 			!list_member_oid(ec->ec_opfamilies, opfamily))
 			continue;
 
+		/* Ignore children here */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2616,15 +2735,18 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
-		/* It's okay to consider "broken" ECs here, see exprs_known_equal */
 
+		/*
+		 * It's okay to consider "broken" ECs here, see exprs_known_equal.
+		 * Ignore children here.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2710,6 +2832,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2722,7 +2845,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2735,29 +2857,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2802,12 +2910,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relids);
 			}
 		}
 	}
@@ -2854,7 +2965,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2867,25 +2978,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2930,9 +3031,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_joinrel->relids);
 			}
 		}
 	}
@@ -2979,14 +3086,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relids);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3001,6 +3112,83 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members belonging to the given 'ec'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec_members for 'ec', and;
+ *	- Any child member added to the given ec by add_child_eq_member() where
+ *	  the child_relids specified in the add_child_eq_member() overlap with
+ *	  the child_relids in the setup_eclass_member_iterator() call.
+ *
+ * Note:
+ *	- The given 'child_relids' must remain allocated and not be changed for
+ *	  the lifetime of the iterator.
+ *
+ * Parameters:
+ *	it - A pointer to the iterator to set up.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	child_relids - The relids to return child members for.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids child_relids)
+{
+	it->ec = ec;
+	/* no need to set this if the class has no child members */
+	it->child_relids = ec->ec_childmembers ? child_relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator(). NULL is
+ *	  returned if there are no members left, in which case callers must not
+ *	  call eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	EquivalenceMember *em = NULL;
+
+	while (it->current_list != NULL)
+	{
+nextcell:
+		while (it->current_cell != NULL)
+		{
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			goto end;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->child_relids, it->current_relid)) > 0)
+		{
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/*
+			 * If there are members in this list, use it, this will exclude
+			 * RELOPT_BASERELs as ec_childmembers[] are not populated for
+			 * those.
+			 */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+		goto end;
+	}
+
+end:
+	return em;
+}
 
 /*
  * generate_implied_equalities_for_column
@@ -3053,6 +3241,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
 
@@ -3076,14 +3265,12 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
 
 		if (!cur_em)
@@ -3091,7 +3278,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 
 		/*
 		 * Found our match.  Scan the other EC members and attempt to generate
-		 * joinclauses.
+		 * joinclauses.  Ignore children here.
 		 */
 		foreach(lc2, cur_ec->ec_members)
 		{
@@ -3099,8 +3286,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3313,13 +3500,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
+	/*
+	 * To join, we need a member not in the given rel.  Ignore children here.
+	 */
 	foreach(lc, eclass->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4cabb358abc..601354ea3e0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3755,7 +3755,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3774,9 +3775,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(&it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 6fac08cb0d9..4e8923e383e 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1143,6 +1143,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			int			best_score = -1;
 			ListCell   *j;
 
+			/* Ignore children here */
 			foreach(j, sub_eclass->ec_members)
 			{
 				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
@@ -1151,8 +1152,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1710,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index ae20691ca91..c9f3b7f08ef 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -710,6 +710,13 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 		ec->ec_relids = adjust_relid_set(ec->ec_relids,
 										 sjinfo->ojrelid, subst);
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Fix up the member expressions.  Any non-const member that ends with
 	 * empty em_relids must be a Var or PHV of the removed relation.  We don't
@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
 		bool		is_redundant = false;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 4c466f76778..59ddafca1cf 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1414,6 +1414,22 @@ typedef struct JoinDomain
  * In contrast, ec_sources holds equality clauses that appear directly in the
  * query. These are typically few and do not require a hash table for lookup.
  *
+ * 'ec_members' is a List of all EquivalenceMembers belonging to
+ * RELOPT_BASERELs.  EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and
+ * RELOPT_OTHER_JOINREL relations are stored in the 'ec_childmembers' array in
+ * the index corresponding to the relid.  'ec_childmembers' may be NULL if the
+ * class has no child EquivalenceMembers.
+ *
+ * For code wishing to look at EquivalenceMembers, if only parent-level
+ * members are needed, then a simple foreach loop over ec_members is
+ * sufficient.  When child members are also required, it is best to use the
+ * functionality provided by EquivalenceMemberIterator.  The reason for this
+ * is because large numbers of child EquivalenceMembers can exist in queries
+ * to partitioned tables with many partitions.  The functionality provided by
+ * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
+ * which belong to specific child relids.  See the header comments for
+ * EquivalenceMemberIterator below for further details.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1431,7 +1447,10 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
+	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	List	  **ec_childmembers;	/* array of Lists of child
+									 * EquivalenceMembers */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1465,12 +1484,17 @@ typedef struct EquivalenceClass
  * child when necessary to build a MergeAppend path for the whole appendrel
  * tree.  An em_is_child member has no impact on the properties of the EC as a
  * whole; in particular the EC's ec_relids field does NOT include the child
- * relation.  An em_is_child member should never be marked em_is_const nor
- * cause ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
+ * relation.  em_is_child members aren't stored in the ec_members List of the
+ * EC and instead they're stored and indexed by the relids of the child
+ * relation(s) they represent equivalence for in ec_childmembers.  An
+ * em_is_child member should never be marked em_is_const nor cause
+ * ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
  * members are not really full-fledged members of the EC, but just reflections
  * or doppelgangers of real members.  Most operations on EquivalenceClasses
- * should ignore em_is_child members, and those that don't should test
- * em_relids to make sure they only consider relevant members.
+ * should ignore em_is_child members by only inspecting members in the
+ * ec_members list.  Callers that require inspecting child members should do
+ * so using an EquivalenceMemberIterator and should test em_relids to make
+ * sure they only consider relevant members.
  *
  * em_datatype is usually the same as exprType(em_expr), but can be
  * different when dealing with a binary-compatible opfamily; in particular
@@ -1493,6 +1517,70 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator allows efficient access to sets of
+ * EquivalenceMembers for callers which require access to child members.
+ * Because partitioning workloads can result in large numbers of child
+ * members, the child members are not stored in the EquivalenceClass's
+ * ec_members List.  Instead, these are stored in the EquivalenceClass's
+ * ec_childmembers array of Lists.  The functionality provided by
+ * EquivalenceMemberIterator aims to provide efficient access to parent
+ * members and child members belonging to specific child relids.
+ *
+ * Currently, there is only one way to initialize and iterate over an
+ * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
+ * and eclass_member_iterator_next functions.  The iterator object is
+ * generally a local variable which is passed by address to
+ * setup_eclass_member_iterator.  The calling function defines which
+ * EquivalenceClass the iterator should be looking at and which child
+ * relids to also include the members for.  child_relids can be passed as NULL
+ * but the caller may as well just perform a foreach loop over ec_members as
+ * only parent-level members will be returned in that case.
+ *
+ * When calling the next function on an EquivalenceMemberIterator, all
+ * parent-level EquivalenceMembers are returned first, followed by any
+ * child members for the relids specified by the child_relids parameter as
+ * specified when calling setup_eclass_member_iterator.  The child members
+ * returned are members which have any of the relids mentioned in
+ * child_relids.  That's not to be confused with returning members which
+ * contain *all* of the child relids specified when calling
+ * setup_eclass_member_iterator.  It is up to the calling function to ensure
+ * that the returned member matches what is required for the purpose.
+ *
+ * It is also important to note that when dealing with child
+ * EquivalenceMembers for RELOPT_OTHER_JOINRELs that it's possible for the
+ * same EquivalenceMembers to be returned more than once by the next function.
+ * This is currently not seen to be a problem, but some callers may want to be
+ * aware of it.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(&it, ec, child_relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *		...
+ * }
+ * -----
+ * It is not valid to call eclass_member_iterator_next() after it has returned
+ * NULL for any given EquivalenceMemberIterator.
+ */
+typedef struct
+{
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		child_relids;	/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b1a76816442..a48c9721797 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,10 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids child_relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index c3f05796a7c..1513b247d9d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -711,6 +711,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
#123David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#122)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Sat, 5 Apr 2025 at 04:05, Tom Lane <tgl@sss.pgh.pa.us> wrote:

This patchset has a distinct whiff of unseemly haste.

hmm, yes. I would like to give this patch as good a chance at making
v18 as I can, and I admit to having optimised for that. Seemingly,
we've got a few other good partitioning performance patches in v18,
and more workloads are now bottlenecked on what this patch aims to fix
than ever before. What I'm aiming to avoid here is tuning those
optimisations to cloud my judgment on the quality of the patch. So,
I'm happy to have your 2nd opinion here.

1. The commit message for 0002 still claims that child EC members
are kept in RelOptInfos, precisely the point I objected to upthread.
I see that in fact that's untrue, but it'd be nice if the commit log
had some connection to what's being committed.

Now adjusted.

2. Because there is no longer any need to find RelOptInfos, the
EquivalenceMemberIterator stuff doesn't need a "root" pointer,
either in the struct or as an setup_eclass_member_iterator argument.

3. Because of #2, the 0001 patch is useless code churn and should
be dropped.

I'm glad that's not needed now. Thanks for noticing. Fixed.

I do note that add_child_eq_member seems to have a considerable
amount of faith that root->simple_rel_array_size can't increase
after we start adding child members. That seems rather unsafe,
though the fact that it hasn't crashed in light testing suggests
that maybe there's something I'm missing. I would be much
happier if there were provision to expand the array at need.

I think it's probably worth making that safer.
add_child_rel_equivalences() is currently called after
add_other_rels_to_query(). It is a similar story in the union planner
for add_setop_child_rel_equivalences(), but that's likely no reason to
not be a bit more cautious.

I am still thinking about the duplicate members being returned from
the iterator for child join rels due to them being duplicated into
each component relid element in ec_childmembers. I did consider if
these could just not be duplicated and instead just put into the
ec_childmember element according to their lowest component relid. For
that to work, all callers that need these would need to ensure they
never pass some subset of child_relids when setting up the
EquivalenceMemberIterator. I need to study a bit more to understand if
that's doable.

In the meantime, I've attached v40 with a rewritten commit message, a
bit more adjustment to comments and a slightly revised version of
eclass_member_iterator_next() to get rid of some gotos and hopefully
make it easier to follow the logic.

Thank you for looking.

David

Attachments:

v40-0001-Speedup-child-EquivalenceMember-lookup-in-planne.patchapplication/octet-stream; name=v40-0001-Speedup-child-EquivalenceMember-lookup-in-planne.patchDownload
From d2ef887aa90d0fb3e64be6d317acda0dd4043e68 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v40] Speedup child EquivalenceMember lookup in planner

When planning queries to partitioned tables, we create child
EquivalenceMembers for each EquivalenceMembers which mentions the parent
partitioned table.  For partitioned tables with large numbers of
partitions, this meant we could end up with many EquivalenceMembers and
this could cause the planner to become slow due to the linear lookups
that are performed on the EquivalenceClass ec_members's List.
Effectively, the more partitions which were present, the more lookups
needed to be performed for operations such as
find_ec_member_matching_expr() during create_plan() and the more
partitions present, the longer these searches would take.

To fix this, here we adjust how we store EquivalenceMembers for
em_is_child members.  Instead of storing these directly in ec_members,
these are now stored in a new array of Lists in the EquivalenceClass
which is indexed by the relid.  When we want to find EquivalenceMembers
belonging to a certain child relation, we can narrow the search to the
array element for that relation.

To make EquivalenceMember lookup easier and to reduce the amount of code
change, this commit provides a pair of functions to allow iteration over
the EquivalenceMembers in a class which handles finding the child
members, if required.  Callers that never need to look at child members
can remain using the foreach loop over ec_members, which will now often
be faster due to only parent-level members being stored there.

The actual performance increases here are highly dependent on the number
of partitions and the query being planned.  Performance increase can be
visible with as few as 8 partitions, but the speedup is marginal for
such low numbers of partitions.  The speedups become much more visible
with a few dozen to hundreds of partitions.  Some tested queries were
around 3x faster with 256 partitions.  For users with thousands of
partitions, these are likely to become significantly faster.  Some
testing has shown planner speedups of 20x or more.

Author: Yuya Watari <watari.yuya@gmail.com>
Co-authored-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andrey Lepikhov <a.lepikhov@postgrespro.ru>
Reviewed-by: Alena Rybakina <lena.ribackina@yandex.ru>
Reviewed-by: Dmitry Dolgov <9erthalion6@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Tested-by: Thom Brown <thom@linux.com>
Tested-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Tested-by: newtglobal postgresql_contributors <postgresql_contributors@newtglobalcorp.com>
Discussion: https://postgr.es/m/CAJ2pMkZNCgoUKSE%2B_5LthD%2BKbXKvq6h2hQN8Esxpxd%2Bcxmgomg%40mail.gmail.com
---
 contrib/postgres_fdw/postgres_fdw.c       |  19 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 399 ++++++++++++++++------
 src/backend/optimizer/path/indxpath.c     |   8 +-
 src/backend/optimizer/path/pathkeys.c     |  10 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +
 src/include/nodes/pathnodes.h             |  99 +++++-
 src/include/optimizer/paths.h             |   4 +
 src/tools/pgindent/typedefs.list          |   1 +
 9 files changed, 438 insertions(+), 118 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b4e0e60928b..a7e0cc9f323 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7908,7 +7907,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
-		/* Locate an EquivalenceClass member matching this expr, if any */
+		/*
+		 * Locate an EquivalenceClass member matching this expr, if any.
+		 * Ignore child members.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
@@ -7918,9 +7920,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 557f06e344f..ceac3fd8620 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,7 +465,9 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
+	WRITE_INT_FIELD(ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_sources);
 	/* Only ec_derives_list is written; hash is not serialized. */
 	WRITE_NODE_FIELD(ec_derives_list);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 9cd54c573a8..8805f0e02c4 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,11 +34,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Relids child_relids);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -314,11 +326,15 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		foreach(lc2, cur_ec->ec_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -428,7 +444,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -445,7 +461,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -465,7 +481,9 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
+		ec->ec_childmembers_size = 0;
 		ec->ec_members = NIL;
+		ec->ec_childmembers = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives_list = NIL;
 		ec->ec_derives_hash = NULL;
@@ -478,9 +496,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -566,11 +584,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *		Build a new EquivalenceMember without adding it to an EC.  If 'parent'
+ *		is NULL, the result will be a parent member, otherwise a child member.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -597,11 +617,94 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	/* add to the members list */
+	ec->ec_members = lappend(ec->ec_members, em);
+
+	/* record the relids for parent members */
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ *
+ * 'root' the PlannerInfo that 'ec' belongs to.
+ * 'ec' the EquivalenceClass to add the child member to.
+ * 'ec_index' the index of 'ec' within root->eq_classes, or -1 if maintaining
+ * the RelOptInfo.eclass_indexes isn't needed.
+ * 'expr' the em_expr for the new member.
+ * 'relids' the 'em_relids' for the new member.
+ * 'jdomain' the 'em_jdomain' for the new member.
+ * 'parent_em' the parent member of the child to create.
+ * 'datatype' the em_datatype of the new member.
+ * 'child_relids' defines which elements of ec_childmembers to add this member
+ * to.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Relids child_relids)
+{
+	EquivalenceMember *em;
+	int			relid;
+
+	Assert(parent_em != NULL);
+
+	/*
+	 * Allocate member to store children.  An array of Lists indexed by relid,
+	 * or expand the existing one, if necessary.
+	 */
+	if (unlikely(ec->ec_childmembers_size < root->simple_rel_array_size))
+	{
+		if (ec->ec_childmembers == NULL)
+		{
+			ec->ec_childmembers = palloc0_array(List *, root->simple_rel_array_size);
+			ec->ec_childmembers_size = root->simple_rel_array_size;
+		}
+		else
+		{
+			ec->ec_childmembers = repalloc0_array(ec->ec_childmembers, List *,
+												  ec->ec_childmembers_size,
+												  root->simple_rel_array_size);
+			ec->ec_childmembers_size = root->simple_rel_array_size;
+		}
+	}
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	/* Record this member in the ec_childmembers Lists for each relid */
+	relid = -1;
+	while ((relid = bms_next_member(child_relids, relid)) >= 0)
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+		ec->ec_childmembers[relid] = lappend(ec->ec_childmembers[relid], em);
+
+		/* Record this EC index for the child rel */
+		if (ec_index >= 0)
+		{
+			RelOptInfo *child_rel = root->simple_rel_array[relid];
+
+			child_rel->eclass_indexes =
+				bms_add_member(child_rel->eclass_indexes, ec_index);
+		}
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -672,7 +775,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -687,10 +791,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -725,7 +828,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
+	newec->ec_childmembers_size = 0;
 	newec->ec_members = NIL;
+	newec->ec_childmembers = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives_list = NIL;
 	newec->ec_derives_hash = NULL;
@@ -747,7 +852,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -821,15 +926,16 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -898,7 +1004,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -912,9 +1019,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -1193,6 +1300,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		return;
 	}
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Find the constant member to use.  We prefer an actual constant to
 	 * pseudo-constants (such as Params), because the constraint exclusion
@@ -1219,7 +1329,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1283,12 +1394,16 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach(lc, ec->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1621,7 +1736,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1632,10 +1748,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1668,6 +1783,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1742,6 +1858,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1749,7 +1866,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2189,6 +2306,9 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		bool		match;
 		ListCell   *lc2;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2206,7 +2326,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2304,6 +2425,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		ListCell   *lc2;
 		int			coal_idx = -1;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2333,7 +2457,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2462,6 +2587,9 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
 
+		/* We don't expect any children yet */
+		Assert(ec->ec_childmembers == NULL);
+
 		/* Need do anything only for a multi-member, no-const EC. */
 		if (list_length(ec->ec_members) > 1 && !ec->ec_has_const)
 		{
@@ -2547,12 +2675,13 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 			!list_member_oid(ec->ec_opfamilies, opfamily))
 			continue;
 
+		/* Ignore children here */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2616,15 +2745,18 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
-		/* It's okay to consider "broken" ECs here, see exprs_known_equal */
 
+		/*
+		 * It's okay to consider "broken" ECs here, see exprs_known_equal.
+		 * Ignore children here.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2710,6 +2842,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2722,7 +2855,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2735,29 +2867,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2802,12 +2920,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relids);
 			}
 		}
 	}
@@ -2854,7 +2975,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2867,25 +2988,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2930,9 +3041,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_joinrel->relids);
 			}
 		}
 	}
@@ -2979,14 +3096,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relids);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3001,6 +3122,89 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members belonging to the given 'ec'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec_members for 'ec', and;
+ *	- Any child member added to the given ec by add_child_eq_member() where
+ *	  the child_relids specified in the add_child_eq_member() overlap with
+ *	  the child_relids in the setup_eclass_member_iterator() call.
+ *
+ * Note:
+ *	- The given 'child_relids' must remain allocated and not be changed for
+ *	  the lifetime of the iterator.
+ *
+ * Parameters:
+ *	it - A pointer to the iterator to set up.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	child_relids - The relids to return child members for.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids child_relids)
+{
+	it->ec = ec;
+	/* no need to set this if the class has no child members */
+	it->child_relids = ec->ec_childmembers != NULL ? child_relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from an EquivalenceMemberIterator 'it'
+ *	  that was setup by setup_eclass_member_iterator(). NULL is
+ *	  returned if there are no members left, in which case callers must not
+ *	  call eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	while (it->current_list != NULL)
+	{
+nextcell:
+		while (it->current_cell != NULL)
+		{
+			EquivalenceMember *em;
+
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			return em;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->child_relids, it->current_relid)) > 0)
+		{
+			/*
+			 * Be paranoid in case we're given relids above what we've sized
+			 * the ec_childmembers array to.
+			 */
+			if (it->current_relid >= it->ec->ec_childmembers_size)
+				return NULL;
+
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/*
+			 * If there are members in this list, use it, this will exclude
+			 * RELOPT_BASERELs as ec_childmembers[] are not populated for
+			 * those.
+			 */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+		return NULL;
+	}
+
+	return NULL;
+}
 
 /*
  * generate_implied_equalities_for_column
@@ -3053,6 +3257,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
 
@@ -3076,14 +3281,12 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
 
 		if (!cur_em)
@@ -3091,7 +3294,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 
 		/*
 		 * Found our match.  Scan the other EC members and attempt to generate
-		 * joinclauses.
+		 * joinclauses.  Ignore children here.
 		 */
 		foreach(lc2, cur_ec->ec_members)
 		{
@@ -3099,8 +3302,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3313,13 +3516,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
+	/*
+	 * To join, we need a member not in the given rel.  Ignore children here.
+	 */
 	foreach(lc, eclass->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4cabb358abc..601354ea3e0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3755,7 +3755,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3774,9 +3775,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(&it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 6fac08cb0d9..4e8923e383e 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1143,6 +1143,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			int			best_score = -1;
 			ListCell   *j;
 
+			/* Ignore children here */
 			foreach(j, sub_eclass->ec_members)
 			{
 				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
@@ -1151,8 +1152,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1710,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index ae20691ca91..c9f3b7f08ef 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -710,6 +710,13 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 		ec->ec_relids = adjust_relid_set(ec->ec_relids,
 										 sjinfo->ojrelid, subst);
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Fix up the member expressions.  Any non-const member that ends with
 	 * empty em_relids must be a Var or PHV of the removed relation.  We don't
@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
 		bool		is_redundant = false;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 4c466f76778..a44f5bb1f60 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1414,6 +1414,22 @@ typedef struct JoinDomain
  * In contrast, ec_sources holds equality clauses that appear directly in the
  * query. These are typically few and do not require a hash table for lookup.
  *
+ * 'ec_members' is a List of all EquivalenceMembers belonging to
+ * RELOPT_BASERELs.  EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and
+ * RELOPT_OTHER_JOINREL relations are stored in the 'ec_childmembers' array in
+ * the index corresponding to the relid.  'ec_childmembers' may be NULL if the
+ * class has no child EquivalenceMembers.
+ *
+ * For code wishing to look at EquivalenceMembers, if only parent-level
+ * members are needed, then a simple foreach loop over ec_members is
+ * sufficient.  When child members are also required, it is best to use the
+ * functionality provided by EquivalenceMemberIterator.  The reason for this
+ * is because large numbers of child EquivalenceMembers can exist in queries
+ * to partitioned tables with many partitions.  The functionality provided by
+ * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
+ * which belong to specific child relids.  See the header comments for
+ * EquivalenceMemberIterator below for further details.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1431,7 +1447,9 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
+	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	List	  **ec_childmembers;	/* array of Lists of child members */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1465,12 +1483,17 @@ typedef struct EquivalenceClass
  * child when necessary to build a MergeAppend path for the whole appendrel
  * tree.  An em_is_child member has no impact on the properties of the EC as a
  * whole; in particular the EC's ec_relids field does NOT include the child
- * relation.  An em_is_child member should never be marked em_is_const nor
- * cause ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
- * members are not really full-fledged members of the EC, but just reflections
- * or doppelgangers of real members.  Most operations on EquivalenceClasses
- * should ignore em_is_child members, and those that don't should test
- * em_relids to make sure they only consider relevant members.
+ * relation.  em_is_child members aren't stored in the ec_members List of the
+ * EC and instead they're stored and indexed by the relids of the child
+ * relation(s) they represent in ec_childmembers.  An em_is_child member
+ * should never be marked em_is_const nor cause ec_has_const or
+ * ec_has_volatile to be set, either.  Thus, em_is_child members are not
+ * really full-fledged members of the EC, but just reflections or
+ * doppelgangers of real members.  Most operations on EquivalenceClasses
+ * should ignore em_is_child members by only inspecting members in the
+ * ec_members list.  Callers that require inspecting child members should do
+ * so using an EquivalenceMemberIterator and should test em_relids to make
+ * sure they only consider relevant members.
  *
  * em_datatype is usually the same as exprType(em_expr), but can be
  * different when dealing with a binary-compatible opfamily; in particular
@@ -1493,6 +1516,70 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator allows efficient access to sets of
+ * EquivalenceMembers for callers which require access to child members.
+ * Because partitioning workloads can result in large numbers of child
+ * members, the child members are not stored in the EquivalenceClass's
+ * ec_members List.  Instead, these are stored in the EquivalenceClass's
+ * ec_childmembers array of Lists.  The functionality provided by
+ * EquivalenceMemberIterator aims to provide efficient access to parent
+ * members and child members belonging to specific child relids.
+ *
+ * Currently, there is only one way to initialize and iterate over an
+ * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
+ * and eclass_member_iterator_next functions.  The iterator object is
+ * generally a local variable which is passed by address to
+ * setup_eclass_member_iterator.  The calling function defines which
+ * EquivalenceClass the iterator should be looking at and which child
+ * relids to also include the members for.  child_relids can be passed as NULL
+ * but the caller may as well just perform a foreach loop over ec_members as
+ * only parent-level members will be returned in that case.
+ *
+ * When calling the next function on an EquivalenceMemberIterator, all
+ * parent-level EquivalenceMembers are returned first, followed by any
+ * child members for the relids specified by the child_relids parameter as
+ * specified when calling setup_eclass_member_iterator.  The child members
+ * returned are members which have any of the relids mentioned in
+ * child_relids.  That's not to be confused with returning members which
+ * contain *all* of the child relids specified when calling
+ * setup_eclass_member_iterator.  It is up to the calling function to ensure
+ * that the returned member matches what is required for the purpose.
+ *
+ * It is also important to note that when dealing with child
+ * EquivalenceMembers for RELOPT_OTHER_JOINRELs that it's possible for the
+ * same EquivalenceMembers to be returned more than once by the next function.
+ * This is currently not seen to be a problem, but some callers may want to be
+ * aware of it.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(&it, ec, child_relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *		...
+ * }
+ * -----
+ * It is not valid to call eclass_member_iterator_next() after it has returned
+ * NULL for any given EquivalenceMemberIterator.
+ */
+typedef struct
+{
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		child_relids;	/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b1a76816442..a48c9721797 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,10 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids child_relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 0c81d03950d..1fef9062da6 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -712,6 +712,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.43.0

#124David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#123)
2 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Sat, 5 Apr 2025 at 16:55, David Rowley <dgrowleyml@gmail.com> wrote:

I am still thinking about the duplicate members being returned from
the iterator for child join rels due to them being duplicated into
each component relid element in ec_childmembers. I did consider if
these could just not be duplicated and instead just put into the
ec_childmember element according to their lowest component relid. For
that to work, all callers that need these would need to ensure they
never pass some subset of child_relids when setting up the
EquivalenceMemberIterator. I need to study a bit more to understand if
that's doable.

It looks like the child members added by
add_child_join_rel_equivalences() are only required for pathkey
requirements. The EquivalenceMember mentions:

* for an appendrel child. These members are used for determining the
* pathkeys of scans on the child relation and for explicitly sorting the
* child when necessary to build a MergeAppend path for the whole appendrel
* tree. An em_is_child member has no impact on the properties of the EC as a

I used the attached .txt file to highlight the places where the
iterator returned the same member twice and saw only that
find_ec_member_matching_expr() does this.

Because during createplan, we'll have the specific RelOptInfo that we
need the EquivalenceMember for, we'll be passing the "relids" of that
RelOptInfo to setup_eclass_member_iterator(), in which case, I think
it's fine to store the member in just one of the ec_childmembers[]
array slots for just one of the relids making up the
RELOPT_OTHER_JOINREL's component relids as that means it'll be found
once only due to how eclass_member_iterator_next() looks at all of the
ec_childmembers[] elements for the given relids.

Doing this also allows the code in add_child_eq_member() to be simplified.

I made this happen in the attached v41 patch, and that's the last
outstanding issue that I had for this.

I think this is worthy of getting into v18. Does anyone else think
differently? It'd be good to know that soon.

David

Attachments:

eq_member_find_duplicates.patch.txttext/plain; charset=US-ASCII; name=eq_member_find_duplicates.patch.txtDownload
diff --git b/src/backend/optimizer/path/equivclass.c a/src/backend/optimizer/path/equivclass.c
index b8c64d80987..30f1ca37e2e 100644
--- b/src/backend/optimizer/path/equivclass.c
+++ a/src/backend/optimizer/path/equivclass.c
@@ -617,6 +617,7 @@ make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
+	em->iterator_generation = 0;
 
 	return em;
 }
@@ -930,6 +931,7 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 		expr = ((RelabelType *) expr)->arg;
 
 	setup_eclass_member_iterator(&it, ec, relids);
+	it.warn_if_returned_before = false;
 	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
 		Expr	   *emexpr;
@@ -3149,10 +3151,14 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
  *	ec - The EquivalenceClass from which to iterate members.
  *	child_relids - The relids to return child members for.
  */
+uint64 iterator_generation = 0;
+
 void
 setup_eclass_member_iterator(EquivalenceMemberIterator *it,
 							 EquivalenceClass *ec, Relids child_relids)
 {
+	it->iterator_generation = ++iterator_generation;
+	it->warn_if_returned_before = true;
 	it->ec = ec;
 	/* no need to set this if the class has no child members */
 	it->child_relids = ec->ec_childmembers != NULL ? child_relids : NULL;
@@ -3179,6 +3185,9 @@ nextcell:
 			EquivalenceMember *em;
 
 			em = lfirst_node(EquivalenceMember, it->current_cell);
+			if (it->warn_if_returned_before && em->iterator_generation == it->iterator_generation)
+				elog(NOTICE, "returned before %llu", it->iterator_generation);
+			em->iterator_generation = it->iterator_generation;
 			it->current_cell = lnext(it->current_list, it->current_cell);
 			return em;
 		}
diff --git b/src/include/nodes/pathnodes.h a/src/include/nodes/pathnodes.h
index a44f5bb1f60..60f156da136 100644
--- b/src/include/nodes/pathnodes.h
+++ a/src/include/nodes/pathnodes.h
@@ -1514,6 +1514,7 @@ typedef struct EquivalenceMember
 	JoinDomain *em_jdomain;		/* join domain containing the source clause */
 	/* if em_is_child is true, this links to corresponding EM for top parent */
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
+	uint64 iterator_generation;
 } EquivalenceMember;
 
 /*
@@ -1570,6 +1571,8 @@ typedef struct EquivalenceMember
  */
 typedef struct
 {
+	uint64 iterator_generation;
+	bool warn_if_returned_before;
 	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
 	int			current_relid;	/* Current relid position within 'relids'. -1
 								 * when still looping over ec_members and -2
v41-0001-Speedup-child-EquivalenceMember-lookup-in-planne.patchapplication/octet-stream; name=v41-0001-Speedup-child-EquivalenceMember-lookup-in-planne.patchDownload
From 87a159451c775c8cb8067c6fcdb23b6891c9ed18 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v41] Speedup child EquivalenceMember lookup in planner

When planning queries to partitioned tables, we create child
EquivalenceMembers for each EquivalenceMembers which mentions the parent
partitioned table.  For partitioned tables with large numbers of
partitions, this meant we could end up with many EquivalenceMembers and
this could cause the planner to become slow due to the linear lookups
that are performed on the EquivalenceClass ec_members's List.
Effectively, the more partitions which were present, the more lookups
needed to be performed for operations such as
find_ec_member_matching_expr() during create_plan() and the more
partitions present, the longer these searches would take.

To fix this, here we adjust how we store EquivalenceMembers for
em_is_child members.  Instead of storing these directly in ec_members,
these are now stored in a new array of Lists in the EquivalenceClass
which is indexed by the relid.  When we want to find EquivalenceMembers
belonging to a certain child relation, we can narrow the search to the
array element for that relation.

To make EquivalenceMember lookup easier and to reduce the amount of code
change, this commit provides a pair of functions to allow iteration over
the EquivalenceMembers in a class which handles finding the child
members, if required.  Callers that never need to look at child members
can remain using the foreach loop over ec_members, which will now often
be faster due to only parent-level members being stored there.

The actual performance increases here are highly dependent on the number
of partitions and the query being planned.  Performance increase can be
visible with as few as 8 partitions, but the speedup is marginal for
such low numbers of partitions.  The speedups become much more visible
with a few dozen to hundreds of partitions.  Some tested queries were
around 3x faster with 256 partitions.  For users with thousands of
partitions, these are likely to become significantly faster.  Some
testing has shown planner speedups of 20x or more.

Author: Yuya Watari <watari.yuya@gmail.com>
Co-authored-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andrey Lepikhov <a.lepikhov@postgrespro.ru>
Reviewed-by: Alena Rybakina <lena.ribackina@yandex.ru>
Reviewed-by: Dmitry Dolgov <9erthalion6@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Tested-by: Thom Brown <thom@linux.com>
Tested-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Tested-by: newtglobal postgresql_contributors <postgresql_contributors@newtglobalcorp.com>
Discussion: https://postgr.es/m/CAJ2pMkZNCgoUKSE%2B_5LthD%2BKbXKvq6h2hQN8Esxpxd%2Bcxmgomg%40mail.gmail.com
---
 contrib/postgres_fdw/postgres_fdw.c       |  19 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 407 ++++++++++++++++------
 src/backend/optimizer/path/indxpath.c     |   8 +-
 src/backend/optimizer/path/pathkeys.c     |  10 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +
 src/include/nodes/pathnodes.h             |  95 ++++-
 src/include/optimizer/paths.h             |   4 +
 src/tools/pgindent/typedefs.list          |   1 +
 9 files changed, 442 insertions(+), 118 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b4e0e60928b..a7e0cc9f323 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7908,7 +7907,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
-		/* Locate an EquivalenceClass member matching this expr, if any */
+		/*
+		 * Locate an EquivalenceClass member matching this expr, if any.
+		 * Ignore child members.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
@@ -7918,9 +7920,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 557f06e344f..ceac3fd8620 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,7 +465,9 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
+	WRITE_INT_FIELD(ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_sources);
 	/* Only ec_derives_list is written; hash is not serialized. */
 	WRITE_NODE_FIELD(ec_derives_list);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 5fb2cf0daf8..fee9ff26817 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,11 +34,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Index child_relid);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -314,11 +326,15 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		foreach(lc2, cur_ec->ec_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -428,7 +444,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -445,7 +461,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -465,7 +481,9 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
+		ec->ec_childmembers_size = 0;
 		ec->ec_members = NIL;
+		ec->ec_childmembers = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives_list = NIL;
 		ec->ec_derives_hash = NULL;
@@ -478,9 +496,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -566,11 +584,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *		Build a new EquivalenceMember without adding it to an EC.  If 'parent'
+ *		is NULL, the result will be a parent member, otherwise a child member.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -597,11 +617,85 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	/* add to the members list */
+	ec->ec_members = lappend(ec->ec_members, em);
+
+	/* record the relids for parent members */
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ *
+ * 'root' the PlannerInfo that 'ec' belongs to.
+ * 'ec' the EquivalenceClass to add the child member to.
+ * 'ec_index' the index of 'ec' within root->eq_classes, or -1 if maintaining
+ * the RelOptInfo.eclass_indexes isn't needed.
+ * 'expr' the em_expr for the new member.
+ * 'relids' the 'em_relids' for the new member.
+ * 'jdomain' the 'em_jdomain' for the new member.
+ * 'parent_em' the parent member of the child to create.
+ * 'datatype' the em_datatype of the new member.
+ * 'child_relid' defines which element of ec_childmembers to add this member
+ * to.  This is generally a RELOPT_OTHER_MEMBER_REL, but for set operations
+ * can be a RELOPT_BASEREL representing the set-op children.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Index child_relid)
+{
+	EquivalenceMember *em;
+
+	Assert(parent_em != NULL);
+
+	/*
+	 * Allocate member to store children.  An array of Lists indexed by relid,
+	 * or expand the existing one, if necessary.
+	 */
+	if (unlikely(ec->ec_childmembers_size < root->simple_rel_array_size))
+	{
+		if (ec->ec_childmembers == NULL)
+			ec->ec_childmembers = palloc0_array(List *, root->simple_rel_array_size);
+		else
+			ec->ec_childmembers = repalloc0_array(ec->ec_childmembers, List *,
+												  ec->ec_childmembers_size,
+												  root->simple_rel_array_size);
+
+		ec->ec_childmembers_size = root->simple_rel_array_size;
+	}
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	/* add member to the ec_childmembers List for the given child_relid */
+	ec->ec_childmembers[child_relid] = lappend(ec->ec_childmembers[child_relid], em);
+
+	/* Record this EC index for the child rel */
+	if (ec_index >= 0)
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		RelOptInfo *child_rel = root->simple_rel_array[child_relid];
+
+		child_rel->eclass_indexes =
+			bms_add_member(child_rel->eclass_indexes, ec_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -672,7 +766,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -687,10 +782,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -725,7 +819,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
+	newec->ec_childmembers_size = 0;
 	newec->ec_members = NIL;
+	newec->ec_childmembers = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives_list = NIL;
 	newec->ec_derives_hash = NULL;
@@ -747,7 +843,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -821,15 +917,16 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -898,7 +995,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -912,9 +1010,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -1193,6 +1291,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		return;
 	}
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Find the constant member to use.  We prefer an actual constant to
 	 * pseudo-constants (such as Params), because the constraint exclusion
@@ -1219,7 +1320,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1283,12 +1385,16 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach(lc, ec->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		Assert(!cur_em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1621,7 +1727,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1632,10 +1739,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1668,6 +1774,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1742,6 +1849,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1749,7 +1857,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2188,6 +2296,9 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		bool		match;
 		ListCell   *lc2;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2205,7 +2316,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			Assert(!cur_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2303,6 +2415,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		ListCell   *lc2;
 		int			coal_idx = -1;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2332,7 +2447,8 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+			Assert(!coal_em->em_is_child);	/* Child members should not exist
+											 * in ec_members */
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2461,6 +2577,9 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
 
+		/* We don't expect any children yet */
+		Assert(ec->ec_childmembers == NULL);
+
 		/* Need do anything only for a multi-member, no-const EC. */
 		if (list_length(ec->ec_members) > 1 && !ec->ec_has_const)
 		{
@@ -2546,12 +2665,13 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 			!list_member_oid(ec->ec_opfamilies, opfamily))
 			continue;
 
+		/* Ignore children here */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			Assert(!em->em_is_child);	/* Child members should not exist in
+										 * ec_members */
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2615,15 +2735,18 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
-		/* It's okay to consider "broken" ECs here, see exprs_known_equal */
 
+		/*
+		 * It's okay to consider "broken" ECs here, see exprs_known_equal.
+		 * Ignore children here.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2709,6 +2832,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 	Relids		top_parent_relids = child_rel->top_parent_relids;
 	Relids		child_relids = child_rel->relids;
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * EC merging should be complete already, so we can use the parent rel's
@@ -2721,7 +2845,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2734,29 +2857,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2801,12 +2910,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relid);
 			}
 		}
 	}
@@ -2853,7 +2965,7 @@ 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);
-		int			num_members;
+		ListCell   *lc;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2866,25 +2978,15 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach(lc, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
+			EquivalenceMember *cur_em = lfirst_node(EquivalenceMember, lc);
 
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2929,9 +3031,35 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				/*
+				 * Add new child member to the EquivalenceClass.  Because this
+				 * is a RELOPT_OTHER_JOINREL which has multiple component
+				 * relids, there is no ideal place to store these members in
+				 * the class.  Ordinarily, child members are stored in the
+				 * ec_childmembers[] array element corresponding to their
+				 * relid, however, here we have multiple component relids, so
+				 * there's no single ec_childmembers[] array element to store
+				 * this member.  So that we still correctly find this member
+				 * for pathkey sort order lookups, we opt to store in the
+				 * ec_childmembers array in only the first component relid
+				 * slot of the array.  This allows the member to be found
+				 * providing callers of setup_eclass_member_iterator() specify
+				 * all the component relids for the RELOPT_OTHER_JOINREL,
+				 * which they do.  If we opted to store the member in each
+				 * ec_childmembers[] element for all the component relids,
+				 * then that would just result in
+				 * eclass_member_iterator_next() finding the member multiple
+				 * times, which is a waste of effort.
+				 */
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									bms_next_member(child_joinrel->relids, -1));
 			}
 		}
 	}
@@ -2978,14 +3106,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relid);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3000,6 +3132,86 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members belonging to the given 'ec'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec_members for 'ec', and;
+ *	- Any child member added to the given ec by add_child_eq_member() where
+ *	  the child_relid specified in the add_child_eq_member() call is a member
+ *	  of the child_relids specified in the setup_eclass_member_iterator()
+ *	  call.
+ *
+ * Note:
+ *	- The given 'child_relids' must remain allocated and not be changed for
+ *	  the lifetime of the iterator.
+ *
+ * Parameters:
+ *	it - A pointer to the iterator to set up.
+ *	ec - The EquivalenceClass from which to iterate members.
+ *	child_relids - The relids to return child members for.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids child_relids)
+{
+	it->ec = ec;
+	/* no need to set this if the class has no child members array set */
+	it->child_relids = ec->ec_childmembers != NULL ? child_relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get a next EquivalenceMember from the EquivalenceMemberIterator 'it'
+ *	  as setup by setup_eclass_member_iterator(). NULL is returned if there
+ *	  are no members left, in which case callers must not call
+ *	  eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	while (it->current_list != NULL)
+	{
+nextcell:
+		while (it->current_cell != NULL)
+		{
+			EquivalenceMember *em;
+
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			return em;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->child_relids, it->current_relid)) > 0)
+		{
+			/*
+			 * Be paranoid in case we're given relids above what we've sized
+			 * the ec_childmembers array to.
+			 */
+			if (it->current_relid >= it->ec->ec_childmembers_size)
+				return NULL;
+
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/* If there are members in this list, use it. */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+		return NULL;
+	}
+
+	return NULL;
+}
 
 /*
  * generate_implied_equalities_for_column
@@ -3052,6 +3264,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
 
@@ -3075,14 +3288,12 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
 
 		if (!cur_em)
@@ -3090,7 +3301,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 
 		/*
 		 * Found our match.  Scan the other EC members and attempt to generate
-		 * joinclauses.
+		 * joinclauses.  Ignore children here.
 		 */
 		foreach(lc2, cur_ec->ec_members)
 		{
@@ -3098,8 +3309,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3312,13 +3523,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
+	/*
+	 * To join, we need a member not in the given rel.  Ignore children here.
+	 */
 	foreach(lc, eclass->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4cabb358abc..601354ea3e0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3755,7 +3755,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3774,9 +3775,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(&it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 981802d7b9d..8b04d40d36d 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1143,6 +1143,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			int			best_score = -1;
 			ListCell   *j;
 
+			/* Ignore children here */
 			foreach(j, sub_eclass->ec_members)
 			{
 				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
@@ -1151,8 +1152,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1710,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index ae20691ca91..c9f3b7f08ef 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -710,6 +710,13 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 		ec->ec_relids = adjust_relid_set(ec->ec_relids,
 										 sjinfo->ojrelid, subst);
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Fix up the member expressions.  Any non-const member that ends with
 	 * empty em_relids must be a Var or PHV of the removed relation.  We don't
@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
 		bool		is_redundant = false;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 4c466f76778..33533cf46d3 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1414,6 +1414,24 @@ typedef struct JoinDomain
  * In contrast, ec_sources holds equality clauses that appear directly in the
  * query. These are typically few and do not require a hash table for lookup.
  *
+ * 'ec_members' is a List of all EquivalenceMembers belonging to parent
+ * members.  EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and
+ * RELOPT_OTHER_JOINREL relations are stored in the 'ec_childmembers' array in
+ * the index corresponding to the relid, or first component relid in the case
+ * of RELOPT_OTHER_JOINRELs.  'ec_childmembers' is NULL if the class has
+ * no child EquivalenceMembers.
+ *
+ * For code wishing to look at EquivalenceMembers, if only parent-level
+ * members are needed, then a simple foreach loop over ec_members is
+ * sufficient.  When child members are also required, it is best to use the
+ * functionality provided by EquivalenceMemberIterator.  This visits all
+ * parent members and only the relevant child members.  The reason for this
+ * is that large numbers of child EquivalenceMembers can exist in queries to
+ * partitioned tables with many partitions.  The functionality provided by
+ * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
+ * which belong to specific child relids.  See the header comments for
+ * EquivalenceMemberIterator below for further details.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1431,7 +1449,9 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
+	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	List	  **ec_childmembers;	/* array of Lists of child members */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1465,12 +1485,17 @@ typedef struct EquivalenceClass
  * child when necessary to build a MergeAppend path for the whole appendrel
  * tree.  An em_is_child member has no impact on the properties of the EC as a
  * whole; in particular the EC's ec_relids field does NOT include the child
- * relation.  An em_is_child member should never be marked em_is_const nor
- * cause ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
- * members are not really full-fledged members of the EC, but just reflections
- * or doppelgangers of real members.  Most operations on EquivalenceClasses
- * should ignore em_is_child members, and those that don't should test
- * em_relids to make sure they only consider relevant members.
+ * relation.  em_is_child members aren't stored in the ec_members List of the
+ * EC and instead they're stored and indexed by the relids of the child
+ * relation they represent in ec_childmembers.  An em_is_child member
+ * should never be marked em_is_const nor cause ec_has_const or
+ * ec_has_volatile to be set, either.  Thus, em_is_child members are not
+ * really full-fledged members of the EC, but just reflections or
+ * doppelgangers of real members.  Most operations on EquivalenceClasses
+ * should ignore em_is_child members by only inspecting members in the
+ * ec_members list.  Callers that require inspecting child members should do
+ * so using an EquivalenceMemberIterator and should test em_relids to make
+ * sure they only consider relevant members.
  *
  * em_datatype is usually the same as exprType(em_expr), but can be
  * different when dealing with a binary-compatible opfamily; in particular
@@ -1493,6 +1518,64 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator allows efficient access to sets of
+ * EquivalenceMembers for callers which require access to child members.
+ * Because partitioning workloads can result in large numbers of child
+ * members, the child members are not stored in the EquivalenceClass's
+ * ec_members List.  Instead, these are stored in the EquivalenceClass's
+ * ec_childmembers array of Lists.  The functionality provided by
+ * EquivalenceMemberIterator aims to provide efficient access to parent
+ * members and child members belonging to specific child relids.
+ *
+ * Currently, there is only one way to initialize and iterate over an
+ * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
+ * and eclass_member_iterator_next functions.  The iterator object is
+ * generally a local variable which is passed by address to
+ * setup_eclass_member_iterator.  The calling function defines which
+ * EquivalenceClass the iterator should be looking at and which child
+ * relids to also include the members for.  child_relids can be passed as NULL
+ * but the caller may as well just perform a foreach loop over ec_members as
+ * only parent-level members will be returned in that case.
+ *
+ * When calling the next function on an EquivalenceMemberIterator, all
+ * parent-level EquivalenceMembers are returned first, followed by any
+ * all child members which have been indexed by add_child_eq_member() for any
+ * of the child_relids specified when calling setup_eclass_member_iterator().
+ * Many callers will want to check the returned member's em_relids matches
+ * their search criteria as in the case of RELOPT_OTHER_JOINREL, the iterator
+ * will return members where the em_relids overlaps the child_relids specified
+ * when calling setup_eclass_member_iterator().  The caller may wish to ensure
+ * the em_relids is a subset of the relids they're searching for.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(&it, ec, child_relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *		...
+ * }
+ * -----
+ * It is not valid to call eclass_member_iterator_next() after it has returned
+ * NULL for any given EquivalenceMemberIterator.
+ */
+typedef struct
+{
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		child_relids;	/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b1a76816442..a48c9721797 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,10 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids child_relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d42b943ef94..e6c917aca72 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -712,6 +712,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.43.0

#125Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#124)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

Thank you very much for updating the patches and clearly addressing
these points.

On Mon, Apr 7, 2025 at 8:04 AM David Rowley <dgrowleyml@gmail.com> wrote:

I used the attached .txt file to highlight the places where the
iterator returned the same member twice and saw only that
find_ec_member_matching_expr() does this.

Because during createplan, we'll have the specific RelOptInfo that we
need the EquivalenceMember for, we'll be passing the "relids" of that
RelOptInfo to setup_eclass_member_iterator(), in which case, I think
it's fine to store the member in just one of the ec_childmembers[]
array slots for just one of the relids making up the
RELOPT_OTHER_JOINREL's component relids as that means it'll be found
once only due to how eclass_member_iterator_next() looks at all of the
ec_childmembers[] elements for the given relids.

Doing this also allows the code in add_child_eq_member() to be simplified.

I agree with you. Using the first member of relids as the array index
is simple, clear, and effective, significantly simplifying the logic
and implementation.

I made this happen in the attached v41 patch, and that's the last
outstanding issue that I had for this.

I think this is worthy of getting into v18. Does anyone else think
differently? It'd be good to know that soon.

Thank you very much for sharing this new patch. It looks good to me,
and I'd also be very happy to see this committed in v18.

--
Best regards,
Yuya Watari

#126Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#124)
Re: [PoC] Reducing planning time when tables have many partitions

David Rowley <dgrowleyml@gmail.com> writes:

I think this is worthy of getting into v18. Does anyone else think
differently? It'd be good to know that soon.

v41 passes an eyeball check for me.

regards, tom lane

#127Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: David Rowley (#124)
Re: [PoC] Reducing planning time when tables have many partitions

On Mon, Apr 7, 2025 at 4:34 AM David Rowley <dgrowleyml@gmail.com> wrote:

On Sat, 5 Apr 2025 at 16:55, David Rowley <dgrowleyml@gmail.com> wrote:

I am still thinking about the duplicate members being returned from
the iterator for child join rels due to them being duplicated into
each component relid element in ec_childmembers. I did consider if
these could just not be duplicated and instead just put into the
ec_childmember element according to their lowest component relid. For
that to work, all callers that need these would need to ensure they
never pass some subset of child_relids when setting up the
EquivalenceMemberIterator. I need to study a bit more to understand if
that's doable.

It looks like the child members added by
add_child_join_rel_equivalences() are only required for pathkey
requirements. The EquivalenceMember mentions:

* for an appendrel child. These members are used for determining the
* pathkeys of scans on the child relation and for explicitly sorting the
* child when necessary to build a MergeAppend path for the whole appendrel
* tree. An em_is_child member has no impact on the properties of the EC as a

I used the attached .txt file to highlight the places where the
iterator returned the same member twice and saw only that
find_ec_member_matching_expr() does this.

Because during createplan, we'll have the specific RelOptInfo that we
need the EquivalenceMember for, we'll be passing the "relids" of that
RelOptInfo to setup_eclass_member_iterator(), in which case, I think
it's fine to store the member in just one of the ec_childmembers[]
array slots for just one of the relids making up the
RELOPT_OTHER_JOINREL's component relids as that means it'll be found
once only due to how eclass_member_iterator_next() looks at all of the
ec_childmembers[] elements for the given relids.

Doing this also allows the code in add_child_eq_member() to be simplified.

I made this happen in the attached v41 patch, and that's the last
outstanding issue that I had for this.

I think this is worthy of getting into v18. Does anyone else think
differently? It'd be good to know that soon.

I took a quick look at patch 0001. I have some questions and cosmetic comments

- foreach(lc2, cur_ec->ec_members)
+ setup_eclass_member_iterator(&it, cur_ec, rel);
+ while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
/*
* Ignore child members unless they match the request.
*/

Can this step be executed in the iterator itself?
find_ec_member_matching_expr() also does something similar but uses
bms_is_subset() instead of bms_equal(). The BMS comparison operation
to use itself could be an argument to the iterator. The iterator knows
when it has started looking for child members and it also scans the
ec_members by relids, so I guess we could eliminate many comparisons
and boolean variable check.

generate_join_implied_equalities_normal() also compares relids but it
performs three comparisons, probably one of the comparisons can be
pushed into the iterator.

Scanning the code further I see why we can't do relids comparison in
the iterator itself - because we don't store ec_member in the array
elements correspodning each of relids participating in it. Let's say
an EM has em_relids (10, 11, 12) all child rels, but the EM is stored
only in list of array element corresponding to 10. If someone searches
EMs with em_relids containing 11, 12 only, this EM will be missed.

* the iterator
* will return members where the em_relids overlaps the child_relids specified
* when calling setup_eclass_member_iterator(). The caller may wish to ensure
* the em_relids is a subset of the relids they're searching for.

em_relids should be subset of or equal to child_relids specified,
otherwise it may be missed as explained above. We should rephrase the
sentence as " The caller should ensure ...". "May wish to" indicates
an optional thing, but it doesn't look optional to me.

@@ -725,7 +819,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
newec = makeNode(EquivalenceClass);
newec->ec_opfamilies = list_copy(opfamilies);
newec->ec_collation = collation;
+ newec->ec_childmembers_size = 0;
newec->ec_members = NIL;

How about renaming ec_members to ec_parent_members to make it
explicit. That might break extensions that use ec_members but that way
they would know that the purpose of this list has changed and it will
nudge them to use ec_parent_member of EC iterator appropriately.

@@ -2461,6 +2577,9 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
+ /* We don't expect any children yet */
+ Assert(ec->ec_childmembers == NULL);
+

This function's prologue or comments inside it do not mention this.
Also the loop over ec_members prior to your change didn't exclude
child ec_members explicitly. So, I guess, we need to add a comment
mentioning why child ec members are not expected here.

- /*
- * We don't use foreach() here because there's no point in scanning
- * newly-added child members, so we can stop after the last
- * pre-existing EC member.
- */
- num_members = list_length(cur_ec->ec_members);
- for (int pos = 0; pos < num_members; pos++)
+ foreach(lc, cur_ec->ec_members)

Since you are adding a new foreach loop, does it make sense to use
foreach_node() instead?

- /*
- * We don't use foreach() here because there's no point in scanning
- * newly-added child members, so we can stop after the last
- * pre-existing EC member.
- */
- num_members = list_length(cur_ec->ec_members);
- for (int pos = 0; pos < num_members; pos++)
+ foreach(lc, cur_ec->ec_members)

foreach_node instead of foreach?

+ /*
+ * We don't expect any EC child members to exist at this point. Ensure
+ * that's the case, otherwise we might be getting asked to do something
+ * this function hasn't been coded for.
+ */
+ Assert(ec->ec_childmembers == NULL);
+

In find_em_for_rel_target() we have Assert(!em->em_is_child), but here
it's different Assert for the same purpose. It would be good to be
consistent in all such places either Asserting !em->em_is_child in the
loop or Assert(ec->ec_childmembers) OR both as you have done in
process_equivalence().

I am also surprised that the function does not have any comment or
Assert about child ec_members. It might be good to explain why we
don't expect any child members in this function. Or do you think that
the explanation is unnecessary.

@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
List *new_members = NIL;
List *new_sources = NIL;
+ /*
+ * We don't expect any EC child members to exist at this point. Ensure
+ * that's the case, otherwise we might be getting asked to do something
+ * this function hasn't been coded for.
+ */
+ Assert(ec->ec_childmembers == NULL);
+

Similar to above.

--
Best Wishes,
Ashutosh Bapat

#128David Rowley
dgrowleyml@gmail.com
In reply to: Ashutosh Bapat (#127)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, 8 Apr 2025 at 04:54, Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

- foreach(lc2, cur_ec->ec_members)
+ setup_eclass_member_iterator(&it, cur_ec, rel);
+ while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
/*
* Ignore child members unless they match the request.
*/

Can this step be executed in the iterator itself?

Yes, all the filtering requested *could* be done within the iterator.
The problem with doing that is that there's not even nearly a single
pattern to what callers need. Here's a summary of all 7 users of it:

1. get_eclass_for_sort_expr: CHILD members must have em_relids EQUAL
to rel (TYPE 1)
2. generate_join_implied_equalities_normal: ALL members must be a
SUBSET of join_relids (TYPE 2)
3. generate_implied_equalities_for_column: ALL members must have
em_relids EQUAL to rel->relids (TYPE 3)
4. find_em_for_rel: ALL members must be a SUBSET of rel->relids (TYPE 2)
5. find_ec_member_matching_expr: CHILD members must have em_relids
SUBSET of relids (TYPE 4)
6. find_computable_ec_member: CHILD members must have em_relids SUBSET
of relids (TYPE 4)
7. match_pathkeys_to_index: ALL members must have em_relids EQUAL to
index->rel->relids (TYPE 3)

So, 4 distinct requirements and only types 3 and 4 are repeated.
Certainly, we could code up the iterator to handle all those different
requirements, but would that code be easy to follow? Do you think 1
iterator should handle all those requirements with a set of IF
statements? I think that would be more difficult to read than how the
patch has it now.

An alternative that I did consider, and even put a comment on the 2nd
paragraph of the header comment for EquivalenceMemberIterator about,
is the possibility of having multiple iterators for different
purposes. That would save the spaghetti code of IF statements doing it
in 1 iterator and alleviate the performance overhead of the spaghetti
code too. The problem is that there's still not a common pattern
that's followed often enough. If I wrote 4 iterators, I'd need to give
them meaningful names. Names like
eclass_member_iterator_children_with_subset_of_next are a too long and
if I was writing new code that was to call some function like that,
I'd probably be happier just doing the filtering locally than trying
to find which of the 4 iterators is the one I want.

find_ec_member_matching_expr() also does something similar but uses
bms_is_subset() instead of bms_equal(). The BMS comparison operation
to use itself could be an argument to the iterator. The iterator knows
when it has started looking for child members and it also scans the
ec_members by relids, so I guess we could eliminate many comparisons
and boolean variable check.

Performance-wise, it would be nicer to filter within the iterator as
calling next() once per loop means an external function call on each
loop. Unfortunately, I don't think there's a common enough pattern to
warrant all the iterators that are needed. I feel we'd need to see
some demonstratable performance improvements to warrant adding
special-purpose iterators. I expect those will be quite small, but
happy if someone proves me wrong. I'd rather not hold up the patch for
a hypothetical 1-2% on some workload when the current patch as-is
gives 20x for some tested other workloads.

generate_join_implied_equalities_normal() also compares relids but it
performs three comparisons, probably one of the comparisons can be
pushed into the iterator.

Could be. There's also the overhead of having to document each
iterator so that callers know what's handled and what they need to
handle themselves.

Scanning the code further I see why we can't do relids comparison in
the iterator itself - because we don't store ec_member in the array
elements correspodning each of relids participating in it. Let's say
an EM has em_relids (10, 11, 12) all child rels, but the EM is stored
only in list of array element corresponding to 10. If someone searches
EMs with em_relids containing 11, 12 only, this EM will be missed.

That currently won't happen as the only child members which have
multiple relids is em_relids are RELOPT_OTHER_JOINREL and they're only
searched during create plan. Those searches are always done with all
the relids belonging to that RELOPT_OTHER_JOINREL. This is the reason
I think it's ok to store the member for that in the slot for relid 10
(in this example)

* the iterator
* will return members where the em_relids overlaps the child_relids specified
* when calling setup_eclass_member_iterator(). The caller may wish to ensure
* the em_relids is a subset of the relids they're searching for.

em_relids should be subset of or equal to child_relids specified,
otherwise it may be missed as explained above. We should rephrase the
sentence as " The caller should ensure ...". "May wish to" indicates
an optional thing, but it doesn't look optional to me.

That comment does not know what the caller needs to do. That's up to
the caller. If every caller needed to do that, we might as well just
filter non-matching ones out in the iterator itself.

I did rewrite that comment quite a bit in the attached version as I
still wasn't quite happy with the wording. I still didn't make any
assumptions about what the caller wants, however.

@@ -725,7 +819,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
newec = makeNode(EquivalenceClass);
newec->ec_opfamilies = list_copy(opfamilies);
newec->ec_collation = collation;
+ newec->ec_childmembers_size = 0;
newec->ec_members = NIL;

How about renaming ec_members to ec_parent_members to make it
explicit. That might break extensions that use ec_members but that way
they would know that the purpose of this list has changed and it will
nudge them to use ec_parent_member of EC iterator appropriately.

The reason I didn't rename that field is mainly because I'm fine with
the current name. In my view, I think of parent members as normal
members of the class and only the child members are 2nd class members.
You can see evidence of that line of thought in the patch with
add_eq_member() not being called add_parent_eq_member().

One reason that I did think about in favour of a rename was to
purposefully break any extension code using that field. I didn't
look, but I imagine that Citus and maybe Timescale could be extensions
that might have code that needs to be updated. However, I do imagine
it won't take them very long to notice their stuff isn't working. Do
you think we need to make it so their compiler prompts them to fix the
code? The rename is permanent, and breaking extensions is just a
transient thing between now and whenever extension authors release
their v18-supported extension. If I thought the breakage was something
subtle, I'd be more inclined to agree to the rename.

I certainly could get on board with renaming if there's consensus to
do so. I don't think that's going to happen in the next 9 hours. Is it
worth pushing this out to v19 because we can't agree on the name of a
struct field? I'd be disappointed if we miss this because of something
so trivial.

@@ -2461,6 +2577,9 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
+ /* We don't expect any children yet */
+ Assert(ec->ec_childmembers == NULL);
+

This function's prologue or comments inside it do not mention this.
Also the loop over ec_members prior to your change didn't exclude
child ec_members explicitly. So, I guess, we need to add a comment
mentioning why child ec members are not expected here.

I've added some comments there. I had already done something similar
in analyzejoins.c

- /*
- * We don't use foreach() here because there's no point in scanning
- * newly-added child members, so we can stop after the last
- * pre-existing EC member.
- */
- num_members = list_length(cur_ec->ec_members);
- for (int pos = 0; pos < num_members; pos++)
+ foreach(lc, cur_ec->ec_members)

Since you are adding a new foreach loop, does it make sense to use
foreach_node() instead?

OK. I am in favour of new code using those. It's a nicer API. I've
edited the patch

+ /*
+ * We don't expect any EC child members to exist at this point. Ensure
+ * that's the case, otherwise we might be getting asked to do something
+ * this function hasn't been coded for.
+ */
+ Assert(ec->ec_childmembers == NULL);
+

In find_em_for_rel_target() we have Assert(!em->em_is_child), but here
it's different Assert for the same purpose. It would be good to be
consistent in all such places either Asserting !em->em_is_child in the
loop or Assert(ec->ec_childmembers) OR both as you have done in
process_equivalence().

I think this might not be obvious when reading the patch, but I think
where the confusion might be starting is that
"Assert(!cur_em->em_is_child);" isn't asserting what it used to. In
unpatched master, there are a few places that do that to verify the
function is never called after child members have been added. The
patch changes the meaning of this Assert. The comment was updated in a
bid to try to make this more obvious. The Assert's new purpose is to
ensure no child members snuck into the ec_members list. For the
functions that fundamentally don't expect children to exist yet, I've
added an Assert(cur_ec->ec_childmembers == NULL) to ensure that
remains true. I believe I'm consistent with that and I believe that's
a worthwhile assert to make.

Occasionally, there are Asserts to check there are no child members in
ec_members elsewhere in places where ec_childmembers might contain
children. I believe all the Asserts doing this were all converted from
an "if (ec->em_is_child) continue;" statement. I don't feel the need
to add this assert everywhere that we loop over ec_members. It does
not seem likely that we'd get an em_is_child member in ec_members as
there's only a single place where the ec_members list is adjusted.
I'd be more easily convinced we can just delete all those Asserts than
I could be to add more.

I am also surprised that the function does not have any comment or
Assert about child ec_members. It might be good to explain why we
don't expect any child members in this function. Or do you think that
the explanation is unnecessary.

@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
List *new_members = NIL;
List *new_sources = NIL;
+ /*
+ * We don't expect any EC child members to exist at this point. Ensure
+ * that's the case, otherwise we might be getting asked to do something
+ * this function hasn't been coded for.
+ */
+ Assert(ec->ec_childmembers == NULL);
+

Similar to above.

The patch isn't adding any new reason for this. If you want this then
I'll need to go and figure out all the existing reasons and document
them. However, I think it's a simple case that these functions are
always called before children exist and are coded with that
assumption. I think the comment I added for this in analyzejoins.c
works ok. I think for cases such as process_equivalence(), it's much
more fundamental that children can't exist yet, as we're still working
on figuring out what the parent members are going to be. So it's not a
case of the function just not being coded to handle children as it is
in analyzejoins.c.

I've attached a version with the foreach_node() changes included. I've
also done some extensive work on the comments to try to make the API
of the interator easier to understand. There's also a small
optimisation made to eclass_member_iterator_next() to move a goto
label to save having to recheck if the ListCell is NULL when moving to
the next list. We're already pointing to the proven non-empty list
head at that point, so we don't need to check the ListCell is NULL
twice.

David

Attachments:

v42-0001-Speedup-child-EquivalenceMember-lookup-in-planne.patchapplication/octet-stream; name=v42-0001-Speedup-child-EquivalenceMember-lookup-in-planne.patchDownload
From e7c5f09d3469261577e85c16007e2bdf8eb3b2f4 Mon Sep 17 00:00:00 2001
From: Yuya Watari <watari.yuya@gmail.com>
Date: Fri, 21 Mar 2025 09:40:24 +0900
Subject: [PATCH v42] Speedup child EquivalenceMember lookup in planner

When planning queries to partitioned tables, we create child
EquivalenceMembers for each EquivalenceMembers which mentions the parent
partitioned table.  For partitioned tables with large numbers of
partitions, this meant we could end up with many EquivalenceMembers and
this could cause the planner to become slow due to the linear lookups
that are performed on the EquivalenceClass ec_members's List.
Effectively, the more partitions which were present, the more lookups
needed to be performed for operations such as
find_ec_member_matching_expr() during create_plan() and the more
partitions present, the longer these searches would take.

To fix this, here we adjust how we store EquivalenceMembers for
em_is_child members.  Instead of storing these directly in ec_members,
these are now stored in a new array of Lists in the EquivalenceClass
which is indexed by the relid.  When we want to find EquivalenceMembers
belonging to a certain child relation, we can narrow the search to the
array element for that relation.

To make EquivalenceMember lookup easier and to reduce the amount of code
change, this commit provides a pair of functions to allow iteration over
the EquivalenceMembers in a class which handles finding the child
members, if required.  Callers that never need to look at child members
can remain using the foreach loop over ec_members, which will now often
be faster due to only parent-level members being stored there.

The actual performance increases here are highly dependent on the number
of partitions and the query being planned.  Performance increase can be
visible with as few as 8 partitions, but the speedup is marginal for
such low numbers of partitions.  The speedups become much more visible
with a few dozen to hundreds of partitions.  Some tested queries were
around 3x faster with 256 partitions.  For users with thousands of
partitions, these are likely to become significantly faster.  Some
testing has shown planner speedups of 20x or more.

Author: Yuya Watari <watari.yuya@gmail.com>
Co-authored-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andrey Lepikhov <a.lepikhov@postgrespro.ru>
Reviewed-by: Alena Rybakina <lena.ribackina@yandex.ru>
Reviewed-by: Dmitry Dolgov <9erthalion6@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Tested-by: Thom Brown <thom@linux.com>
Tested-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Tested-by: newtglobal postgresql_contributors <postgresql_contributors@newtglobalcorp.com>
Discussion: https://postgr.es/m/CAJ2pMkZNCgoUKSE%2B_5LthD%2BKbXKvq6h2hQN8Esxpxd%2Bcxmgomg%40mail.gmail.com
---
 contrib/postgres_fdw/postgres_fdw.c       |  19 +-
 src/backend/nodes/outfuncs.c              |   2 +
 src/backend/optimizer/path/equivclass.c   | 410 ++++++++++++++++------
 src/backend/optimizer/path/indxpath.c     |   8 +-
 src/backend/optimizer/path/pathkeys.c     |  10 +-
 src/backend/optimizer/plan/analyzejoins.c |  14 +
 src/include/nodes/pathnodes.h             |  98 +++++-
 src/include/optimizer/paths.h             |   4 +
 src/tools/pgindent/typedefs.list          |   1 +
 9 files changed, 446 insertions(+), 120 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b4e0e60928b..a7e0cc9f323 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -7847,14 +7847,13 @@ conversion_error_callback(void *arg)
 EquivalenceMember *
 find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 {
-	ListCell   *lc;
-
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, rel->relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
-
 		/*
 		 * Note we require !bms_is_empty, else we'd accept constant
 		 * expressions which are not suitable for the purpose.
@@ -7908,7 +7907,10 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 		while (expr && IsA(expr, RelabelType))
 			expr = ((RelabelType *) expr)->arg;
 
-		/* Locate an EquivalenceClass member matching this expr, if any */
+		/*
+		 * Locate an EquivalenceClass member matching this expr, if any.
+		 * Ignore child members.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
@@ -7918,9 +7920,8 @@ find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
 			if (em->em_is_const)
 				continue;
 
-			/* Ignore child members */
-			if (em->em_is_child)
-				continue;
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* Match if same expression (after stripping relabel) */
 			em_expr = em->em_expr;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 557f06e344f..ceac3fd8620 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -465,7 +465,9 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 
 	WRITE_NODE_FIELD(ec_opfamilies);
 	WRITE_OID_FIELD(ec_collation);
+	WRITE_INT_FIELD(ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_members);
+	WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size);
 	WRITE_NODE_FIELD(ec_sources);
 	/* Only ec_derives_list is written; hash is not serialized. */
 	WRITE_NODE_FIELD(ec_derives_list);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 5fb2cf0daf8..441f12f6c50 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,11 +34,23 @@
 #include "utils/lsyscache.h"
 
 
+static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
+										 Expr *expr, Relids relids,
+										 JoinDomain *jdomain,
+										 EquivalenceMember *parent,
+										 Oid datatype);
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 										Expr *expr, Relids relids,
 										JoinDomain *jdomain,
-										EquivalenceMember *parent,
 										Oid datatype);
+static EquivalenceMember *add_child_eq_member(PlannerInfo *root,
+											  EquivalenceClass *ec,
+											  int ec_index, Expr *expr,
+											  Relids relids,
+											  JoinDomain *jdomain,
+											  EquivalenceMember *parent_em,
+											  Oid datatype,
+											  Index child_relid);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 												   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -314,11 +326,15 @@ process_equivalence(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		foreach(lc2, cur_ec->ec_members)
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Match constants only within the same JoinDomain (see
@@ -428,7 +444,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item2 to ec1 */
 		em2 = add_eq_member(ec1, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_min_security = Min(ec1->ec_min_security,
 								   restrictinfo->security_level);
@@ -445,7 +461,7 @@ process_equivalence(PlannerInfo *root,
 	{
 		/* Case 3: add item1 to ec2 */
 		em1 = add_eq_member(ec2, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_min_security = Min(ec2->ec_min_security,
 								   restrictinfo->security_level);
@@ -465,7 +481,9 @@ process_equivalence(PlannerInfo *root,
 
 		ec->ec_opfamilies = opfamilies;
 		ec->ec_collation = collation;
+		ec->ec_childmembers_size = 0;
 		ec->ec_members = NIL;
+		ec->ec_childmembers = NULL;
 		ec->ec_sources = list_make1(restrictinfo);
 		ec->ec_derives_list = NIL;
 		ec->ec_derives_hash = NULL;
@@ -478,9 +496,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
 		em1 = add_eq_member(ec, item1, item1_relids,
-							jdomain, NULL, item1_type);
+							jdomain, item1_type);
 		em2 = add_eq_member(ec, item2, item2_relids,
-							jdomain, NULL, item2_type);
+							jdomain, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -566,11 +584,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 }
 
 /*
- * add_eq_member - build a new EquivalenceMember and add it to an EC
+ * make_eq_member
+ *		Build a new EquivalenceMember without adding it to an EC.  If 'parent'
+ *		is NULL, the result will be a parent member, otherwise a child member.
  */
 static EquivalenceMember *
-add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
+make_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			   JoinDomain *jdomain, EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
@@ -597,11 +617,85 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 		ec->ec_has_const = true;
 		/* it can't affect ec_relids */
 	}
-	else if (!parent)			/* child members don't add to ec_relids */
+
+	return em;
+}
+
+/*
+ * add_eq_member - build a new non-child EquivalenceMember and add it to 'ec'.
+ */
+static EquivalenceMember *
+add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
+			  JoinDomain *jdomain, Oid datatype)
+{
+	EquivalenceMember *em = make_eq_member(ec, expr, relids, jdomain,
+										   NULL, datatype);
+
+	/* add to the members list */
+	ec->ec_members = lappend(ec->ec_members, em);
+
+	/* record the relids for parent members */
+	ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+
+	return em;
+}
+
+/*
+ * add_child_eq_member
+ *		Create an em_is_child=true EquivalenceMember and add it to 'ec'.
+ *
+ * 'root' is the PlannerInfo that 'ec' belongs to.
+ * 'ec' is the EquivalenceClass to add the child member to.
+ * 'ec_index' the index of 'ec' within root->eq_classes, or -1 if maintaining
+ * the RelOptInfo.eclass_indexes isn't needed.
+ * 'expr' is the em_expr for the new member.
+ * 'relids' is the 'em_relids' for the new member.
+ * 'jdomain' is the 'em_jdomain' for the new member.
+ * 'parent_em' is the parent member of the child to create.
+ * 'datatype' is the em_datatype of the new member.
+ * 'child_relid' defines which element of ec_childmembers to add this member
+ * to.  This is generally a RELOPT_OTHER_MEMBER_REL, but for set operations
+ * can be a RELOPT_BASEREL representing the set-op children.
+ */
+static EquivalenceMember *
+add_child_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index,
+					Expr *expr, Relids relids, JoinDomain *jdomain,
+					EquivalenceMember *parent_em, Oid datatype,
+					Index child_relid)
+{
+	EquivalenceMember *em;
+
+	Assert(parent_em != NULL);
+
+	/*
+	 * Allocate the array to store child members; an array of Lists indexed by
+	 * relid, or expand the existing one, if necessary.
+	 */
+	if (unlikely(ec->ec_childmembers_size < root->simple_rel_array_size))
 	{
-		ec->ec_relids = bms_add_members(ec->ec_relids, relids);
+		if (ec->ec_childmembers == NULL)
+			ec->ec_childmembers = palloc0_array(List *, root->simple_rel_array_size);
+		else
+			ec->ec_childmembers = repalloc0_array(ec->ec_childmembers, List *,
+												  ec->ec_childmembers_size,
+												  root->simple_rel_array_size);
+
+		ec->ec_childmembers_size = root->simple_rel_array_size;
+	}
+
+	em = make_eq_member(ec, expr, relids, jdomain, parent_em, datatype);
+
+	/* add member to the ec_childmembers List for the given child_relid */
+	ec->ec_childmembers[child_relid] = lappend(ec->ec_childmembers[child_relid], em);
+
+	/* Record this EC index for the child rel */
+	if (ec_index >= 0)
+	{
+		RelOptInfo *child_rel = root->simple_rel_array[child_relid];
+
+		child_rel->eclass_indexes =
+			bms_add_member(child_rel->eclass_indexes, ec_index);
 	}
-	ec->ec_members = lappend(ec->ec_members, em);
 
 	return em;
 }
@@ -672,7 +766,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	foreach(lc1, root->eq_classes)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *cur_em;
 
 		/*
 		 * Never match to a volatile EC, except when we are looking at another
@@ -687,10 +782,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		if (!equal(opfamilies, cur_ec->ec_opfamilies))
 			continue;
 
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
 			/*
 			 * Ignore child members unless they match the request.
 			 */
@@ -725,7 +819,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	newec = makeNode(EquivalenceClass);
 	newec->ec_opfamilies = list_copy(opfamilies);
 	newec->ec_collation = collation;
+	newec->ec_childmembers_size = 0;
 	newec->ec_members = NIL;
+	newec->ec_childmembers = NULL;
 	newec->ec_sources = NIL;
 	newec->ec_derives_list = NIL;
 	newec->ec_derives_hash = NULL;
@@ -747,7 +843,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 	expr_relids = pull_varnos(root, (Node *) expr);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  jdomain, NULL, opcintype);
+						  jdomain, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -821,15 +917,16 @@ find_ec_member_matching_expr(EquivalenceClass *ec,
 							 Expr *expr,
 							 Relids relids)
 {
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/* We ignore binary-compatible relabeling on both ends */
 	while (expr && IsA(expr, RelabelType))
 		expr = ((RelabelType *) expr)->arg;
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		Expr	   *emexpr;
 
 		/*
@@ -898,7 +995,8 @@ find_computable_ec_member(PlannerInfo *root,
 						  bool require_parallel_safe)
 {
 	List	   *exprvars;
-	ListCell   *lc;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *em;
 
 	/*
 	 * Pull out the Vars and quasi-Vars present in "exprs".  In the typical
@@ -912,9 +1010,9 @@ find_computable_ec_member(PlannerInfo *root,
 							   PVC_INCLUDE_PLACEHOLDERS |
 							   PVC_INCLUDE_CONVERTROWTYPES);
 
-	foreach(lc, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, relids);
+	while ((em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
 		List	   *emvars;
 		ListCell   *lc2;
 
@@ -1193,6 +1291,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		return;
 	}
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Find the constant member to use.  We prefer an actual constant to
 	 * pseudo-constants (such as Params), because the constraint exclusion
@@ -1219,7 +1320,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		Oid			eq_op;
 		RestrictInfo *rinfo;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 		if (cur_em == const_em)
 			continue;
 		eq_op = select_equality_operator(ec,
@@ -1283,12 +1385,17 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	prev_ems = (EquivalenceMember **)
 		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
 
+	/* We don't expect any children yet */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach(lc, ec->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		int			relid;
 
-		Assert(!cur_em->em_is_child);	/* no children yet */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
+
 		if (!bms_get_singleton_member(cur_em->em_relids, &relid))
 			continue;
 		Assert(relid < root->simple_rel_array_size);
@@ -1621,7 +1728,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 	List	   *new_members = NIL;
 	List	   *outer_members = NIL;
 	List	   *inner_members = NIL;
-	ListCell   *lc1;
+	EquivalenceMemberIterator it;
+	EquivalenceMember *cur_em;
 
 	/*
 	 * First, scan the EC to identify member values that are computable at the
@@ -1632,10 +1740,9 @@ 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.
 	 */
-	foreach(lc1, ec->ec_members)
+	setup_eclass_member_iterator(&it, ec, join_relids);
+	while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 	{
-		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
-
 		/*
 		 * We don't need to check explicitly for child EC members.  This test
 		 * against join_relids will cause them to be ignored except when
@@ -1668,6 +1775,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		Oid			best_eq_op = InvalidOid;
 		int			best_score = -1;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		foreach(lc1, outer_members)
 		{
@@ -1742,6 +1850,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 		List	   *old_members = list_concat(outer_members, inner_members);
 		EquivalenceMember *prev_em = NULL;
 		RestrictInfo *rinfo;
+		ListCell   *lc1;
 
 		/* For now, arbitrarily take the first old_member as the one to use */
 		if (old_members)
@@ -1749,7 +1858,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
 
 		foreach(lc1, new_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
+			cur_em = (EquivalenceMember *) lfirst(lc1);
 
 			if (prev_em != NULL)
 			{
@@ -2188,6 +2297,9 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		bool		match;
 		ListCell   *lc2;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2205,7 +2317,8 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		{
 			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-			Assert(!cur_em->em_is_child);	/* no children yet */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 			if (equal(outervar, cur_em->em_expr))
 			{
 				match = true;
@@ -2303,6 +2416,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		ListCell   *lc2;
 		int			coal_idx = -1;
 
+		/* We don't expect any children yet */
+		Assert(cur_ec->ec_childmembers == NULL);
+
 		/* Ignore EC unless it contains pseudoconstants */
 		if (!cur_ec->ec_has_const)
 			continue;
@@ -2332,7 +2448,9 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 		foreach(lc2, cur_ec->ec_members)
 		{
 			coal_em = (EquivalenceMember *) lfirst(lc2);
-			Assert(!coal_em->em_is_child);	/* no children yet */
+
+			/* Child members should not exist in ec_members */
+			Assert(!coal_em->em_is_child);
 			if (IsA(coal_em->em_expr, CoalesceExpr))
 			{
 				CoalesceExpr *cexpr = (CoalesceExpr *) coal_em->em_expr;
@@ -2461,6 +2579,13 @@ rebuild_eclass_attr_needed(PlannerInfo *root)
 	{
 		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
 
+		/*
+		 * We don't expect any EC child members to exist at this point. Ensure
+		 * that's the case, otherwise, we might be getting asked to do
+		 * something this function hasn't been coded for.
+		 */
+		Assert(ec->ec_childmembers == NULL);
+
 		/* Need do anything only for a multi-member, no-const EC. */
 		if (list_length(ec->ec_members) > 1 && !ec->ec_has_const)
 		{
@@ -2546,12 +2671,13 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2, Oid opfamily)
 			!list_member_oid(ec->ec_opfamilies, opfamily))
 			continue;
 
+		/* Ignore children here */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 			if (equal(item1, em->em_expr))
 				item1member = true;
 			else if (equal(item2, em->em_expr))
@@ -2615,15 +2741,18 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
 		/* Never match to a volatile EC */
 		if (ec->ec_has_volatile)
 			continue;
-		/* It's okay to consider "broken" ECs here, see exprs_known_equal */
 
+		/*
+		 * It's okay to consider "broken" ECs here, see exprs_known_equal.
+		 * Ignore children here.
+		 */
 		foreach(lc2, ec->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 			Var		   *var;
 
-			if (em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
 
 			/* EM must be a Var, possibly with RelabelType */
 			var = (Var *) em->em_expr;
@@ -2721,7 +2850,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2734,29 +2862,13 @@ add_child_rel_equivalences(PlannerInfo *root,
 		/* Sanity check eclass_indexes only contain ECs for parent_rel */
 		Assert(bms_is_subset(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach_node(EquivalenceMember, cur_em, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.  Otherwise, if some original
-			 * member expression references more than one appendrel, we'd get
-			 * an O(N^2) explosion of useless derived expressions for
-			 * combinations of children.  (But add_child_join_rel_equivalences
-			 * may add targeted combinations for partitionwise-join purposes.)
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * Consider only members that reference and can be computed at
@@ -2801,12 +2913,15 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
-
-				/* Record this EC index for the child rel */
-				child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
+				add_child_eq_member(root,
+									cur_ec,
+									i,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									child_rel->relid);
 			}
 		}
 	}
@@ -2853,7 +2968,6 @@ 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);
-		int			num_members;
 
 		/*
 		 * If this EC contains a volatile expression, then generating child
@@ -2866,25 +2980,13 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 		/* Sanity check on get_eclass_indexes_for_relids result */
 		Assert(bms_overlap(top_parent_relids, cur_ec->ec_relids));
 
-		/*
-		 * We don't use foreach() here because there's no point in scanning
-		 * newly-added child members, so we can stop after the last
-		 * pre-existing EC member.
-		 */
-		num_members = list_length(cur_ec->ec_members);
-		for (int pos = 0; pos < num_members; pos++)
+		foreach_node(EquivalenceMember, cur_em, cur_ec->ec_members)
 		{
-			EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
-
 			if (cur_em->em_is_const)
 				continue;		/* ignore consts here */
 
-			/*
-			 * We consider only original EC members here, not
-			 * already-transformed child members.
-			 */
-			if (cur_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!cur_em->em_is_child);
 
 			/*
 			 * We may ignore expressions that reference a single baserel,
@@ -2929,9 +3031,35 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				(void) add_eq_member(cur_ec, child_expr, new_relids,
-									 cur_em->em_jdomain,
-									 cur_em, cur_em->em_datatype);
+				/*
+				 * Add new child member to the EquivalenceClass.  Because this
+				 * is a RELOPT_OTHER_JOINREL which has multiple component
+				 * relids, there is no ideal place to store these members in
+				 * the class.  Ordinarily, child members are stored in the
+				 * ec_childmembers[] array element corresponding to their
+				 * relid, however, here we have multiple component relids, so
+				 * there's no single ec_childmembers[] array element to store
+				 * this member.  So that we still correctly find this member
+				 * in loops iterating over an EquivalenceMemberIterator, we
+				 * opt to store the member in the ec_childmembers array in
+				 * only the first component relid slot of the array.  This
+				 * allows the member to be found, providing callers of
+				 * setup_eclass_member_iterator() specify all the component
+				 * relids for the RELOPT_OTHER_JOINREL, which they do.  If we
+				 * opted to store the member in each ec_childmembers[] element
+				 * for all the component relids, then that would just result
+				 * in eclass_member_iterator_next() finding the member
+				 * multiple times, which is a waste of effort.
+				 */
+				add_child_eq_member(root,
+									cur_ec,
+									-1,
+									child_expr,
+									new_relids,
+									cur_em->em_jdomain,
+									cur_em,
+									cur_em->em_datatype,
+									bms_next_member(child_joinrel->relids, -1));
 			}
 		}
 	}
@@ -2978,14 +3106,18 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 		 * We can safely pass the parent member as the first member in the
 		 * ec_members list as this is added first in generate_union_paths,
 		 * likewise, the JoinDomain can be that of the initial member of the
-		 * Pathkey's EquivalenceClass.
+		 * Pathkey's EquivalenceClass.  We pass -1 for ec_index since we
+		 * maintain the eclass_indexes for the child_rel after the loop.
 		 */
-		add_eq_member(pk->pk_eclass,
-					  tle->expr,
-					  child_rel->relids,
-					  parent_em->em_jdomain,
-					  parent_em,
-					  exprType((Node *) tle->expr));
+		add_child_eq_member(root,
+							pk->pk_eclass,
+							-1,
+							tle->expr,
+							child_rel->relids,
+							parent_em->em_jdomain,
+							parent_em,
+							exprType((Node *) tle->expr),
+							child_rel->relid);
 
 		lc2 = lnext(setop_pathkeys, lc2);
 	}
@@ -3000,6 +3132,85 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel,
 											  list_length(root->eq_classes) - 1);
 }
 
+/*
+ * setup_eclass_member_iterator
+ *	  Setup an EquivalenceMemberIterator 'it' to iterate over all parent
+ *	  EquivalenceMembers and child members belonging to the given 'ec'.
+ *
+ * This iterator returns:
+ *	- All parent members stored directly in ec_members for 'ec', and;
+ *	- Any child member added to the given ec by add_child_eq_member() where
+ *	  the child_relid specified in the add_child_eq_member() call is a member
+ *	  of the 'child_relids' parameter.
+ *
+ * Note:
+ * The given 'child_relids' must remain allocated and not be changed for the
+ * lifetime of the iterator.
+ *
+ * Parameters:
+ *	'it' is a pointer to the iterator to set up.  Normally stack allocated.
+ *	'ec' is the EquivalenceClass from which to iterate members for.
+ *	'child_relids' is the relids to return child members for.
+ */
+void
+setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+							 EquivalenceClass *ec, Relids child_relids)
+{
+	it->ec = ec;
+	/* no need to set this if the class has no child members array set */
+	it->child_relids = ec->ec_childmembers != NULL ? child_relids : NULL;
+	it->current_relid = -1;
+	it->current_list = ec->ec_members;
+	it->current_cell = list_head(it->current_list);
+}
+
+/*
+ * eclass_member_iterator_next
+ *	  Get the next EquivalenceMember from the EquivalenceMemberIterator 'it',
+ *	  as setup by setup_eclass_member_iterator().  NULL is returned if there
+ *	  are no members left, after which callers must not call
+ *	  eclass_member_iterator_next() again for the given iterator.
+ */
+EquivalenceMember *
+eclass_member_iterator_next(EquivalenceMemberIterator *it)
+{
+	while (it->current_list != NULL)
+	{
+		while (it->current_cell != NULL)
+		{
+			EquivalenceMember *em;
+
+	nextcell:
+			em = lfirst_node(EquivalenceMember, it->current_cell);
+			it->current_cell = lnext(it->current_list, it->current_cell);
+			return em;
+		}
+
+		/* Search for the next list to return members from */
+		while ((it->current_relid = bms_next_member(it->child_relids, it->current_relid)) > 0)
+		{
+			/*
+			 * Be paranoid in case we're given relids above what we've sized
+			 * the ec_childmembers array to.
+			 */
+			if (it->current_relid >= it->ec->ec_childmembers_size)
+				return NULL;
+
+			it->current_list = it->ec->ec_childmembers[it->current_relid];
+
+			/* If there are members in this list, use it. */
+			if (it->current_list != NIL)
+			{
+				/* point current_cell to the head of this list */
+				it->current_cell = list_head(it->current_list);
+				goto nextcell;
+			}
+		}
+		return NULL;
+	}
+
+	return NULL;
+}
 
 /*
  * generate_implied_equalities_for_column
@@ -3052,6 +3263,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 	while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
 	{
 		EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+		EquivalenceMemberIterator it;
 		EquivalenceMember *cur_em;
 		ListCell   *lc2;
 
@@ -3075,14 +3287,12 @@ 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.)
 		 */
-		cur_em = NULL;
-		foreach(lc2, cur_ec->ec_members)
+		setup_eclass_member_iterator(&it, cur_ec, rel->relids);
+		while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
 		{
-			cur_em = (EquivalenceMember *) lfirst(lc2);
 			if (bms_equal(cur_em->em_relids, rel->relids) &&
 				callback(root, rel, cur_ec, cur_em, callback_arg))
 				break;
-			cur_em = NULL;
 		}
 
 		if (!cur_em)
@@ -3090,7 +3300,7 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 
 		/*
 		 * Found our match.  Scan the other EC members and attempt to generate
-		 * joinclauses.
+		 * joinclauses.  Ignore children here.
 		 */
 		foreach(lc2, cur_ec->ec_members)
 		{
@@ -3098,8 +3308,8 @@ generate_implied_equalities_for_column(PlannerInfo *root,
 			Oid			eq_op;
 			RestrictInfo *rinfo;
 
-			if (other_em->em_is_child)
-				continue;		/* ignore children here */
+			/* Child members should not exist in ec_members */
+			Assert(!other_em->em_is_child);
 
 			/* Make sure it'll be a join to a different rel */
 			if (other_em == cur_em ||
@@ -3312,13 +3522,15 @@ eclass_useful_for_merging(PlannerInfo *root,
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
-	/* To join, we need a member not in the given rel */
+	/*
+	 * To join, we need a member not in the given rel.  Ignore children here.
+	 */
 	foreach(lc, eclass->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 
-		if (cur_em->em_is_child)
-			continue;			/* ignore children here */
+		/* Child members should not exist in ec_members */
+		Assert(!cur_em->em_is_child);
 
 		if (!bms_overlap(cur_em->em_relids, relids))
 			return true;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 4cabb358abc..601354ea3e0 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3755,7 +3755,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
 		bool		found = false;
-		ListCell   *lc2;
+		EquivalenceMemberIterator it;
+		EquivalenceMember *member;
 
 
 		/* Pathkey must request default sort order for the target opfamily */
@@ -3774,9 +3775,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		 * be considered to match more than one pathkey list, which is OK
 		 * here.  See also get_eclass_for_sort_expr.)
 		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
+		setup_eclass_member_iterator(&it, pathkey->pk_eclass,
+									 index->rel->relids);
+		while ((member = eclass_member_iterator_next(&it)) != NULL)
 		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
 
 			/* No possibility of match if it references other relations */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 981802d7b9d..8b04d40d36d 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1143,6 +1143,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 			int			best_score = -1;
 			ListCell   *j;
 
+			/* Ignore children here */
 			foreach(j, sub_eclass->ec_members)
 			{
 				EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
@@ -1151,8 +1152,8 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				Oid			sub_expr_coll = sub_eclass->ec_collation;
 				ListCell   *k;
 
-				if (sub_member->em_is_child)
-					continue;	/* ignore children here */
+				/* Child members should not exist in ec_members */
+				Assert(!sub_member->em_is_child);
 
 				foreach(k, subquery_tlist)
 				{
@@ -1709,8 +1710,11 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
+			/* Child members should not exist in ec_members */
+			Assert(!em->em_is_child);
+
 			/* Potential future join partner? */
-			if (!em->em_is_const && !em->em_is_child &&
+			if (!em->em_is_const &&
 				!bms_overlap(em->em_relids, joinrel->relids))
 				score++;
 		}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index ae20691ca91..6b58567f511 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -710,6 +710,13 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
 		ec->ec_relids = adjust_relid_set(ec->ec_relids,
 										 sjinfo->ojrelid, subst);
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise, we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	/*
 	 * Fix up the member expressions.  Any non-const member that ends with
 	 * empty em_relids must be a Var or PHV of the removed relation.  We don't
@@ -1509,6 +1516,13 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
 	List	   *new_members = NIL;
 	List	   *new_sources = NIL;
 
+	/*
+	 * We don't expect any EC child members to exist at this point.  Ensure
+	 * that's the case, otherwise, we might be getting asked to do something
+	 * this function hasn't been coded for.
+	 */
+	Assert(ec->ec_childmembers == NULL);
+
 	foreach_node(EquivalenceMember, em, ec->ec_members)
 	{
 		bool		is_redundant = false;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 4c466f76778..a09a00a7456 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1414,6 +1414,24 @@ typedef struct JoinDomain
  * In contrast, ec_sources holds equality clauses that appear directly in the
  * query. These are typically few and do not require a hash table for lookup.
  *
+ * 'ec_members' is a List of all !em_is_child EquivalenceMembers in the class.
+ * EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and RELOPT_OTHER_JOINREL
+ * relations are stored in the 'ec_childmembers' array in the index
+ * corresponding to the relid, or first component relid in the case of
+ * RELOPT_OTHER_JOINRELs.  'ec_childmembers' is NULL if the class has no child
+ * EquivalenceMembers.
+ *
+ * For code wishing to look at EquivalenceMembers, if only parent-level
+ * members are needed, then a simple foreach loop over ec_members is
+ * sufficient.  When child members are also required, it is best to use the
+ * functionality provided by EquivalenceMemberIterator.  This visits all
+ * parent members and only the relevant child members.  The reason for this
+ * is that large numbers of child EquivalenceMembers can exist in queries to
+ * partitioned tables with many partitions.  The functionality provided by
+ * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
+ * which belong to specific child relids.  See the header comments for
+ * EquivalenceMemberIterator below for further details.
+ *
  * NB: if ec_merged isn't NULL, this class has been merged into another, and
  * should be ignored in favor of using the pointed-to class.
  *
@@ -1431,7 +1449,9 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
+	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_members;		/* list of EquivalenceMembers */
+	List	  **ec_childmembers;	/* array of Lists of child members */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1465,12 +1485,17 @@ typedef struct EquivalenceClass
  * child when necessary to build a MergeAppend path for the whole appendrel
  * tree.  An em_is_child member has no impact on the properties of the EC as a
  * whole; in particular the EC's ec_relids field does NOT include the child
- * relation.  An em_is_child member should never be marked em_is_const nor
- * cause ec_has_const or ec_has_volatile to be set, either.  Thus, em_is_child
- * members are not really full-fledged members of the EC, but just reflections
- * or doppelgangers of real members.  Most operations on EquivalenceClasses
- * should ignore em_is_child members, and those that don't should test
- * em_relids to make sure they only consider relevant members.
+ * relation.  em_is_child members aren't stored in the ec_members List of the
+ * EC and instead they're stored and indexed by the relids of the child
+ * relation they represent in ec_childmembers.  An em_is_child member
+ * should never be marked em_is_const nor cause ec_has_const or
+ * ec_has_volatile to be set, either.  Thus, em_is_child members are not
+ * really full-fledged members of the EC, but just reflections or
+ * doppelgangers of real members.  Most operations on EquivalenceClasses
+ * should ignore em_is_child members by only inspecting members in the
+ * ec_members list.  Callers that require inspecting child members should do
+ * so using an EquivalenceMemberIterator and should test em_relids to make
+ * sure they only consider relevant members.
  *
  * em_datatype is usually the same as exprType(em_expr), but can be
  * different when dealing with a binary-compatible opfamily; in particular
@@ -1493,6 +1518,67 @@ typedef struct EquivalenceMember
 	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
 } EquivalenceMember;
 
+/*
+ * EquivalenceMemberIterator
+ *
+ * EquivalenceMemberIterator allows efficient access to sets of
+ * EquivalenceMembers for callers which require access to child members.
+ * Because partitioning workloads can result in large numbers of child
+ * members, the child members are not stored in the EquivalenceClass's
+ * ec_members List.  Instead, these are stored in the EquivalenceClass's
+ * ec_childmembers array of Lists.  The functionality provided by
+ * EquivalenceMemberIterator aims to provide efficient access to parent
+ * members and child members belonging to specific child relids.
+ *
+ * Currently, there is only one way to initialize and iterate over an
+ * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
+ * and eclass_member_iterator_next functions.  The iterator object is
+ * generally a local variable which is passed by address to
+ * setup_eclass_member_iterator.  The calling function defines which
+ * EquivalenceClass the iterator should be looking at and which child
+ * relids to also return members for.  child_relids can be passed as NULL, but
+ * the caller may as well just perform a foreach loop over ec_members as only
+ * parent-level members will be returned in that case.
+ *
+ * When calling the next function on an EquivalenceMemberIterator, all
+ * parent-level EquivalenceMembers are returned first, followed by all child
+ * members for the specified 'child_relids' for all child members which were
+ * indexed by any of the specified 'child_relids' in add_child_eq_member().
+ *
+ * Code using the iterator method of finding EquivalenceMembers will generally
+ * always want to ensure the returned member matches their search criteria
+ * rather than relying on the filtering to be done for them as all parent
+ * members are returned and for members belonging to RELOPT_OTHER_JOINREL
+ * rels, the member's em_relids may be a superset of the specified
+ * 'child_relids'.
+ *
+ * The most common way to use this iterator is as follows:
+ * -----
+ * EquivalenceMemberIterator		it;
+ * EquivalenceMember			   *em;
+ *
+ * setup_eclass_member_iterator(&it, ec, child_relids);
+ * while ((em = eclass_member_iterator_next(&it)) != NULL)
+ * {
+ *		...
+ * }
+ * -----
+ * It is not valid to call eclass_member_iterator_next() after it has returned
+ * NULL for any given EquivalenceMemberIterator.  Individual fields within
+ * the EquivalenceMemberIterator struct must not be accessed by callers.
+ */
+typedef struct
+{
+	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
+	int			current_relid;	/* Current relid position within 'relids'. -1
+								 * when still looping over ec_members and -2
+								 * at the end of iteration */
+	Relids		child_relids;	/* Relids of child relations of interest.
+								 * Non-child rels are ignored */
+	ListCell   *current_cell;	/* Next cell to return within current_list */
+	List	   *current_list;	/* Current list of members being returned */
+} EquivalenceMemberIterator;
+
 /*
  * PathKeys
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b1a76816442..a48c9721797 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -183,6 +183,10 @@ extern void add_setop_child_rel_equivalences(PlannerInfo *root,
 											 RelOptInfo *child_rel,
 											 List *child_tlist,
 											 List *setop_pathkeys);
+extern void setup_eclass_member_iterator(EquivalenceMemberIterator *it,
+										 EquivalenceClass *ec,
+										 Relids child_relids);
+extern EquivalenceMember *eclass_member_iterator_next(EquivalenceMemberIterator *it);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 													RelOptInfo *rel,
 													ec_matches_callback_type callback,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index f7ba0ec809e..87e6da8d25e 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -714,6 +714,7 @@ EphemeralNamedRelationMetadata
 EphemeralNamedRelationMetadataData
 EquivalenceClass
 EquivalenceMember
+EquivalenceMemberIterator
 ErrorContextCallback
 ErrorData
 ErrorSaveContext
-- 
2.43.0

#129Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#128)
Re: [PoC] Reducing planning time when tables have many partitions

David Rowley <dgrowleyml@gmail.com> writes:

I certainly could get on board with renaming if there's consensus to
do so. I don't think that's going to happen in the next 9 hours. Is it
worth pushing this out to v19 because we can't agree on the name of a
struct field? I'd be disappointed if we miss this because of something
so trivial.

Renaming functions or struct fields is hardly something that's forbidden
post-feature-freeze. I think you could go ahead while agreeing to
change that stuff later if there's consensus for it. At this point it
seems much more useful to get some buildfarm mileage on the patch.

(FWIW, I buy your argument that there's a limit to how much complexity
we should shove into the iterator.)

regards, tom lane

#130Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: David Rowley (#128)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, Apr 8, 2025 at 8:30 AM David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 8 Apr 2025 at 04:54, Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

- foreach(lc2, cur_ec->ec_members)
+ setup_eclass_member_iterator(&it, cur_ec, rel);
+ while ((cur_em = eclass_member_iterator_next(&it)) != NULL)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
-
/*
* Ignore child members unless they match the request.
*/

Can this step be executed in the iterator itself?

Yes, all the filtering requested *could* be done within the iterator.
The problem with doing that is that there's not even nearly a single
pattern to what callers need. Here's a summary of all 7 users of it:

1. get_eclass_for_sort_expr: CHILD members must have em_relids EQUAL
to rel (TYPE 1)
2. generate_join_implied_equalities_normal: ALL members must be a
SUBSET of join_relids (TYPE 2)
3. generate_implied_equalities_for_column: ALL members must have
em_relids EQUAL to rel->relids (TYPE 3)
4. find_em_for_rel: ALL members must be a SUBSET of rel->relids (TYPE 2)
5. find_ec_member_matching_expr: CHILD members must have em_relids
SUBSET of relids (TYPE 4)
6. find_computable_ec_member: CHILD members must have em_relids SUBSET
of relids (TYPE 4)
7. match_pathkeys_to_index: ALL members must have em_relids EQUAL to
index->rel->relids (TYPE 3)

So, 4 distinct requirements and only types 3 and 4 are repeated.
Certainly, we could code up the iterator to handle all those different
requirements, but would that code be easy to follow? Do you think 1
iterator should handle all those requirements with a set of IF
statements? I think that would be more difficult to read than how the
patch has it now.

An alternative that I did consider, and even put a comment on the 2nd
paragraph of the header comment for EquivalenceMemberIterator about,
is the possibility of having multiple iterators for different
purposes. That would save the spaghetti code of IF statements doing it
in 1 iterator and alleviate the performance overhead of the spaghetti
code too. The problem is that there's still not a common pattern
that's followed often enough. If I wrote 4 iterators, I'd need to give
them meaningful names. Names like
eclass_member_iterator_children_with_subset_of_next are a too long and
if I was writing new code that was to call some function like that,
I'd probably be happier just doing the filtering locally than trying
to find which of the 4 iterators is the one I want.

Thanks for listing all the patterns. Creating four different iterators
is going to affect functionality and might require duplicate code. But
each of the patterns is using exactly one BMS operation on em_relids
and relids being used as search criteria. That BMS operation/function
pointer can be passed to the iterator. I have not looked into whether
each of those BMS functions return boolean or not OR whether all the
functions take arguments in the same order. But, those things can be
fixed. However, given that the feature freeze deadline is so close,
it's fine to do it later either during the beta phase or in PG 19. The
speed up would be small enough to be noticeable in PG 18 given this
and other improvements that have gone in.

* the iterator
* will return members where the em_relids overlaps the child_relids specified
* when calling setup_eclass_member_iterator(). The caller may wish to ensure
* the em_relids is a subset of the relids they're searching for.

em_relids should be subset of or equal to child_relids specified,
otherwise it may be missed as explained above. We should rephrase the
sentence as " The caller should ensure ...". "May wish to" indicates
an optional thing, but it doesn't look optional to me.

That comment does not know what the caller needs to do. That's up to
the caller. If every caller needed to do that, we might as well just
filter non-matching ones out in the iterator itself.

I did rewrite that comment quite a bit in the attached version as I
still wasn't quite happy with the wording. I still didn't make any
assumptions about what the caller wants, however.

I have tried to improve it further in the attached diff. Please accept
the diff if you find it useful and if time permits.

@@ -725,7 +819,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
newec = makeNode(EquivalenceClass);
newec->ec_opfamilies = list_copy(opfamilies);
newec->ec_collation = collation;
+ newec->ec_childmembers_size = 0;
newec->ec_members = NIL;

How about renaming ec_members to ec_parent_members to make it
explicit. That might break extensions that use ec_members but that way
they would know that the purpose of this list has changed and it will
nudge them to use ec_parent_member of EC iterator appropriately.

The reason I didn't rename that field is mainly because I'm fine with
the current name. In my view, I think of parent members as normal
members of the class and only the child members are 2nd class members.
You can see evidence of that line of thought in the patch with
add_eq_member() not being called add_parent_eq_member().

I think the distinction between parent and child is useful. So I will
still suggest renaming the field but it can be done
post-feature-freeze. Tom seems to be fine with that per his last email
on the thread. If we do earlier in the beta cycle like in April
itself, that will give enough time for the extension authors to adjust
their code, if necessary.

+ /*
+ * We don't expect any EC child members to exist at this point. Ensure
+ * that's the case, otherwise we might be getting asked to do something
+ * this function hasn't been coded for.
+ */
+ Assert(ec->ec_childmembers == NULL);
+

In find_em_for_rel_target() we have Assert(!em->em_is_child), but here
it's different Assert for the same purpose. It would be good to be
consistent in all such places either Asserting !em->em_is_child in the
loop or Assert(ec->ec_childmembers) OR both as you have done in
process_equivalence().

I think this might not be obvious when reading the patch, but I think
where the confusion might be starting is that
"Assert(!cur_em->em_is_child);" isn't asserting what it used to. In
unpatched master, there are a few places that do that to verify the
function is never called after child members have been added. The
patch changes the meaning of this Assert. The comment was updated in a
bid to try to make this more obvious. The Assert's new purpose is to
ensure no child members snuck into the ec_members list. For the
functions that fundamentally don't expect children to exist yet, I've
added an Assert(cur_ec->ec_childmembers == NULL) to ensure that
remains true. I believe I'm consistent with that and I believe that's
a worthwhile assert to make.

Occasionally, there are Asserts to check there are no child members in
ec_members elsewhere in places where ec_childmembers might contain
children. I believe all the Asserts doing this were all converted from
an "if (ec->em_is_child) continue;" statement. I don't feel the need
to add this assert everywhere that we loop over ec_members. It does
not seem likely that we'd get an em_is_child member in ec_members as
there's only a single place where the ec_members list is adjusted.
I'd be more easily convinced we can just delete all those Asserts than
I could be to add more.

Thanks for the clarification. That's very useful

Attached diff also brings ec_childmembers_size closer to
ec_childmembers - usual practice of keeping the array and its size
together.

Thanks for all your last minute work. I think this is good to go for PG 18.

--
Best Wishes,
Ashutosh Bapat

Attachments:

some_edits.txttext/plain; charset=US-ASCII; name=some_edits.txtDownload
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a09a00a7456..056911b2165 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1449,9 +1449,9 @@ typedef struct EquivalenceClass
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
 	Oid			ec_collation;	/* collation, if datatypes are collatable */
-	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_members;		/* list of EquivalenceMembers */
 	List	  **ec_childmembers;	/* array of Lists of child members */
+	int			ec_childmembers_size;	/* # elements in ec_childmembers */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives_list;	/* list of derived RestrictInfos */
 	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
@@ -1549,8 +1549,9 @@ typedef struct EquivalenceMember
  * always want to ensure the returned member matches their search criteria
  * rather than relying on the filtering to be done for them as all parent
  * members are returned and for members belonging to RELOPT_OTHER_JOINREL
- * rels, the member's em_relids may be a superset of the specified
- * 'child_relids'.
+ * rels, the member's em_relids will contain one of the relids specified in
+ * 'child_relids', but it may not match the exact criteria the caller is looking
+ * for.
  *
  * The most common way to use this iterator is as follows:
  * -----
#131David Rowley
dgrowleyml@gmail.com
In reply to: Ashutosh Bapat (#130)
1 attachment(s)
Re: [PoC] Reducing planning time when tables have many partitions

On Tue, 8 Apr 2025 at 17:41, Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:

Thanks for listing all the patterns. Creating four different iterators
is going to affect functionality and might require duplicate code. But
each of the patterns is using exactly one BMS operation on em_relids
and relids being used as search criteria. That BMS operation/function
pointer can be passed to the iterator. I have not looked into whether
each of those BMS functions return boolean or not OR whether all the
functions take arguments in the same order. But, those things can be
fixed. However, given that the feature freeze deadline is so close,
it's fine to do it later either during the beta phase or in PG 19. The
speed up would be small enough to be noticeable in PG 18 given this
and other improvements that have gone in.

I'll be happy to see further speedups proposed here. I doubt we'll see
much in the same league as this one, but a % here and there are often
welcome, providing the added complexity is acceptable.

I think the distinction between parent and child is useful. So I will
still suggest renaming the field but it can be done
post-feature-freeze. Tom seems to be fine with that per his last email
on the thread. If we do earlier in the beta cycle like in April
itself, that will give enough time for the extension authors to adjust
their code, if necessary.

That's fine for me. I did grep the Citus codebase earlier to look for
ec_member usages and I did see one. So, let's see who complains first.

Attached diff also brings ec_childmembers_size closer to
ec_childmembers - usual practice of keeping the array and its size
together.

The reason I put it where it was is because it was filling a 4-byte
hole in the struct. I'd not tested any performance with it located in
a place that would cause the struct to be enlarged. IMO, we should be
allowed some flexibility here for such small struct. I don't think
having it 1 field up is confusing. We could swap ec_members and
ec_childmembers positions if you feel strongly.

I've pushed the patch now. Thanks for all the reviews of my adjustments.

Thanks to Yuya for persisting on this for so many years. I was
impressed with that persistence and also with your very detailed and
easy to understand performance benchmark results. This feels like
(all going well) it's making v18 is big win for the big partitioning
users of Postgres. I kind of feel the "up to a few thousand partitions
fairly well" in [1]https://www.postgresql.org/docs/current/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE-BEST-PRACTICES has been abused by many or taken without the
caveat of "the query planner to prune all but a small number of
partitions" over the years and the "Never just assume that more
partitions are better than fewer partitions, nor vice-versa." has been
ignored by too many. It's good that the people pushing these limits
will no longer be getting as big an unwelcome surprise now (I hope).
Or, at least, onto the next bottleneck. Maybe relcache :)

I've attached a dump of the performance tests of v42 I did an hour or
so ago with up to 8K partitions. That's where I got the 60x numbers I
hinted at in the commit message.

David

[1]: https://www.postgresql.org/docs/current/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE-BEST-PRACTICES

Attachments:

bench_results_v42.sql.bz2application/x-compressed; name=bench_results_v42.sql.bz2Download
BZh91AY&SY����0�_�@0@��?������`���5T5���!#j =*��=^D-��8�(8���z'�Q�U�xs\��z<<�p�mP��������z>����:}�xh�Sm:�����H�D�G�K�p�v06.�n&�(�ve�w�pt�`Zvh
j���BP	;s��hP�I,
P��fB��yUmQ@��A���P��RT�IM�4&Q�0�H��G�FD�A)�����I&�"*R*��T���4�d�` ��dd4�������ZN�YY��f{��s++31��c�Neeff1��o_���zG���/N~@�?���i��4ZE�C �"U&`�'?++31��cy��?���(+��)F"a!ICVfc�����}�:$I{�}5]���Nu�\�?�������` ��	�j9�LD�%��`�%
AY��f{�����>�]zs���9�����g��������M���G�'>VVfc��1�I(�>���7qB,%@�fc���H��Iow^o��VVfc������o|�����,���32��*c(��+$����T�2�Yde���fa��eaXQ�Y���f���)����2&L2&F&Q1�I��dd���)3&(�,2J2� ��2�0�����,��0�����k3C0�	�c(F)�+���~)Y������C������fg!�VEdeff33��s�H�;��cE B��i��4ZE�C �"U������VFVfc39�I��C@���0�8����N??"�2�3����_��@H%���q4�%�.��1������G}z~��n�{��[?�~"�2�3�����I>m%��G���������fr>��:��I�wq��W������fr/>��N�{���[���?��������DX��$������������G�������t�<%���7ss-l�L���I���p�0����-n��n�I�"�371�dCJIfo[w���f��5�B!�l��8�E�Z$2	�%R`�8)�(+��)F"a!V$Q�ZH�1�H�PL��-�Fz'�R�LI�!.4R-�:����t$t�$�a'�����n^�P�$���Um@h�a@TIfg��c0�3��e�e���/{�������u��}����}��������{��2�e�gY�B�R�{���[��t"�h �#�
5�0P��������CH2K��x{�I"B^H�H������A�QH�O3<WYs �����H��bb��*���J.�������71��8i�9�u���I��G����*�0,Ywe�e�e�L���3q�9��w�qZ�:�������{���\����*&���!��(�/V!���V����f�I���"���YI�z{�o
>y����w���{��{��@��F3e�����~��"��I'������}�je���i<`���{��m����Z�N�D�������I;5���!!#���q����s�s%�nx�����qs�y=���o&Nk}�;��
B�� ����:8���L\�
 �43F��/�ov{�I>���"�+yZb�E��vJ}�%rI���rn��y����Jw��4��m*�w��kI��H�2& [|^7����oEm%�rk����t�N�9q���:x��*�T�I�����������Ky����	��h�!��. #NL���D�w)l�bD�oJ4�v�w$�D&&����m��,D�*��e�<�n�N�%�����m������O����
N�����&�lQD�Z��M�o��j�qz����!�V�m&�-�i'��K���n�&�������,����fr�5�#33i=���1Md����Ofg;�~�Grs��9��?��e�Gh��D�A>D�K@�h�������fr��'��"/';��V�wj��Ms|~?Y233$'�A2T� �KI�0�i:�.K��S��d����Of`���&"O����4������fs��}$������������������}�rv���|���S�&Fff�{39a#��$�a'��;U������S�&Fff�{38���Iowy��}d����Ofg?������������r�l[�n9���g��V}�~��y��=����g��������3/�ejaN)�"�R��eO	��"�~vbddf��n��/�W�3<�8)JyM��7"I���D�8\d��[E���Af$I�=�x�M��J���[�Kq4���;��*����f�I��U���MU�*e6��|�h�d��II:{{/y����{1o4������"�����Y�O�}�����������M�/���N����sk�\��-���|���m>*S���1&�i�����4�DIS��FD_3.�.���������?}�>����}����_9�����Z���/w����<P�J�/���)	�Jd�
�lg,���W6�d��9����up,j'Yt$�K����1��5'�.��u���f�6�o+��ujZ)����[�!���w. L@�����_]���y������<����}����=����:����<\�dU��s��.�%i����C��"+nC�q2��!��j6�+W�_6�C�2�(h@�8H�X&E
���fs39�#�����g33���9����f_�_��q������sO��ci��4ZE�C �"U&P����efg39��?��y9�IZi��~�4M������Gd�s2�3�����t��%�����w^��9�IrX�g�#��! �� � v���b$�--� ��#��#�����g���O�i.�;��+���#�����g��'k>�7�w��G�Gd�s2�3���O�'K	=�t�V��vE~r?";$s��������-�����|����fVfs3�n�o|��OvG���&bD��k��s)�B�6��K'�5�j�{xY����$�-��h�>-��:QR�"��$��OO&���M<��WNR->xx�-��R|���<���}��333333332<s|���,�d��vg[{'{e4���%>{Y������[<kIis�\t������y$U'�q�I5@�����������%�[������K�����Kq$;�<�x1��S�;��,�$����I�5��|�ff@��V��U�;�f���[B��%;r��.�9%�m7�'+u�����w|���5���}�~�I� ��AB�w|��9��I���I&|�������3�����|���&U���������&2�������	�e����l��8�E�Z$2	�%Rz�N��� � �,A*&�a"`a�.�� ��A^%�aJ0q1	��h��fff��nw|���t��2�����D	i#��M"�A2K��D���4pB �!J����XH�o�1�&"O����I4c�������3�����|���7��Q'FB\h�I'F ��ffh���w��>GJ��*�~������&�ws�Y��fff����Gz�>��I��O~�;U����g��3334Leg*9R+��[���+�3334Leg,�������>WW`0����R12^�
F���d�������e����������~������=��7�����}��I$�I$�v�)�<�1�Y���x�^�[\&0��Q�{������Y������q���mLif������1(����2(j5�*����$���#$�t������W����j��/+X�5�.����j !	gSx�D
�* \@����	GF�m�e�����=[�&��6 L�Ir{6��%��L�������������bdsn�}�����
�����J��y��{��&ffffe��G���f^��[pq���q���y����M�7�Rz���d�u����Q�4`��0`�4�������fffd�r*��331����I���$ H- �{[-�4�iK�,I����?>*�333���4�������������4��?t�&��|�U~fff33 �	��(�A-$v����P(+$�,~~*�333���a2�s���>KKl�4ha�A�����O�������^���������fffd�}rw�~�7��9�U�331������t�� ����q7`�U�331����I���d��us�U�331����I��V��D�c�@$"�{��w�=�������8�� Lns��7���ne��3/�y���S3so�rR8�m���)��a��I�w�_3�������#�$OB��#����CTQ2����������$&	F-��]��:!d�Q	$��T'$n%s����n\�R�����&�d^e���U]H���;k�w^���e���Y��x��a���1���-uRY��^c������G��}��_/}���ffffffffe�/?O��oW��I'��]���qO�A�=[�[����/�A����s������B2Ca��6a�����X�E*�333332���UVfffffeb���G���/N~g4�l��8�E�Z$2	�%RpPEEW�fffffV#7�?�"�s������T��h��">?�_������X������D�����U�{�':�.K��~?�_������X�}����t���������U�����������O�i.�;�#����������}��;Y�I�;��F?*�fffffebbG��I��O~�;U�������W�������gE|�K{�*8�������]]\��:���7����������������{�U]��$�qZ�dV$ZM��O&��4Jl�I
����I�;�-'I������L������i�{�f��������4��f��^����Cy���D�v�$S��y����d��)'un��'F��Pz��<�}�������S"���(&Z(A�6[Li��-��0`���h�X��0���(�A-$r����P(&Ip�#=	����m�$����5����r:�n�j�_��$EF�����Im%�����%��g:Ei���8�����7����j�]^:):�����H�����2�V�hC�i���6Y����Z��lw�\S��gS-0"�8���e�}���ow������KO��y%�T�M���M����[��)������m���S�o1�����E������n��m���l�s���������M��Jn���Sz�x�^��Jw��2���7����:�au�/S�d�E3Kjm�V�y�����1$
	���C��m0q�k��iq��y�g����!�PWIbR�D�Bi9����9��;u��o7wM���N|���k��������s��:�>^ts�k{l������n�����N�{n��������d��������:����m�Z\�T��>�����;����w������~��>�z�WI�)E�0��r���[�����J �K~mo�w��JH$MA��^ql��$�d��SJqM��(J$B����r��T�@������5��r��s��b�R��aN��8)JM������S
qJy�s��t���7�f�<S�����q9O9�ogp�������+ikEVsIomM)��w��{�����
U��u(h�w-�4�i��|�T�0FN
A4

�,C
Q���A�DU�A ��9La��H�$�g���I�T���h�K��@�f�7�w	?I:XI��v�wW�0�-���[P�xu������v�S����jY����m�5h��uw��9L��QD6 L@������zSO��e6����k5�mbtz����gZ�)���Jl]%��G���~t�r�-~�����+��eX0��a���<�j�sk��>-(��s���
�V���c��dq�{��x
W�)H&��z����8M�������7��1m�U���jO��8�<vL��cDs���EV�V�4�`>���-�$HI��0��v��V��6K'q
�az�0��> ��Bh�n���AR�M�@�m����_�7�m�x���p���X6N��N�r�))�h'�FT=��O�o7�^U]��{'�����
��]	���!�]���W4y����M����=;�\�J�:A��7k`��y��g�e��s�<���#��uv������L�����u$sQ�����.`;CrwL3��ha�T����m�nfw1E���wGO&��%h+�;��`����aI`�)L��0E������N���G
�|�P��SD���i��!z�Wn`�hl[�.�s�<b1�
�7"sN��K�:f�Vim����;E�����eBf����5^���	�,�*��r�����no��K�{#��%�mS������W_^�w1����(��NJ5�_Y�������fm�g7�z����#U�5�H���I���'#kt�0���g��-q�8�����=c��,y��wn�]�z�������}�.��0�������v5�77�n��^�Yp��3��������,���&4nea���<���;��V�MY<� ������(���WET������]�n��c������ejf�%��������i���y#����5-R<�T�{-���3F�f��S�N����_1�H8#�L"Y��C���#w�D�i���G%�
�YgF��-��<Z���b[���.�XC���v�v�H�3Bj���u�4���b������I�s�ng��09�����.�������:��� �337^Nb!�o^MV�l��������K�r�<Q�a�Fc0�2O�� ��*Z�:�{�1n�*���f)T'c��5�0�ctf�%gAs3������r��9��� A�{2V�n�i��d�Nl�sq{�VP�5
)F���qO
��Rml���!	��wE�������;�q�\z@��'[�-�����=�.�7V���2k���5X-�~-�~<L!�j�6�����������l��s�&r���+G�]�-S��#8B��I
rw`�{]"nf��Z���y���&�����Ux`��\7o����a��`���2�������a��&	<�*�tY�/zj�VK8[��2����f���#�;�b���M�d�l��wF/���Gt��W������������B|�I�*b����m�qo�o7qw�j��	v-���u��umw�o8�8�V����Di�{��)��cz���w��rC������>�A h���w<@��vB���8�a����_T\����B��}J�D��(���
�)�7��W����oy��s��	.�(��Fv6�K����������/�����F3>_���ls���>uF;�iU�q����h]E�G9���Gk���c��2��Rbc>��O{^NLwM��p�~~�L;�S���Q��r�`��|:N��
?>2�~;���B�c��G�a��f�����L�k@}�^�'�/y�Y�Q���v����$#	�i�&���Z��N��>>�H��`nQ��x�$@M�0#�����]���!n����[h�M��\:�� o&����� 6� to���YU�h�xW)�1(��0�$��c��3�c� �	U��eZ/��fonN����-�w$�n��m�%��������D�'�F���R�"�NS��(�5�J�P�UD�Q!�|�RX_ �g�J�#��!a�j-�3Y���m�K��������$��X� Vn�B�o�|��1/��SP.�n%�
��n���ssZ�mD������^i.�BG���t�����/�����2�g@!��db5������L��^���57C��}G"�^�57X��/C�B�
�|}�,C��s����!"������kEY�qw6�i���_����^.l
,HuC��$��;������)$>����$����:$����"y���� b�%�"��uEH���������~�]e����D���� �a�l�!��!jgIy���G� �~CO�cb�0��T>4��c��+YH�S��}����>��^&�u���A�&��9�Z:
�E��A)V=�5z{|��.L�W���=h���Z����~�����"_k���H�����q{
5I�v����#���P�����J�uC��D��1��fl��s@U9z{h^`�A1"E���� 25���V%>�2.���9+������/N�0&�p6i�$��:�H��N�Cg�E
H�H��R��0�I������d�8|���S��t�BY>PH���"�.�, fW�Z&H��0�9��!�P��x�K,��5�'�/j@������C���A��Z,?$a�"/k��	k���$F���������[��7�*�)!�//tO���Hd��D.�c��do%MW�6y�ZD�Ib�>��:�O� ��\���`�+���9�
�X�P��I�p��HY���4	���,��^d�3%��7�3B��
���H�NB!�2x��$���#�i�u><|���`1S+"�y�v�6���|�u���B�%S^���@�OXc�;G��:�QG�sr��
������q*t��z�a���Y-#���"���������L a@���^�!(@
��D|�+!�����:��|�I�&��@�F}Q������}�/b��~b[����G�>����P
o*�7k�)R��\~B"�
(P��=UTUU=UUUUUUUT��s�����z���9�����������������YE[B�MI'�~�E?fO����Is&� I$�Bu�4�@$���2��$>fM	r��m��m�2�UTUU=UUUUUUUUr��m��m����BM��5�� �IHne�B�wM����w�����r���������������Us���<=�I$DE�B� �	�e�$���9�s��UUUOUUUUUUUU*���������
��e�Y�9�-�HH�$���9�s��z����z��������U\�/��!	�)�4�2� �	R��$I����m��n_=UTUU=UUUUUU��.W.\��ng3,t�I'��d��32� I	$HL��$;���_�-���y�6I�32�e��|������8���t�+��"��`�"�����3���
�'"��DFDsT2*�	MD��qB����\d�=*�����
��.U���Y�3/�,�d�0�ed2H��a��f���ks��Vqv����b`�t�������Nn���n�j�Y�����a!d�0�Z���R\]r���H����V��������H������*dS��x�L�JE(�)O)�:��>R����;�����:g}��{x���p���t�S�eJS�c��g��X��g��<�LIP�6��MZ��!E����H��q��'�������{[1|���Q�;T����J(��iH.��R��R)�"�R&a��������U]�T�5�WN�9�A���"9SJE8�y�&^k����U���qx�r<��H���B��E� �R&��R��q�]^s��n�T\t��J@�@@(
���jV���2������y5��3S3��g�a�O)��uL�b����}�a����7�X�F`�sm��|�b�SJpS�����{:�{�r���Gu����x����t�T�����Tr�)�)�F�L?�iKS�iL$Y$���	 ����`��)I�0�T���e`���"D�d8��hda	�)����R�:���LeH���B0�*�@��iL)�)JP��UUOR�r����U��0�![�x��`}���Pq��/h!_U�Z+���'Xk.�c=;��sf�Q���g��v�82���[��{j�������k�{��!C\�~=��}}�%YX�e�yNg��e���S�tz*N�O��oq�Z�@(g$��>4��G�33��W[Q����V��Y�Z�VdIE�"��\+��b��K����gm�V�0����3�7�t�V�,��7z�u��e�E�dFK3wq�g��]+�Q�����-e�t�H��Z�Z��9�����A�����Pa���U	#F$"�F��#�"E>SjR"��)A��(PhH!��(R��1� ���6������VQ�w���L�Mr�a����@3>R)���)��R���1�"�R�S����2fs3.I$�I$�I$��I$�I$�_�-�I$�I$�I/3/s.\�I$�I$�JI$�I$�I%���I$�I$�I-��I$�I$�I.f[�|��g���Cx5v^5^�MUt�HE<)��e#�-)P�iJZR�=R����E
8�PA���N)�$*V��7Z����P
@v�))�
��H�S�XQ����E(�mM���(��%��U�h�\������	7�M��I�6�hR��(X
E4��I�@)k�yNp����Fn��W[��������Q�		.bb\1wbT�t�H���T��6����&����v�U@�U6
��
eHX�R�H
g���u��F+F&�����!wC��e\�JR�����Sh�CH�jD��BE���S�P9��{y�r��Vs3N���f����T��
� ''�����,])��DE:����9�|�������M��<�\*v�JR����JG�(��b�iH$R"E?T�)�S�|��I~�����>�wu��o��P��z
mH)�4)@)�)H�!)M)�)�CG�B�(�[�zdfv��Fgo#3�23;#3��#3��#3��Fgg�{����z���\���|�39���g3x�g3��c����39��fs7��(���*E!�P�)�(��\"���H@�P�J�P�� ����2*�QU��s3���fs7�fs1��f9���s3���g3{����y�P��S�0�)����H����P��-H�	h�'���8�,����EW*3��������3+#������fVF�2�7�+!�PH�TH���4Xh����L�32�{�s��|�}�w���>C>��������s��|�|w���>C>z>|�[�0`�f`���r��P���6��2���aH�$Y!?�����UW�c37����1���fc���fo1������?<�����l!G�d��A��,����c37����1���fc���fo1����0e � �H��)))�@�@�@H����R��mJB�"�$$d��^�j��>��������;���D��/Ev`03��X>*5/����.��X�l24���1s����\)�36���#�g������!��s���/���.k��� h��,��������H�{������,
���]7�LP�r4���^1M!Z{��{�B�e9>��C��|��wh�
�5W����u���/w�@�-N=z��v��27�2&v�	�6?�X�$��~d�����$I>6�(��0)�X)� )h��H�R�)��F�"�S�t���� ��(eJ)A)B�6���S�/������p����3�����Mg<6|���QiL��-N	��!I�4)JE.��J
"D�Ji�������y����7[������ri���8�bRH�@�eJE�A-J.���iH�	���)�O����5=�?w��9��
�(�'����Pv�d���,t�4Z��
R�JP��I$�I$�I)$�I$�I$�3-�I$�I$�I.�-�I$�I$�I/���$�I$�I$�I$�I$�I)$�I$��"D�$6����|��/k7n�h\^�g������P:R��<�T��"AH�R)�)JiL	�'��N)���T�>R�Z����5�G�O^v]�gJ���U����
�L2�"�Jv���"��yL)�4���gvo?��M���1o'^�Eg���w�_&`/H
�@ �)J)�(l�E(����R)�N��6U����s�`�b������o:��v�}*�-O���$@8����HE2�	�"YBI$�I$�RI$�I$�I$�I$�I$�I$�I$�I$��I$�I$�I{�m�I$�I$�Iu�m�I$�I$�A p4jv<�
_���>��`�u�}��)��"`�x�R� �<R;S*Z�J���������gW��sg����}$�P.�x��KR���0"��4�H�$�@��R���0�EQ!$T�DmH��$���N��2!� �R�iH�@�A�@Sq�@�@�O���Z�R��T�*D��RiN��b������da�C`B00�T�X�BC	��C!T1"�9>���g��
�����Oy��KS���_����ox����?$g.Zc�+z*E�a�����f|}�������\D���$�{�O���S7CW,���a���u^B\��m
���Q��FL������w�tt��7�!W����*����{���!��tDY����H�s�Y1^�M��$0�U2#s.
��LE�<����M���t�>o���g��D�����.��<�h�������.�v��_�E�����%����v�],���&r)W�wX�&���2�R�)�@��R)@��t����a}���x\��$�����F�@$iJ
eK-S
Z��`1mJ@6�� �S
iJ)<����y�>��+���z�N�L)��
��JR�I��R���&�)|��uO�6�k�Z3���Mox�u���x�,��S�d�C
B�� AH��@���&��S*bCn�oX�������x�k�C���
a��� JR)CJA��S�R��� SjpS�'��L_w���{����>���)�"���R.���J)� �iH)� ���'���8��9��*���e"�S�X=��#�)t�)O7�n�f����`����
��jT����Sn��_T���R���R��iL��5{362}<4G�c�[\����?d�J)H����<���aH��iL��"��~3Y�:RoQ,���x�����$B��iHR{��C�+���UJ����UQUQUJ��EUTEUR��UU<W*�UJ�
UR�T�UR��J�R��U^�W)�e��#&1���1� ����)���#$���)mO��AnA&C�
,�W**���DUUDUUDUr���UUU���,�,��R����H����'�{��>$z$39+�\��{���W*���EW*+��Er��U�����\�{���������EW**�A\�Ur�U���W�����TUr�\���)C�"�2YE�,���Y`�J�,���$���U{�IT��Ur�*��$�����D���g**�QU^��"��"��"��T������TUR�*��{��@b��"FNF!A�����Z�U��R��3����we��&Ct�+����������O85nQZ98S���/Yl`��`���^���>}*�'~W;Mf~Q~6c�6���nB����CI�j�kbI�G�BdY�_z���.0�
���s=vOw����
U��v?��
�nO���q��@�6h�|+�=��j��l"R}��.�]C�� q�����&{��g_�J�6�A�)`�| #Uk5��iD�Lu�.���|Yz����;�r��;�1U�����`�N
$��2�T���� |�B���mJ]�*	�8����;��=x��?o��k7�������)���"�0S�R��)��)O�DQ>R�_���?��;D+�����<���u���R
uN%�`���R�))1
 C�iHD���pwy:W�S������-��KR�)�"�M�Bz����S"�:�R
yE���0�����.U���:��3�C"����)Jg����P�0p�JiJJ�;S
p�s���%��jx���V�����@g���N��R
�DO�4�E:���w�����{�bb������[����@��H���H�6 t@��������E��T�0l�q��gm�i:�����)Y������$�I$�I$�I$�I$�I$�I$�I$�I/�����$�I$�I$��I$�I$�I$�I$�I$�^�[l��!B�!BS�s�=�k��h?/���`���<9��B!�%HqH����R�� E`H�RJeO)�I���������v������Y���{>�N��� �R��R`D	)�O��S)jyKM`�"�A�E4�lI��S�yH�jD�z�QjME$"I$!M����qJmH���S(��*c0�$d��>R�	JA��^)C`b1
H&��0eH�B���7	��BKU�43�r;'�;�wm�J.5/���6�v�{R�B
�������t������o���_���3c�����47�vkFM���o�� )Y1��
!m�HrkTe1�#=���:"V��8r*z� �����\lm��er/C����=��s}�|���\zU��^<�#1�_��������������gufP�f}�+���ZuB5TBzs:�%j�=#� ��%&��B�'��3��q�$z�]]uN��8�f����T�*uJ)��)��R)I�R���{�!�����k�ssUO�����d"��B(eK^)�@��"yHR)jA��������;U��F���l��w�o�EW�8�H;�O)K�6��K�mM��2��P2�>��������/{3X���������"R�:��E�����(�mN��aM�jR�<���Lx?�����[����>��U^Z��)H)�R�C,Q��][/��l����}.n�L����riK0��5jR]�jqL2'��w��I$�I$�K�*��$�I$�I$����e��I$�I$�II$�I$�I$�I$�I$�I$�I$�I$�JI$�I$�I%�?^wo������k&����w��������*R�SeH$RRA�M������k��K�9��v��)
�)� ����SD@q��MtmOO�O>�����1�+=����>l�3�,�����!�:�W�@�<]�x�}�r�w�e�g5��>.$�)2�S!��{�_��J��%E^�3��Lg3t�zL�f�**����n�=�s7I��f�1^����#�����TVedf2�2�QY������edf{����edf8{�����p���\	��emT���

H��E��x�)R	;(
B��H@"�%)>RJA�)�(mH�R%:C%�Y,�EfVFc+#+��Y\���VFg�����V!�	�8�O��1��E��?<�>$s��TW�r���YY����TUedfr��=Y��+#3=Y��=�VEW**�Q��������FfVEW*3�����2�3�����!��y��{�y9��UT��ffc����ff*�����)�/����Sb��3��c �4t�ae�XYe�`���FfR��#3)UU������������{���2���32�D0 M�D�yLR �����zW:g���=:���*����{���i���ua��9��������'&R�E�����*6V��f����x9��R�c���F��Kx+	��^O�"��;*�%[T6�?Xo.������y�X:��{����s�1l�}�#�������(�I�;a�"z7K�o�{�X�1S2����45�a���~{�a��0O��\I����R^'�{���s>�)#�B��:<=�iJ���BOJ�)�E8�qH�H�R�����)�0���_o]���W���7�6p��a��!JE���aJp)�+�2�SJR��)9��%�}}�\��]9����R��D�6�4��AJD|���t��%������e�w��U��'����	� ���AM
 |��X��R���l��k�s{�7�\�=��/��|���JxO)2�_uKS"�: f���}x|>�
� D]�#w�,j�����S
z)�)9jaK
))p)�4���O���h=�
���_{�}��I�0)�)
F)jS�R��-L�I� aN
mO)�����l
���������w����
� �����KR1L)�(y����~��l����U~S�O�)�K�)_)�N)Xh�5W���7�\���_�=&�R��yJCjF���C� eN)��SJU�NH$R�I� &�Z���2'��P��S�E
DM�R|�D�"S�RT��4���n�|��Q{Fg�50���S�s�-L)G"�R������g�O�E���3��4]�Q���:T��i�w����'���n�C��s�4��3m{p\x�Jp-�8o|������o9�mb*��L���^�U#pE�>/�������n���
�:�,��$�n���.=��Wn������g(13D'�%h���&0.��Y���hZ�^;��IB����JH"(�d|c^�	���X�~cm�5U1Awu�{��
P��n��BAM)J|Eb���JR�R�jE��������{�����^�0�_����(R�R�XZ�S�_���vc}�{�2I:��	�J�$FF��S*O�z��?[�{&��y�:�+����s�{PS�mO�����$�&T�3y��5��>����bf����Q��~�U
u"��4��K
)RR�/�=��,�Q����c�����.�����_�)�0�4)|�:RRuL��6)����3���5��v��J����{��H��K��\)
JSj|�S�R�O���mj�}����QS�<�SCf.�b��)�|�JH)2���7@��� x'��v�b%l��Kc�����7)� E2�'����S"�S��S
pS�k~�Q�>���^cW��I��s����k�g�x�@��R�RHH�>R��)J=�I��&��l�&��l�&��l�&��l�&��l���m��m��m���9�r#��G9��s�
(���!�-��S�X���Q.�'y��e����G9��s��"9�Ds���9�r#��G9��Q
�M��>S9R6�JSh|�L���H�d���"9�Ds���9�r#��G9��s��"9�Gx{���9�r#��G9��s��"9�Ds���9�qP��C�l
�*U��)�M"9�Ds���9�r#��G9��s��"9�Ds����/���]EG,��
���n
���n
���n
���n
���ns.Y�~���]�`�H�]s�{<����Ss���$GD\x\I��>��pft>/��'��i���!�/`�������S����i)S�|��
����#�0�v�uAP=�gzD�s:�����L�����Hw�4T�@xV���VC2S"��9����&�`
�l�(P��eG���`�G��l��8|&�������
�e������:fg*�d�x*?�6WfJ2
m��R��-�r)�4�)�>��T���0)@�T�s������W	��7.�")�(���P��S�uK����O�s�B�x�����g�E1H�K�)
qN��"�Uw�s����]^y�G�����������b���R�RM�E)�@q.�^��8q����|
y����CN���!���(+
Q@��@�)�2�)��R������ck�vH�]��}���<E#���R|�J�����eJS�n���{1���>�U�8�������.���� J������c��J��l��+	�?�-M&(��BB�)��R6qM��<�=yq���}������6:w��H��)2�'=X�O������l��C�|PaH���SM�jmL��
��T�%��G�`N)�+�v�F�)�@�����E+�CI#!D�]�E�"��B�L�	A�!j@��R�6�Z��M�8�S7���(R��d���D���R(��w~���Z�������&V����8&^Z�ns�r�G�3!��u���.-�#tM�\��M�#�d^�����nH��,R�S2{���"@��:�[���x�q���<2���=2���T���0Q���j'ymLR���0���������k�:��
���= 6���������L����K����I#�y��D*��3j��Z��`�7S���]uO)JiH���~5LFJ���
�������M)JE8���[��d����f���T�=6�
RI�bp���EQ���L_K��0_��L�rg|���>��T� ��>R���S����YWF�Wz�1y�����G^n��j@r���"�'�����k����]~9Z����STaOaOT�D�2���R�S��}{��^*����d��iHz,�-�
eJKS����)�"(iH)�^�������h������sE��FyO���(�h�)X���uN)���C[�7��V��Q��<���d���@�%�^�N�@����}u6c�)E�O��Ha	�)�)R8��&��Sp�R�T%9F�f�,�r��u; �3�������W���ol��o�A����A��.�����!���J������zQSb��_,P�&T���"��c��l���|
���;��-����5DuS�X�2<��NC����#���O��!	!uIu�;���[�H���	i�u���E�3���*���K�"�6������=�M����3(��2Qk�I������P�/���`��o%�A��3b�E�h��
��d��]tD����7�������P0153�N�M%9���pHBHY��aFiD5-�"�0�fj�QAJZ	�
1`���N�#�Y�Ki�l�(�u���L�v��XI	��Vo7X���z+�XD�4#k9���\j�G^
��l��z�"*?�Y������M���8K��*�;`Ju>e"{#�DXM�����+t�$���e�o�����)�����'P
#132Yuya Watari
watari.yuya@gmail.com
In reply to: David Rowley (#131)
Re: [PoC] Reducing planning time when tables have many partitions

Hello David,

On Tue, Apr 8, 2025 at 3:31 PM David Rowley <dgrowleyml@gmail.com> wrote:

I've pushed the patch now. Thanks for all the reviews of my adjustments.

Thank you very much for pushing the patch! I also wish to extend my
deepest thanks to everyone who has contributed to reviewing and
improving this patch.

Thanks to Yuya for persisting on this for so many years. I was
impressed with that persistence and also with your very detailed and
easy to understand performance benchmark results. This feels like
(all going well) it's making v18 is big win for the big partitioning
users of Postgres. I kind of feel the "up to a few thousand partitions
fairly well" in [1] has been abused by many or taken without the
caveat of "the query planner to prune all but a small number of
partitions" over the years and the "Never just assume that more
partitions are better than fewer partitions, nor vice-versa." has been
ignored by too many. It's good that the people pushing these limits
will no longer be getting as big an unwelcome surprise now (I hope).
Or, at least, onto the next bottleneck. Maybe relcache :)

This work could not have been realized without your significant
contributions. You have provided many valuable ideas and efforts since
I first proposed the patch three years ago. I would like to express my
sincere thanks for your continued and extensive support. I hope that
this work we have done together will greatly benefit users managing
highly partitioned configurations. It has truly been an honor for me
to work closely with you and all the reviewers on this work. It would
be my pleasure to continue contributing to PostgreSQL.

--
Best regards,
Yuya Watari

#133Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#131)
Re: [PoC] Reducing planning time when tables have many partitions

David Rowley <dgrowleyml@gmail.com> writes:

I've pushed the patch now. Thanks for all the reviews of my adjustments.

Shouldn't the CF entry be marked committed?

Thanks to Yuya for persisting on this for so many years. I was
impressed with that persistence and also with your very detailed and
easy to understand performance benchmark results.

+1

regards, tom lane

#134David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#133)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 9 Apr 2025 at 02:24, Tom Lane <tgl@sss.pgh.pa.us> wrote:

David Rowley <dgrowleyml@gmail.com> writes:

I've pushed the patch now. Thanks for all the reviews of my adjustments.

Shouldn't the CF entry be marked committed?

I've done that now.

88f55bc97 added code to do faster lookups of ec_derives clauses, so I
think that likely renders part of the remaining patches obsolete. The
v35-0004 also had some indexing of ec_sources. It would be good to
know if there's still any gains to be had from indexing those. If
there's work to do, then a new CF entry can be made for that.

David

#135Amit Langote
amitlangote09@gmail.com
In reply to: David Rowley (#134)
Re: [PoC] Reducing planning time when tables have many partitions

Hi David,

On Wed, Apr 9, 2025 at 5:12 AM David Rowley <dgrowleyml@gmail.com> wrote:

On Wed, 9 Apr 2025 at 02:24, Tom Lane <tgl@sss.pgh.pa.us> wrote:

David Rowley <dgrowleyml@gmail.com> writes:

I've pushed the patch now. Thanks for all the reviews of my adjustments.

Shouldn't the CF entry be marked committed?

I've done that now.

Should the following paragraph in src/backend/optimizer/README be
updated to reflect the new reality after recent changes?

An EquivalenceClass can contain "em_is_child" members, which are copies
of members that contain appendrel parent relation Vars, transposed to
contain the equivalent child-relation variables or expressions. These
members are not full-fledged members of the EquivalenceClass and do not
affect the class's overall properties at all. They are kept only to
simplify matching of child-relation expressions to EquivalenceClasses.
Most operations on EquivalenceClasses should ignore child members.

The part about these being in the EquivalenceClass might be worth
rewording now that we keep them in a separate array.

--
Thanks, Amit Langote

#136Tom Lane
tgl@sss.pgh.pa.us
In reply to: Amit Langote (#135)
Re: [PoC] Reducing planning time when tables have many partitions

Amit Langote <amitlangote09@gmail.com> writes:

Should the following paragraph in src/backend/optimizer/README be
updated to reflect the new reality after recent changes?

An EquivalenceClass can contain "em_is_child" members, which are copies
of members that contain appendrel parent relation Vars, transposed to
contain the equivalent child-relation variables or expressions.

Hm. They still are "in" the EquivalenceClass in a very real sense;
there is no other data structure that links to them. So this isn't
incorrect. I do get your feeling that maybe some rewording is
warranted, but I'm not sure what.

regards, tom lane

#137David Rowley
dgrowleyml@gmail.com
In reply to: Amit Langote (#135)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 9 Apr 2025 at 17:09, Amit Langote <amitlangote09@gmail.com> wrote:

Should the following paragraph in src/backend/optimizer/README be
updated to reflect the new reality after recent changes?

An EquivalenceClass can contain "em_is_child" members, which are copies
of members that contain appendrel parent relation Vars, transposed to
contain the equivalent child-relation variables or expressions. These
members are not full-fledged members of the EquivalenceClass and do not
affect the class's overall properties at all. They are kept only to
simplify matching of child-relation expressions to EquivalenceClasses.
Most operations on EquivalenceClasses should ignore child members.

The part about these being in the EquivalenceClass might be worth
rewording now that we keep them in a separate array.

I did read over that as part of the search I did for things that need
to be updated, but I didn't see the need to adjust anything since the
text doesn't talk about where the members are stored. The only thing
I see as a hint to that is the final sentence.

If the README is light on documentation about where members are
stored, do we really need to start detailing that because of this
change? I've tried to be fairly comprehensive about where members are
stored in the header comment for struct EquivalenceClass. Wouldn't
stating something similar in the README just be duplicating that? I
always think of the READMEs as more of an overview on how things fit
together with some high-level theory. I think talking about data
structures might be a bit too much detail.

I'm happy to view wording suggestions if you think we need to detail
this further. Maybe there's something that can be adjusted without
going into too much depth.

David

#138Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: David Rowley (#137)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, Apr 9, 2025 at 10:51 AM David Rowley <dgrowleyml@gmail.com> wrote:

On Wed, 9 Apr 2025 at 17:09, Amit Langote <amitlangote09@gmail.com> wrote:

Should the following paragraph in src/backend/optimizer/README be
updated to reflect the new reality after recent changes?

An EquivalenceClass can contain "em_is_child" members, which are copies
of members that contain appendrel parent relation Vars, transposed to
contain the equivalent child-relation variables or expressions. These
members are not full-fledged members of the EquivalenceClass and do not
affect the class's overall properties at all. They are kept only to
simplify matching of child-relation expressions to EquivalenceClasses.
Most operations on EquivalenceClasses should ignore child members.

The part about these being in the EquivalenceClass might be worth
rewording now that we keep them in a separate array.

I did read over that as part of the search I did for things that need
to be updated, but I didn't see the need to adjust anything since the
text doesn't talk about where the members are stored. The only thing
I see as a hint to that is the final sentence.

If the README is light on documentation about where members are
stored, do we really need to start detailing that because of this
change? I've tried to be fairly comprehensive about where members are
stored in the header comment for struct EquivalenceClass. Wouldn't
stating something similar in the README just be duplicating that? I
always think of the READMEs as more of an overview on how things fit
together with some high-level theory. I think talking about data
structures might be a bit too much detail.

This change didn't require us to change the README indicates that the
README is at the right level. The code changes internally reorganize
how child EMs are stored within an EC, so changing the comments for
relevant structures seems good enough, not a change that should bubble
up to the README. The only change in the EC interface is the addition
of a new iterator method - maybe we could document that in README but
that too seems more detail than what README is about.

--
Best Wishes,
Ashutosh Bapat

#139Amit Langote
amitlangote09@gmail.com
In reply to: David Rowley (#137)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, Apr 9, 2025 at 2:21 PM David Rowley <dgrowleyml@gmail.com> wrote:

On Wed, 9 Apr 2025 at 17:09, Amit Langote <amitlangote09@gmail.com> wrote:

Should the following paragraph in src/backend/optimizer/README be
updated to reflect the new reality after recent changes?

An EquivalenceClass can contain "em_is_child" members, which are copies
of members that contain appendrel parent relation Vars, transposed to
contain the equivalent child-relation variables or expressions. These
members are not full-fledged members of the EquivalenceClass and do not
affect the class's overall properties at all. They are kept only to
simplify matching of child-relation expressions to EquivalenceClasses.
Most operations on EquivalenceClasses should ignore child members.

The part about these being in the EquivalenceClass might be worth
rewording now that we keep them in a separate array.

I did read over that as part of the search I did for things that need
to be updated, but I didn't see the need to adjust anything since the
text doesn't talk about where the members are stored. The only thing
I see as a hint to that is the final sentence.

If the README is light on documentation about where members are
stored, do we really need to start detailing that because of this
change? I've tried to be fairly comprehensive about where members are
stored in the header comment for struct EquivalenceClass. Wouldn't
stating something similar in the README just be duplicating that? I
always think of the READMEs as more of an overview on how things fit
together with some high-level theory. I think talking about data
structures might be a bit too much detail.

I'm happy to view wording suggestions if you think we need to detail
this further. Maybe there's something that can be adjusted without
going into too much depth.

Fair point that the current text isn't wrong (as Tom says) -- and we
do have the storage details in the struct comment already as you say.

Still, maybe a tiny tweak to the last line could help steer readers
right without diving into storage. How about:

Most operations on EquivalenceClasses should ignore child members,
which are stored separately from normal members.

No big deal either way -- just throwing it out there.

--
Thanks, Amit Langote

#140David Rowley
dgrowleyml@gmail.com
In reply to: Amit Langote (#139)
Re: [PoC] Reducing planning time when tables have many partitions

On Wed, 9 Apr 2025 at 17:38, Amit Langote <amitlangote09@gmail.com> wrote:

Still, maybe a tiny tweak to the last line could help steer readers
right without diving into storage. How about:

Most operations on EquivalenceClasses should ignore child members,
which are stored separately from normal members.

I think the only part of the current text that makes me slightly
uncomfortable is the "ignore child members". I don't mind your text,
but it does introduce detail about how the members are stored, which
isn't there before.

I think the "ignore child members" part could be fixed with:

--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -902,7 +902,7 @@ contain the equivalent child-relation variables or
expressions.  These
 members are *not* full-fledged members of the EquivalenceClass and do not
 affect the class's overall properties at all.  They are kept only to
 simplify matching of child-relation expressions to EquivalenceClasses.
-Most operations on EquivalenceClasses should ignore child members.
+Most operations on EquivalenceClasses needn't look at child members.

Would that be ok?

David

#141Amit Langote
amitlangote09@gmail.com
In reply to: David Rowley (#140)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, Apr 10, 2025 at 12:03 PM David Rowley <dgrowleyml@gmail.com> wrote:

On Wed, 9 Apr 2025 at 17:38, Amit Langote <amitlangote09@gmail.com> wrote:

Still, maybe a tiny tweak to the last line could help steer readers
right without diving into storage. How about:

Most operations on EquivalenceClasses should ignore child members,
which are stored separately from normal members.

I think the only part of the current text that makes me slightly
uncomfortable is the "ignore child members". I don't mind your text,
but it does introduce detail about how the members are stored, which
isn't there before.

I think the "ignore child members" part could be fixed with:

--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -902,7 +902,7 @@ contain the equivalent child-relation variables or
expressions.  These
members are *not* full-fledged members of the EquivalenceClass and do not
affect the class's overall properties at all.  They are kept only to
simplify matching of child-relation expressions to EquivalenceClasses.
-Most operations on EquivalenceClasses should ignore child members.
+Most operations on EquivalenceClasses needn't look at child members.

Would that be ok?

Yeah, I think that wording works well. It avoids sounding too strict
but still points things in the right direction.

--
Thanks, Amit Langote

#142David Rowley
dgrowleyml@gmail.com
In reply to: Amit Langote (#141)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, 10 Apr 2025 at 16:57, Amit Langote <amitlangote09@gmail.com> wrote:

On Thu, Apr 10, 2025 at 12:03 PM David Rowley <dgrowleyml@gmail.com> wrote:

-Most operations on EquivalenceClasses should ignore child members.
+Most operations on EquivalenceClasses needn't look at child members.

Would that be ok?

Yeah, I think that wording works well. It avoids sounding too strict
but still points things in the right direction.

Thanks. Pushed.

David

#143Amit Langote
amitlangote09@gmail.com
In reply to: David Rowley (#142)
Re: [PoC] Reducing planning time when tables have many partitions

On Thu, Apr 10, 2025 at 2:35 PM David Rowley <dgrowleyml@gmail.com> wrote:

On Thu, 10 Apr 2025 at 16:57, Amit Langote <amitlangote09@gmail.com> wrote:

On Thu, Apr 10, 2025 at 12:03 PM David Rowley <dgrowleyml@gmail.com> wrote:

-Most operations on EquivalenceClasses should ignore child members.
+Most operations on EquivalenceClasses needn't look at child members.

Would that be ok?

Yeah, I think that wording works well. It avoids sounding too strict
but still points things in the right direction.

Thanks. Pushed.

Thank you.

--
Thanks, Amit Langote