v2-0003-amit-delta.patch
application/octet-stream
Filename: v2-0003-amit-delta.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0003
Subject: amit delta
| File | + | − |
|---|---|---|
| src/backend/nodes/outfuncs.c | 1 | 5 |
| src/backend/optimizer/path/equivclass.c | 205 | 167 |
| src/backend/optimizer/plan/analyzejoins.c | 2 | 2 |
| src/include/nodes/pathnodes.h | 15 | 16 |
| src/include/optimizer/paths.h | 1 | 1 |
From bd1faff01cb6a669a38a8bb9440ba93cd82de262 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Wed, 19 Mar 2025 17:30:54 +0900
Subject: [PATCH v2 3/3] amit delta
---
src/backend/nodes/outfuncs.c | 6 +-
src/backend/optimizer/path/equivclass.c | 372 ++++++++++++----------
src/backend/optimizer/plan/analyzejoins.c | 4 +-
src/include/nodes/pathnodes.h | 31 +-
src/include/optimizer/paths.h | 2 +-
5 files changed, 224 insertions(+), 191 deletions(-)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 642ead7e629..557f06e344f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -467,12 +467,8 @@ _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
WRITE_OID_FIELD(ec_collation);
WRITE_NODE_FIELD(ec_members);
WRITE_NODE_FIELD(ec_sources);
+ /* Only ec_derives_list is written; hash is not serialized. */
WRITE_NODE_FIELD(ec_derives_list);
-
- /*
- * ec_derives_list and ec_derives_hash contain the same set of
- * RestrictInfos, hence no need to write contents of ec_derives_hash.
- */
WRITE_BITMAPSET_FIELD(ec_relids);
WRITE_BOOL_FIELD(ec_has_const);
WRITE_BOOL_FIELD(ec_has_volatile);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0d21461dc7c..4d926f3b582 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -73,22 +73,23 @@ static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
Relids relids);
static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
Relids relids2);
-static void add_derived_clauses(EquivalenceClass *ec, List *clauses);
-static void add_derived_clause(EquivalenceClass *ec, RestrictInfo *clause);
-static RestrictInfo *search_clause_for_ems(PlannerInfo *root, EquivalenceClass *ec,
- EquivalenceMember *leftem,
- EquivalenceMember *rightem,
- EquivalenceClass *parent_ec);
-static RestrictInfo *search_derived_clause_for_ems(PlannerInfo *root,
- EquivalenceClass *ec,
- EquivalenceMember *leftem,
- EquivalenceMember *rightem,
- EquivalenceClass *parent_ec);
-static void add_clause_to_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo);
static void build_ec_derives_hash(PlannerInfo *root, EquivalenceClass *ec);
+static void ec_add_derived_clauses(EquivalenceClass *ec, List *clauses);
+static void ec_add_derived_clause(EquivalenceClass *ec, RestrictInfo *clause);
+static void add_clause_to_ec_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo);
+static RestrictInfo *ec_search_clause_for_ems(PlannerInfo *root, EquivalenceClass *ec,
+ EquivalenceMember *leftem,
+ EquivalenceMember *rightem,
+ EquivalenceClass *parent_ec);
+static RestrictInfo *ec_search_derived_clause_for_ems(PlannerInfo *root,
+ EquivalenceClass *ec,
+ EquivalenceMember *leftem,
+ EquivalenceMember *rightem,
+ EquivalenceClass *parent_ec);
-
-/* Hash key for derived clause lookup by EquivalenceMembers. */
+/*
+ * Hash key identifying a derived clause by EquivalenceMembers and parent EC.
+ */
typedef struct
{
EquivalenceMember *em1;
@@ -96,7 +97,7 @@ typedef struct
EquivalenceClass *parent_ec;
} ECDerivesKey;
-/* Hash table entry in ec_derives_hash. */
+/* Hash table entry used in ec_derives_hash. */
typedef struct
{
uint32 status;
@@ -108,8 +109,10 @@ typedef struct
#define SH_ELEMENT_TYPE ECDerivesEntry
#define SH_KEY_TYPE ECDerivesKey
#define SH_KEY key
-#define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(ECDerivesKey))
-#define SH_EQUAL(tb, a, b) ((a).em1 == (b).em1 && (a).em2 == (b).em2 && (a).parent_ec == (b).parent_ec)
+#define SH_HASH_KEY(tb, key) \
+ hash_bytes((const unsigned char *) &(key), sizeof(ECDerivesKey))
+#define SH_EQUAL(tb, a, b) \
+ ((a).em1 == (b).em1 && (a).em2 == (b).em2 && (a).parent_ec == (b).parent_ec)
#define SH_SCOPE static inline
#define SH_DECLARE
#define SH_DEFINE
@@ -383,7 +386,8 @@ process_equivalence(PlannerInfo *root,
*/
ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
- add_derived_clauses(ec1, ec2->ec_derives_list);
+ /* Updates ec1's ec_derives_list and ec_derives_hash if present. */
+ ec_add_derived_clauses(ec1, ec2->ec_derives_list);
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 */
@@ -396,7 +400,7 @@ process_equivalence(PlannerInfo *root,
/* just to avoid debugging confusion w/ dangling pointers: */
ec2->ec_members = NIL;
ec2->ec_sources = NIL;
- clear_ec_derived_clauses(ec2);
+ ec_clear_derived_clauses(ec2);
ec2->ec_relids = NULL;
ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_min_security = Min(ec1->ec_min_security,
@@ -1069,8 +1073,8 @@ 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 source or derived clauses for matches. It doesn't really
- * seem worth the trouble to do so.
+ * don't search existing source or derived clauses in the EC for matches. It
+ * doesn't really seem worth the trouble to do so.
*/
void
generate_base_implied_equalities(PlannerInfo *root)
@@ -1243,7 +1247,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
rinfo->left_ec = rinfo->right_ec = ec;
rinfo->left_em = cur_em;
rinfo->right_em = const_em;
- add_derived_clause(ec, rinfo);
+ ec_add_derived_clause(ec, rinfo);
}
}
}
@@ -1308,10 +1312,10 @@ 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 save it
- * as a derived clause however; we don't currently need to re-find
- * such clauses, and we don't want to clutter that set with
- * non-join clauses.
+ * correct markings for a mergejoinable clause. We don't record it
+ * as a derived clause, since we don't currently need to re-find
+ * such clauses, and don't want to clutter the derived-clause set
+ * with non-join clauses.
*/
if (rinfo && rinfo->mergeopfamilies)
{
@@ -1797,7 +1801,7 @@ 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 part of EC derived clauses , but there
+ * RestrictInfos that are not included in EC's derived clauses, 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.
*
@@ -1845,97 +1849,6 @@ select_equality_operator(EquivalenceClass *ec, Oid lefttype, Oid righttype)
return InvalidOid;
}
-/*
- * search_clause_for_ems
- * Return an already built RestrictInfo for the given pair of
- * EquivalenceMembers, if exists.
- *
- * We can use either original source clauses or previously-derived clauses, and
- * a commutator clause is acceptable.
- *
- * We used to verify that opno matches, but that seems redundant: even if it's
- * not identical, it'd better have the same effects, or the operator families
- * we're using are broken.
- *
- * Returns NULL if the required RestrictInfo is not already built.
- */
-static RestrictInfo *
-search_clause_for_ems(PlannerInfo *root, EquivalenceClass *ec, EquivalenceMember *leftem, EquivalenceMember *rightem, EquivalenceClass *parent_ec)
-{
- foreach_node(RestrictInfo, rinfo, ec->ec_sources)
- {
- if (rinfo->left_em == leftem &&
- rinfo->right_em == rightem &&
- rinfo->parent_ec == parent_ec)
- return rinfo;
- if (rinfo->left_em == rightem &&
- rinfo->right_em == leftem &&
- rinfo->parent_ec == parent_ec)
- return rinfo;
- }
-
- return search_derived_clause_for_ems(root, ec, leftem, rightem, parent_ec);
-}
-
-/*
- * search_derived_clause_for_ems
- * Similar to search_clause_for_ems() but looks up derived clauses.
- */
-static RestrictInfo *
-search_derived_clause_for_ems(PlannerInfo *root, EquivalenceClass *ec, EquivalenceMember *leftem, EquivalenceMember *rightem, EquivalenceClass *parent_ec)
-{
- /*
- * Switch to using hash lookup when list grows "too long". The threshold
- * is arbitrary and is known only here.
- */
- if (!ec->ec_derives_hash && list_length(ec->ec_derives_list) >= 32)
- build_ec_derives_hash(root, ec);
-
- /* Perform hash table lookup or linear search as appropriate. */
- if (ec->ec_derives_hash)
- {
- ECDerivesKey key;
- RestrictInfo *rinfo;
- ECDerivesEntry *entry;
-
- /*
- * add_clause_to_derives_hash() explains why we don't perform a second
- * search by swapping EquivalenceMembers.
- */
- key.em1 = leftem;
- key.em2 = rightem;
- key.parent_ec = parent_ec;
- entry = derives_lookup(ec->ec_derives_hash, key);
- if (entry)
- {
- rinfo = entry->rinfo;
- Assert(rinfo);
- return rinfo;
- }
- }
- else
- {
- foreach_node(RestrictInfo, rinfo, ec->ec_derives_list)
- {
- if (!rightem &&
- rinfo->left_em == leftem)
- {
- Assert(rinfo->right_em->em_is_const);
- return rinfo;
- }
- if (rinfo->left_em == leftem &&
- rinfo->right_em == rightem &&
- rinfo->parent_ec == parent_ec)
- return rinfo;
- if (rinfo->left_em == rightem &&
- rinfo->right_em == leftem &&
- rinfo->parent_ec == parent_ec)
- return rinfo;
- }
- }
-
- return NULL;
-}
/*
* create_join_clause
@@ -1959,7 +1872,7 @@ create_join_clause(PlannerInfo *root,
RestrictInfo *parent_rinfo = NULL;
MemoryContext oldcontext;
- rinfo = search_clause_for_ems(root, ec, leftem, rightem, parent_ec);
+ rinfo = ec_search_clause_for_ems(root, ec, leftem, rightem, parent_ec);
if (rinfo)
return rinfo;
@@ -2025,7 +1938,7 @@ create_join_clause(PlannerInfo *root,
rinfo->left_em = leftem;
rinfo->right_em = rightem;
/* and save it for possible re-use */
- add_derived_clause(ec, rinfo);
+ ec_add_derived_clause(ec, rinfo);
MemoryContextSwitchTo(oldcontext);
@@ -2757,7 +2670,7 @@ find_derived_clause_for_ec_member(PlannerInfo *root,
Assert(ec->ec_has_const);
Assert(!em->em_is_const);
- return search_derived_clause_for_ems(root, ec, em, NULL, NULL);
+ return ec_search_derived_clause_for_ems(root, ec, em, NULL, NULL);
}
@@ -3532,81 +3445,84 @@ get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
}
/*
- * add_derived_clauses
- * Add given clause to the set of clauses derived from the given EquivalenceClass; adding to the list and hash table if needed.
+ * build_ec_derives_hash
+ * Construct the auxiliary hash table for derived clauses.
*/
static void
-add_derived_clause(EquivalenceClass *ec, RestrictInfo *clause)
+build_ec_derives_hash(PlannerInfo *root, EquivalenceClass *ec)
+{
+ Assert(!ec->ec_derives_hash);
+
+ /* Create the hash table */
+ ec->ec_derives_hash = derives_create(root->planner_cxt, 256L, NULL);
+
+ foreach_node(RestrictInfo, rinfo, ec->ec_derives_list)
+ add_clause_to_ec_derives_hash(ec, rinfo);
+}
+
+/*
+ * ec_add_derived_clause
+ * Add a clause to the set of derived clauses for the given
+ * EquivalenceClass. Always appends to ec_derives_list; also adds
+ * to ec_derives_hash if it exists.
+ */
+static void
+ec_add_derived_clause(EquivalenceClass *ec, RestrictInfo *clause)
{
ec->ec_derives_list = lappend(ec->ec_derives_list, clause);
if (ec->ec_derives_hash)
- add_clause_to_derives_hash(ec, clause);
+ add_clause_to_ec_derives_hash(ec, clause);
}
/*
- * add_derived_clauses
- * Add a list of clauses to the set of clauses derived from the given EquivalenceClass; adding to the list and hash table if needed.
+ * ec_add_derived_clauses
+ * Add a list of clauses to the set of clauses derived from the given
+ * EquivalenceClass; adding to the list and hash table if needed.
*
* This function is similar to above function but optimized for adding multiple
* clauses at a time to the ec_derives_list.
*/
static void
-add_derived_clauses(EquivalenceClass *ec, List *clauses)
+ec_add_derived_clauses(EquivalenceClass *ec, List *clauses)
{
ec->ec_derives_list = list_concat(ec->ec_derives_list, clauses);
if (ec->ec_derives_hash)
foreach_node(RestrictInfo, rinfo, clauses)
- add_clause_to_derives_hash(ec, rinfo);
+ add_clause_to_ec_derives_hash(ec, rinfo);
}
/*
- * build_ec_derives_hash
- * Construct the auxiliary hash table for derived clauses.
+ * add_clause_to_ec_derives_hash
+ * Add a derived clause to the ec_derives_hash of the given
+ * EquivalenceClass.
*/
static void
-build_ec_derives_hash(PlannerInfo *root, EquivalenceClass *ec)
-{
- Assert(!ec->ec_derives_hash);
-
- /* Create the hash table */
- ec->ec_derives_hash = derives_create(root->planner_cxt, 256L, NULL);
-
- foreach_node(RestrictInfo, rinfo, ec->ec_derives_list)
- add_clause_to_derives_hash(ec, rinfo);
-}
-
-/*
- * add_clause_to_derives_hash
- * Add the given clause to the hash table of derived clauses in the given EquivalenceClass.
- */
-static void
-add_clause_to_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo)
+add_clause_to_ec_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo)
{
ECDerivesKey key;
ECDerivesEntry *entry;
bool found;
/*
- * Per generate_base_implied_equalities_const(), a constant always appears
- * on the RHS of equality.
+ * Constants are always placed on the RHS; see
+ * generate_base_implied_equalities_const().
*/
Assert(!rinfo->left_em->em_is_const);
if (rinfo->right_em->em_is_const)
{
/*
- * generate_base_implied_equalities_const() doesn't set parent_ec
- * since clauses involving constants are not redundant.
+ * Clauses containing a constant never considered redundant, so
+ * parent_ec is not set.
*/
Assert(!rinfo->parent_ec);
/*
- * find_derived_clause_for_ec_member() looks up a clause involving a
- * constant by the non-constant EquivalenceMember. The actual constant
- * EquivalenceMember is irrelevant for such lookup and is not
- * available when looking up. Further there is only one constant
- * EquivalenceMember per EquivalenceClass. For ease of lookup, we set
- * right_em to NULL in the key.
+ * find_derived_clause_for_ec_member() performs lookup of a clause
+ * involving a constant using only the non-constant EM and NULL for
+ * the RHS. Since there's only one constant EM per EC, we don't need
+ * to store or match it during lookup. We set key.em2 = NULL to
+ * reflect this.
*/
key.em1 = rinfo->left_em;
key.em2 = NULL;
@@ -3625,8 +3541,9 @@ add_clause_to_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo)
entry->rinfo = rinfo;
/*
- * Add another entry to match the clause even if left and right EMs
- * have swapped.
+ * Insert the clause under the given EM pair key, and also under the
+ * reverse order. This ensures we can find the clause regardless of
+ * the order in which EMs are passed to the lookup function.
*/
key.em1 = rinfo->right_em;
key.em2 = rinfo->left_em;
@@ -3635,21 +3552,21 @@ add_clause_to_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo)
Assert(!found);
entry->rinfo = rinfo;
}
-
}
/*
* ec_clear_derived_clauses
- * Reset the ec_derives list and the hash table.
+ * Reset ec_derives_list and ec_derives_hash.
*
- * We destroy the hash table since it consumes more space than the list. But we
- * don't bother to free the list.
+ * We destroy the hash table explicitly, since it may consume significant
+ * space. The list is simply cleared by setting it to NIL; we do not
+ * explicitly free it.
*
- * XXX: when there are thousands of partitions involved, the list can grow
- * sizable and thus freeing it might be desirable.
+ * XXX: When thousands of partitions are involved, the list can become
+ * sizable. It might be worth freeing it explicitly in such cases.
*/
void
-clear_ec_derived_clauses(EquivalenceClass *ec)
+ec_clear_derived_clauses(EquivalenceClass *ec)
{
ec->ec_derives_list = NIL;
if (ec->ec_derives_hash)
@@ -3658,3 +3575,124 @@ clear_ec_derived_clauses(EquivalenceClass *ec)
ec->ec_derives_hash = NULL;
}
}
+
+/*
+ * ec_search_clause_for_ems
+ * Search for an existing RestrictInfo that equates the given pair
+ * of EquivalenceMembers, either from ec_sources or ec_derives.
+ *
+ * We accept clauses in either operand order, so commutators are matched. We
+ * used to require matching operator OIDs, but dropped that since any
+ * semantically different operator here would indicate a broken operator
+ * family.
+ *
+ * Returns NULL if no matching clause is found.
+ */
+
+static RestrictInfo *
+ec_search_clause_for_ems(PlannerInfo *root, EquivalenceClass *ec,
+ EquivalenceMember *leftem, EquivalenceMember *rightem,
+ EquivalenceClass *parent_ec)
+{
+ /* Check original source clauses */
+ foreach_node(RestrictInfo, rinfo, ec->ec_sources)
+ {
+ if (rinfo->left_em == leftem &&
+ rinfo->right_em == rightem &&
+ rinfo->parent_ec == parent_ec)
+ return rinfo;
+ if (rinfo->left_em == rightem &&
+ rinfo->right_em == leftem &&
+ rinfo->parent_ec == parent_ec)
+ return rinfo;
+ }
+
+ /* Not found in ec_sources; search derived clauses */
+ return ec_search_derived_clause_for_ems(root, ec, leftem, rightem,
+ parent_ec);
+}
+
+/*
+ * ec_search_derived_clause_for_ems
+ * Search for an existing derived clause between two EquivalenceMembers.
+ *
+ * If the number of derived clauses exceeds a threshold, switch to hash table
+ * lookup; otherwise, scan ec_derives_list linearly.
+ *
+ * Clauses involving constants are looked up using only the non-const EM
+ * (leftem) and a NULL rightem. In that case, we expect to find a clause with
+ * a constant on the RHS.
+ *
+ * We do not attempt a second lookup with EMs swapped when using the hash
+ * table; such clauses are inserted under both orderings at the time of
+ * insertion.
+ */
+static RestrictInfo *
+ec_search_derived_clause_for_ems(PlannerInfo *root, EquivalenceClass *ec,
+ EquivalenceMember *leftem,
+ EquivalenceMember *rightem,
+ EquivalenceClass *parent_ec)
+{
+ /*
+ * Switch to using hash lookup when list grows "too long". The threshold
+ * is arbitrary and is known only here.
+ */
+ if (!ec->ec_derives_hash && list_length(ec->ec_derives_list) >= 32)
+ build_ec_derives_hash(root, ec);
+
+ /* Perform hash table lookup if available */
+ if (ec->ec_derives_hash)
+ {
+ ECDerivesKey key;
+ RestrictInfo *rinfo;
+ ECDerivesEntry *entry;
+
+ /*
+ * See add_clause_to_ec_derives_hash() for rationale: derived clauses
+ * are inserted into the hash table under both (em1, em2) and
+ * (em2, em1), so a single lookup with the original order is
+ * sufficient.
+ */
+ key.em1 = leftem;
+ key.em2 = rightem;
+ key.parent_ec = parent_ec;
+ entry = derives_lookup(ec->ec_derives_hash, key);
+ if (entry)
+ {
+ rinfo = entry->rinfo;
+ Assert(rinfo);
+
+ /*
+ * If this is a lookup in a const-containing EC, the RHS must be a
+ * constant. The caller signals this by passing NULL for rightem.
+ */
+ Assert(rightem || rinfo->right_em->em_is_const);
+ return rinfo;
+ }
+ }
+ else
+ {
+ /* Fallback to linear search over ec_derives_list */
+ foreach_node(RestrictInfo, rinfo, ec->ec_derives_list)
+ {
+ /* Handle special case: lookup by non-const EM alone */
+ if (!rightem &&
+ rinfo->left_em == leftem)
+ {
+ /* See the comment above in hash path for rationale. */
+ Assert(rinfo->right_em->em_is_const);
+ return rinfo;
+ }
+ if (rinfo->left_em == leftem &&
+ rinfo->right_em == rightem &&
+ rinfo->parent_ec == parent_ec)
+ return rinfo;
+ if (rinfo->left_em == rightem &&
+ rinfo->right_em == leftem &&
+ rinfo->parent_ec == parent_ec)
+ return rinfo;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 84489cdc4b4..ae20691ca91 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -749,7 +749,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.)
*/
- clear_ec_derived_clauses(ec);
+ ec_clear_derived_clauses(ec);
}
/*
@@ -1544,7 +1544,7 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
list_free(ec->ec_members);
ec->ec_members = new_members;
- clear_ec_derived_clauses(ec);
+ ec_clear_derived_clauses(ec);
/* Update EC source expressions */
foreach_node(RestrictInfo, rinfo, ec->ec_sources)
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index d4af0df010e..ff5c69c84aa 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1403,6 +1403,17 @@ 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.
*
+ * Derived equality clauses between EC members are stored in ec_derives_list.
+ * For small queries, this list is scanned directly during lookup. For larger
+ * queries -- e.g., with many partitions or joins -- a hash table
+ * (ec_derives_hash) is built for faster lookup. Both structures contain the
+ * same RestrictInfos and are maintained in parallel. We retain the list even
+ * when the hash is used to simplify serialization (e.g., in
+ * _outEquivalenceClass()) and support EquivalenceClass merging.
+ *
+ * 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.
+ *
* 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.
*
@@ -1422,22 +1433,10 @@ typedef struct EquivalenceClass
Oid ec_collation; /* collation, if datatypes are collatable */
List *ec_members; /* list of EquivalenceMembers */
List *ec_sources; /* list of generating RestrictInfos */
-
- /*
- * ec_derives_list is a list of derived RestrictInfos. For small problems
- * we just scan the list to lookup RestrictInfos with given
- * EquivalenceMembers, but when there are many derived clauses, because of
- * many partitions or many tables being joined or both, we build hash
- * table for faster lookups. The hash table is present and valid when
- * ec_derives_hash is not NULL. Note that we still maintain the list even
- * when using the hash table for lookups; this simplifies life for
- * _outEquivalenceClass() and when merging two EquivalenceClasses.
- *
- * Note that ec_sources contains the clauses, mentioned in the query,
- * which are fewer. It does not need a hash table for lookups.
- */
- List *ec_derives_list;
- struct derives_hash *ec_derives_hash;
+ List *ec_derives_list; /* list of derived RestrictInfos */
+ struct derives_hash *ec_derives_hash; /* optional hash table for fast
+ * lookup; contains same
+ * RestrictInfos as list */
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 f80fcf606d6..16dc4d5ee82 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -198,7 +198,7 @@ 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 clear_ec_derived_clauses(EquivalenceClass *ec);
+extern void ec_clear_derived_clauses(EquivalenceClass *ec);
/*
* pathkeys.c
--
2.43.0