|
|
|
@ -20,6 +20,7 @@
|
|
|
|
|
|
|
|
|
|
#include "access/stratnum.h"
|
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
|
#include "common/hashfn.h"
|
|
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
|
#include "optimizer/appendinfo.h"
|
|
|
|
@ -72,7 +73,56 @@ 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 ec_build_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 ec_add_clause_to_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 identifying a derived clause.
|
|
|
|
|
*
|
|
|
|
|
* This structure should not be filled manually. Use fill_ec_derives_key() to
|
|
|
|
|
* set it up in canonical form.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
EquivalenceMember *em1;
|
|
|
|
|
EquivalenceMember *em2;
|
|
|
|
|
EquivalenceClass *parent_ec;
|
|
|
|
|
} ECDerivesKey;
|
|
|
|
|
|
|
|
|
|
/* Hash table entry in ec_derives_hash. */
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint32 status;
|
|
|
|
|
ECDerivesKey key;
|
|
|
|
|
RestrictInfo *rinfo;
|
|
|
|
|
} ECDerivesEntry;
|
|
|
|
|
|
|
|
|
|
/* Threshold for switching from list to hash table */
|
|
|
|
|
#define EC_DERIVES_HASH_THRESHOLD 32
|
|
|
|
|
|
|
|
|
|
#define SH_PREFIX derives
|
|
|
|
|
#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_SCOPE static inline
|
|
|
|
|
#define SH_DECLARE
|
|
|
|
|
#define SH_DEFINE
|
|
|
|
|
#include "lib/simplehash.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* process_equivalence
|
|
|
|
@ -342,7 +392,12 @@ 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);
|
|
|
|
|
ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Appends ec2's derived clauses to ec1->ec_derives_list and adds them
|
|
|
|
|
* to ec1->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 */
|
|
|
|
@ -355,7 +410,7 @@ process_equivalence(PlannerInfo *root,
|
|
|
|
|
/* just to avoid debugging confusion w/ dangling pointers: */
|
|
|
|
|
ec2->ec_members = NIL;
|
|
|
|
|
ec2->ec_sources = NIL;
|
|
|
|
|
ec2->ec_derives = NIL;
|
|
|
|
|
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,
|
|
|
|
@ -412,7 +467,8 @@ process_equivalence(PlannerInfo *root,
|
|
|
|
|
ec->ec_collation = collation;
|
|
|
|
|
ec->ec_members = NIL;
|
|
|
|
|
ec->ec_sources = list_make1(restrictinfo);
|
|
|
|
|
ec->ec_derives = NIL;
|
|
|
|
|
ec->ec_derives_list = NIL;
|
|
|
|
|
ec->ec_derives_hash = NULL;
|
|
|
|
|
ec->ec_relids = NULL;
|
|
|
|
|
ec->ec_has_const = false;
|
|
|
|
|
ec->ec_has_volatile = false;
|
|
|
|
@ -671,7 +727,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
|
|
|
|
newec->ec_collation = collation;
|
|
|
|
|
newec->ec_members = NIL;
|
|
|
|
|
newec->ec_sources = NIL;
|
|
|
|
|
newec->ec_derives = NIL;
|
|
|
|
|
newec->ec_derives_list = NIL;
|
|
|
|
|
newec->ec_derives_hash = NULL;
|
|
|
|
|
newec->ec_relids = NULL;
|
|
|
|
|
newec->ec_has_const = false;
|
|
|
|
|
newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
|
|
|
|
@ -1026,8 +1083,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 ec_sources or ec_derives 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)
|
|
|
|
@ -1188,11 +1245,11 @@ 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
|
|
|
|
|
* will not re-use such clauses directly, but selectivity estimation
|
|
|
|
|
* may consult the list later. Note that this use of ec_derives does
|
|
|
|
|
* not overlap with its use for join clauses, since we never generate
|
|
|
|
|
* join clauses from an ec_has_const eclass.)
|
|
|
|
|
* markings for a mergejoinable clause, and save it as a derived
|
|
|
|
|
* clause. (We will not re-use such clauses directly, but selectivity
|
|
|
|
|
* estimation may consult those later. Note that this use of derived
|
|
|
|
|
* clauses does not overlap with its use for join clauses, since we
|
|
|
|
|
* never generate join clauses from an ec_has_const eclass.)
|
|
|
|
|
*/
|
|
|
|
|
if (rinfo && rinfo->mergeopfamilies)
|
|
|
|
|
{
|
|
|
|
@ -1200,7 +1257,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;
|
|
|
|
|
ec->ec_derives = lappend(ec->ec_derives, rinfo);
|
|
|
|
|
ec_add_derived_clause(ec, rinfo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1265,10 +1322,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 put it
|
|
|
|
|
* in ec_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.
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
|
|
|
@ -1369,7 +1426,7 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
|
|
|
|
|
* 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
|
|
|
|
|
* or previously-built derived clause. 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.
|
|
|
|
@ -1754,9 +1811,9 @@ 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
|
|
|
|
|
* any duplication, and it's a sufficiently narrow corner case that we
|
|
|
|
|
* shouldn't sweat too much over it anyway.
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Since inner_rel might be an indirect descendant of the baserel
|
|
|
|
|
* mentioned in the ec_sources clauses, we have to be prepared to apply
|
|
|
|
@ -1823,43 +1880,11 @@ create_join_clause(PlannerInfo *root,
|
|
|
|
|
{
|
|
|
|
|
RestrictInfo *rinfo;
|
|
|
|
|
RestrictInfo *parent_rinfo = NULL;
|
|
|
|
|
ListCell *lc;
|
|
|
|
|
MemoryContext oldcontext;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Search to see if we already built a RestrictInfo for this pair of
|
|
|
|
|
* EquivalenceMembers. 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.
|
|
|
|
|
*/
|
|
|
|
|
foreach(lc, ec->ec_sources)
|
|
|
|
|
{
|
|
|
|
|
rinfo = (RestrictInfo *) lfirst(lc);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(lc, ec->ec_derives)
|
|
|
|
|
{
|
|
|
|
|
rinfo = (RestrictInfo *) lfirst(lc);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
rinfo = ec_search_clause_for_ems(root, ec, leftem, rightem, parent_ec);
|
|
|
|
|
if (rinfo)
|
|
|
|
|
return rinfo;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Not there, so build it, in planner context so we can re-use it. (Not
|
|
|
|
@ -1923,7 +1948,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);
|
|
|
|
|
ec_add_derived_clause(ec, rinfo);
|
|
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
|
|
|
|
|
@ -2648,28 +2673,14 @@ 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;
|
|
|
|
|
|
|
|
|
|
Assert(ec->ec_has_const);
|
|
|
|
|
Assert(!em->em_is_const);
|
|
|
|
|
foreach(lc, ec->ec_derives)
|
|
|
|
|
{
|
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* generate_base_implied_equalities_const will have put non-const
|
|
|
|
|
* members on the left side of derived clauses.
|
|
|
|
|
*/
|
|
|
|
|
if (rinfo->left_em == em)
|
|
|
|
|
{
|
|
|
|
|
Assert(rinfo->right_em->em_is_const);
|
|
|
|
|
return rinfo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
return ec_search_derived_clause_for_ems(root, ec, em, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3442,3 +3453,281 @@ 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ec_build_derives_hash
|
|
|
|
|
* Construct the auxiliary hash table for derived clause lookups.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
ec_build_derives_hash(PlannerInfo *root, EquivalenceClass *ec)
|
|
|
|
|
{
|
|
|
|
|
Assert(!ec->ec_derives_hash);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create the hash table.
|
|
|
|
|
*
|
|
|
|
|
* We pass list_length(ec->ec_derives_list) as the initial size.
|
|
|
|
|
* Simplehash will divide this by the fillfactor (typically 0.9) and round
|
|
|
|
|
* up to the next power of two, so this will usually give us at least 64
|
|
|
|
|
* buckets around the threshold. That avoids immediate resizing without
|
|
|
|
|
* hardcoding a specific size.
|
|
|
|
|
*/
|
|
|
|
|
ec->ec_derives_hash = derives_create(root->planner_cxt,
|
|
|
|
|
list_length(ec->ec_derives_list),
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
foreach_node(RestrictInfo, rinfo, ec->ec_derives_list)
|
|
|
|
|
ec_add_clause_to_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.
|
|
|
|
|
*
|
|
|
|
|
* Also asserts expected invariants of derived clauses.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
ec_add_derived_clause(EquivalenceClass *ec, RestrictInfo *clause)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Constant, if present, is always placed on the RHS; see
|
|
|
|
|
* generate_base_implied_equalities_const(). LHS is never a constant.
|
|
|
|
|
*/
|
|
|
|
|
Assert(!clause->left_em->em_is_const);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Clauses containing a constant are never considered redundant, so
|
|
|
|
|
* parent_ec is not set.
|
|
|
|
|
*/
|
|
|
|
|
Assert(!clause->parent_ec || !clause->right_em->em_is_const);
|
|
|
|
|
|
|
|
|
|
ec->ec_derives_list = lappend(ec->ec_derives_list, clause);
|
|
|
|
|
if (ec->ec_derives_hash)
|
|
|
|
|
ec_add_clause_to_derives_hash(ec, clause);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 ec_add_derived_clause() but optimized for adding
|
|
|
|
|
* multiple clauses at a time to the ec_derives_list. The assertions from
|
|
|
|
|
* ec_add_derived_clause() are not repeated here, as the input clauses are
|
|
|
|
|
* assumed to have already been validated.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
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)
|
|
|
|
|
ec_add_clause_to_derives_hash(ec, rinfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* fill_ec_derives_key
|
|
|
|
|
* Compute a canonical key for ec_derives_hash lookup or insertion.
|
|
|
|
|
*
|
|
|
|
|
* Derived clauses are looked up using a pair of EquivalenceMembers and a
|
|
|
|
|
* parent EquivalenceClass. To avoid storing or searching for both EM orderings,
|
|
|
|
|
* we canonicalize the key:
|
|
|
|
|
*
|
|
|
|
|
* - For clauses involving two non-constant EMs, em1 is set to the EM with lower
|
|
|
|
|
* memory address and em2 is set to the other one.
|
|
|
|
|
* - For clauses involving a constant EM, the caller must pass the non-constant
|
|
|
|
|
* EM as leftem and NULL as rightem; we then set em1 = NULL and em2 = leftem.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
fill_ec_derives_key(ECDerivesKey *key,
|
|
|
|
|
EquivalenceMember *leftem,
|
|
|
|
|
EquivalenceMember *rightem,
|
|
|
|
|
EquivalenceClass *parent_ec)
|
|
|
|
|
{
|
|
|
|
|
Assert(leftem); /* Always required for lookup or insertion */
|
|
|
|
|
|
|
|
|
|
if (rightem == NULL)
|
|
|
|
|
{
|
|
|
|
|
key->em1 = NULL;
|
|
|
|
|
key->em2 = leftem;
|
|
|
|
|
}
|
|
|
|
|
else if (leftem < rightem)
|
|
|
|
|
{
|
|
|
|
|
key->em1 = leftem;
|
|
|
|
|
key->em2 = rightem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
key->em1 = rightem;
|
|
|
|
|
key->em2 = leftem;
|
|
|
|
|
}
|
|
|
|
|
key->parent_ec = parent_ec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ec_add_clause_to_derives_hash
|
|
|
|
|
* Add a derived clause to ec_derives_hash in the given EquivalenceClass.
|
|
|
|
|
*
|
|
|
|
|
* Each clause is associated with a canonicalized key. For constant-containing
|
|
|
|
|
* clauses, only the non-constant EM is used for lookup; see comments in
|
|
|
|
|
* fill_ec_derives_key().
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
ec_add_clause_to_derives_hash(EquivalenceClass *ec, RestrictInfo *rinfo)
|
|
|
|
|
{
|
|
|
|
|
ECDerivesKey key;
|
|
|
|
|
ECDerivesEntry *entry;
|
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Constants are always placed on the RHS; see
|
|
|
|
|
* generate_base_implied_equalities_const().
|
|
|
|
|
*/
|
|
|
|
|
Assert(!rinfo->left_em->em_is_const);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Clauses containing a constant are never considered redundant, so
|
|
|
|
|
* parent_ec is not set.
|
|
|
|
|
*/
|
|
|
|
|
Assert(!rinfo->parent_ec || !rinfo->right_em->em_is_const);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* See fill_ec_derives_key() for details: we use a canonicalized key to
|
|
|
|
|
* avoid storing both EM orderings. For constant EMs, only the
|
|
|
|
|
* non-constant EM is included in the key.
|
|
|
|
|
*/
|
|
|
|
|
fill_ec_derives_key(&key,
|
|
|
|
|
rinfo->left_em,
|
|
|
|
|
rinfo->right_em->em_is_const ? NULL : rinfo->right_em,
|
|
|
|
|
rinfo->parent_ec);
|
|
|
|
|
entry = derives_insert(ec->ec_derives_hash, key, &found);
|
|
|
|
|
Assert(!found);
|
|
|
|
|
entry->rinfo = rinfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ec_clear_derived_clauses
|
|
|
|
|
* Reset ec_derives_list and ec_derives_hash.
|
|
|
|
|
*
|
|
|
|
|
* We destroy the hash table explicitly, since it may consume significant
|
|
|
|
|
* space. The list holds the same set of entries and can become equally large
|
|
|
|
|
* when thousands of partitions are involved, so we free it as well -- even
|
|
|
|
|
* though we do not typically free lists.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
ec_clear_derived_clauses(EquivalenceClass *ec)
|
|
|
|
|
{
|
|
|
|
|
list_free(ec->ec_derives_list);
|
|
|
|
|
ec->ec_derives_list = NIL;
|
|
|
|
|
|
|
|
|
|
if (ec->ec_derives_hash)
|
|
|
|
|
{
|
|
|
|
|
derives_destroy(ec->ec_derives_hash);
|
|
|
|
|
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.
|
|
|
|
|
*
|
|
|
|
|
* Returns a clause with matching operands in either given order or commuted
|
|
|
|
|
* order. 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 by passing the non-constant EM
|
|
|
|
|
* as leftem and setting rightem to NULL. In that case, we expect to find a
|
|
|
|
|
* clause with a constant on the RHS.
|
|
|
|
|
*
|
|
|
|
|
* While searching the list, we compare each given EM with both sides of each
|
|
|
|
|
* clause. But for hash table lookups, we construct a canonicalized key and
|
|
|
|
|
* perform a single lookup.
|
|
|
|
|
*/
|
|
|
|
|
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". */
|
|
|
|
|
if (!ec->ec_derives_hash &&
|
|
|
|
|
list_length(ec->ec_derives_list) >= EC_DERIVES_HASH_THRESHOLD)
|
|
|
|
|
ec_build_derives_hash(root, ec);
|
|
|
|
|
|
|
|
|
|
/* Perform hash table lookup if available */
|
|
|
|
|
if (ec->ec_derives_hash)
|
|
|
|
|
{
|
|
|
|
|
ECDerivesKey key;
|
|
|
|
|
RestrictInfo *rinfo;
|
|
|
|
|
ECDerivesEntry *entry;
|
|
|
|
|
|
|
|
|
|
fill_ec_derives_key(&key, leftem, rightem, parent_ec);
|
|
|
|
|
entry = derives_lookup(ec->ec_derives_hash, key);
|
|
|
|
|
if (entry)
|
|
|
|
|
{
|
|
|
|
|
rinfo = entry->rinfo;
|
|
|
|
|
Assert(rinfo);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|