From bd1faff01cb6a669a38a8bb9440ba93cd82de262 Mon Sep 17 00:00:00 2001 From: Amit Langote 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