@ -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 generat e
* 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 b e
* 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 ther e
* 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 ;
}