@ -47,7 +47,8 @@ static void ExecHashIncreaseNumBatches(HashJoinTable hashtable);
static void ExecHashIncreaseNumBuckets ( HashJoinTable hashtable ) ;
static void ExecHashIncreaseNumBuckets ( HashJoinTable hashtable ) ;
static void ExecParallelHashIncreaseNumBatches ( HashJoinTable hashtable ) ;
static void ExecParallelHashIncreaseNumBatches ( HashJoinTable hashtable ) ;
static void ExecParallelHashIncreaseNumBuckets ( HashJoinTable hashtable ) ;
static void ExecParallelHashIncreaseNumBuckets ( HashJoinTable hashtable ) ;
static void ExecHashBuildSkewHash ( HashJoinTable hashtable , Hash * node ,
static void ExecHashBuildSkewHash ( HashState * hashstate ,
HashJoinTable hashtable , Hash * node ,
int mcvsToUse ) ;
int mcvsToUse ) ;
static void ExecHashSkewTableInsert ( HashJoinTable hashtable ,
static void ExecHashSkewTableInsert ( HashJoinTable hashtable ,
TupleTableSlot * slot ,
TupleTableSlot * slot ,
@ -138,11 +139,9 @@ static void
MultiExecPrivateHash ( HashState * node )
MultiExecPrivateHash ( HashState * node )
{
{
PlanState * outerNode ;
PlanState * outerNode ;
List * hashkeys ;
HashJoinTable hashtable ;
HashJoinTable hashtable ;
TupleTableSlot * slot ;
TupleTableSlot * slot ;
ExprContext * econtext ;
ExprContext * econtext ;
uint32 hashvalue ;
/*
/*
* get state info from node
* get state info from node
@ -153,7 +152,6 @@ MultiExecPrivateHash(HashState *node)
/*
/*
* set expression context
* set expression context
*/
*/
hashkeys = node - > hashkeys ;
econtext = node - > ps . ps_ExprContext ;
econtext = node - > ps . ps_ExprContext ;
/*
/*
@ -162,15 +160,23 @@ MultiExecPrivateHash(HashState *node)
*/
*/
for ( ; ; )
for ( ; ; )
{
{
bool isnull ;
Datum hashdatum ;
slot = ExecProcNode ( outerNode ) ;
slot = ExecProcNode ( outerNode ) ;
if ( TupIsNull ( slot ) )
if ( TupIsNull ( slot ) )
break ;
break ;
/* We have to compute the hash value */
/* We have to compute the hash value */
econtext - > ecxt_outertuple = slot ;
econtext - > ecxt_outertuple = slot ;
if ( ExecHashGetHashValue ( hashtable , econtext , hashkeys ,
false , hashtable - > keepNulls ,
ResetExprContext ( econtext ) ;
& hashvalue ) )
hashdatum = ExecEvalExprSwitchContext ( node - > hash_expr , econtext ,
& isnull ) ;
if ( ! isnull )
{
{
uint32 hashvalue = DatumGetUInt32 ( hashdatum ) ;
int bucketNumber ;
int bucketNumber ;
bucketNumber = ExecHashGetSkewBucket ( hashtable , hashvalue ) ;
bucketNumber = ExecHashGetSkewBucket ( hashtable , hashvalue ) ;
@ -215,7 +221,6 @@ MultiExecParallelHash(HashState *node)
{
{
ParallelHashJoinState * pstate ;
ParallelHashJoinState * pstate ;
PlanState * outerNode ;
PlanState * outerNode ;
List * hashkeys ;
HashJoinTable hashtable ;
HashJoinTable hashtable ;
TupleTableSlot * slot ;
TupleTableSlot * slot ;
ExprContext * econtext ;
ExprContext * econtext ;
@ -232,7 +237,6 @@ MultiExecParallelHash(HashState *node)
/*
/*
* set expression context
* set expression context
*/
*/
hashkeys = node - > hashkeys ;
econtext = node - > ps . ps_ExprContext ;
econtext = node - > ps . ps_ExprContext ;
/*
/*
@ -279,13 +283,20 @@ MultiExecParallelHash(HashState *node)
ExecParallelHashTableSetCurrentBatch ( hashtable , 0 ) ;
ExecParallelHashTableSetCurrentBatch ( hashtable , 0 ) ;
for ( ; ; )
for ( ; ; )
{
{
bool isnull ;
slot = ExecProcNode ( outerNode ) ;
slot = ExecProcNode ( outerNode ) ;
if ( TupIsNull ( slot ) )
if ( TupIsNull ( slot ) )
break ;
break ;
econtext - > ecxt_outertuple = slot ;
econtext - > ecxt_outertuple = slot ;
if ( ExecHashGetHashValue ( hashtable , econtext , hashkeys ,
false , hashtable - > keepNulls ,
ResetExprContext ( econtext ) ;
& hashvalue ) )
hashvalue = DatumGetUInt32 ( ExecEvalExprSwitchContext ( node - > hash_expr ,
econtext ,
& isnull ) ) ;
if ( ! isnull )
ExecParallelHashTableInsert ( hashtable , slot , hashvalue ) ;
ExecParallelHashTableInsert ( hashtable , slot , hashvalue ) ;
hashtable - > partialTuples + + ;
hashtable - > partialTuples + + ;
}
}
@ -371,8 +382,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
hashstate - > ps . plan = ( Plan * ) node ;
hashstate - > ps . plan = ( Plan * ) node ;
hashstate - > ps . state = estate ;
hashstate - > ps . state = estate ;
hashstate - > ps . ExecProcNode = ExecHash ;
hashstate - > ps . ExecProcNode = ExecHash ;
/* delay building hashtable until ExecHashTableCreate() in executor run */
hashstate - > hashtable = NULL ;
hashstate - > hashtable = NULL ;
hashstate - > hashkeys = NIL ; /* will be set by parent HashJoin */
/*
/*
* Miscellaneous initialization
* Miscellaneous initialization
@ -393,12 +404,16 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
ExecInitResultTupleSlotTL ( & hashstate - > ps , & TTSOpsMinimalTuple ) ;
ExecInitResultTupleSlotTL ( & hashstate - > ps , & TTSOpsMinimalTuple ) ;
hashstate - > ps . ps_ProjInfo = NULL ;
hashstate - > ps . ps_ProjInfo = NULL ;
Assert ( node - > plan . qual = = NIL ) ;
/*
/*
* initialize child expressions
* Delay initialization of hash_expr until ExecInitHashJoin ( ) . We cannot
* build the ExprState here as we don ' t yet know the join type we ' re going
* to be hashing values for and we need to know that before calling
* ExecBuildHash32Expr as the keep_nulls parameter depends on the join
* type .
*/
*/
Assert ( node - > plan . qual = = NIL ) ;
hashstate - > hash_expr = NULL ;
hashstate - > hashkeys =
ExecInitExprList ( node - > hashkeys , ( PlanState * ) hashstate ) ;
return hashstate ;
return hashstate ;
}
}
@ -429,7 +444,7 @@ ExecEndHash(HashState *node)
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
*/
HashJoinTable
HashJoinTable
ExecHashTableCreate ( HashState * state , List * hashOperators , List * hashCollations , bool keepNulls )
ExecHashTableCreate ( HashState * state )
{
{
Hash * node ;
Hash * node ;
HashJoinTable hashtable ;
HashJoinTable hashtable ;
@ -440,10 +455,6 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
double rows ;
double rows ;
int num_skew_mcvs ;
int num_skew_mcvs ;
int log2_nbuckets ;
int log2_nbuckets ;
int nkeys ;
int i ;
ListCell * ho ;
ListCell * hc ;
MemoryContext oldcxt ;
MemoryContext oldcxt ;
/*
/*
@ -487,7 +498,6 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
hashtable - > log2_nbuckets = log2_nbuckets ;
hashtable - > log2_nbuckets = log2_nbuckets ;
hashtable - > log2_nbuckets_optimal = log2_nbuckets ;
hashtable - > log2_nbuckets_optimal = log2_nbuckets ;
hashtable - > buckets . unshared = NULL ;
hashtable - > buckets . unshared = NULL ;
hashtable - > keepNulls = keepNulls ;
hashtable - > skewEnabled = false ;
hashtable - > skewEnabled = false ;
hashtable - > skewBucket = NULL ;
hashtable - > skewBucket = NULL ;
hashtable - > skewBucketLen = 0 ;
hashtable - > skewBucketLen = 0 ;
@ -540,32 +550,6 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
oldcxt = MemoryContextSwitchTo ( hashtable - > hashCxt ) ;
oldcxt = MemoryContextSwitchTo ( hashtable - > hashCxt ) ;
/*
* Get info about the hash functions to be used for each hash key . Also
* remember whether the join operators are strict .
*/
nkeys = list_length ( hashOperators ) ;
hashtable - > outer_hashfunctions = palloc_array ( FmgrInfo , nkeys ) ;
hashtable - > inner_hashfunctions = palloc_array ( FmgrInfo , nkeys ) ;
hashtable - > hashStrict = palloc_array ( bool , nkeys ) ;
hashtable - > collations = palloc_array ( Oid , nkeys ) ;
i = 0 ;
forboth ( ho , hashOperators , hc , hashCollations )
{
Oid hashop = lfirst_oid ( ho ) ;
Oid left_hashfn ;
Oid right_hashfn ;
if ( ! get_op_hash_functions ( hashop , & left_hashfn , & right_hashfn ) )
elog ( ERROR , " could not find hash function for hash operator %u " ,
hashop ) ;
fmgr_info ( left_hashfn , & hashtable - > outer_hashfunctions [ i ] ) ;
fmgr_info ( right_hashfn , & hashtable - > inner_hashfunctions [ i ] ) ;
hashtable - > hashStrict [ i ] = op_strict ( hashop ) ;
hashtable - > collations [ i ] = lfirst_oid ( hc ) ;
i + + ;
}
if ( nbatch > 1 & & hashtable - > parallel_state = = NULL )
if ( nbatch > 1 & & hashtable - > parallel_state = = NULL )
{
{
MemoryContext oldctx ;
MemoryContext oldctx ;
@ -652,7 +636,7 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
* it . )
* it . )
*/
*/
if ( nbatch > 1 )
if ( nbatch > 1 )
ExecHashBuildSkewHash ( hashtable , node , num_skew_mcvs ) ;
ExecHashBuildSkewHash ( state , hashtable , node , num_skew_mcvs ) ;
MemoryContextSwitchTo ( oldcxt ) ;
MemoryContextSwitchTo ( oldcxt ) ;
}
}
@ -1803,103 +1787,6 @@ ExecParallelHashTableInsertCurrentBatch(HashJoinTable hashtable,
heap_free_minimal_tuple ( tuple ) ;
heap_free_minimal_tuple ( tuple ) ;
}
}
/*
* ExecHashGetHashValue
* Compute the hash value for a tuple
*
* The tuple to be tested must be in econtext - > ecxt_outertuple ( thus Vars in
* the hashkeys expressions need to have OUTER_VAR as varno ) . If outer_tuple
* is false ( meaning it ' s the HashJoin ' s inner node , Hash ) , econtext ,
* hashkeys , and slot need to be from Hash , with hashkeys / slot referencing and
* being suitable for tuples from the node below the Hash . Conversely , if
* outer_tuple is true , econtext is from HashJoin , and hashkeys / slot need to
* be appropriate for tuples from HashJoin ' s outer node .
*
* A true result means the tuple ' s hash value has been successfully computed
* and stored at * hashvalue . A false result means the tuple cannot match
* because it contains a null attribute , and hence it should be discarded
* immediately . ( If keep_nulls is true then false is never returned . )
*/
bool
ExecHashGetHashValue ( HashJoinTable hashtable ,
ExprContext * econtext ,
List * hashkeys ,
bool outer_tuple ,
bool keep_nulls ,
uint32 * hashvalue )
{
uint32 hashkey = 0 ;
FmgrInfo * hashfunctions ;
ListCell * hk ;
int i = 0 ;
MemoryContext oldContext ;
/*
* We reset the eval context each time to reclaim any memory leaked in the
* hashkey expressions .
*/
ResetExprContext ( econtext ) ;
oldContext = MemoryContextSwitchTo ( econtext - > ecxt_per_tuple_memory ) ;
if ( outer_tuple )
hashfunctions = hashtable - > outer_hashfunctions ;
else
hashfunctions = hashtable - > inner_hashfunctions ;
foreach ( hk , hashkeys )
{
ExprState * keyexpr = ( ExprState * ) lfirst ( hk ) ;
Datum keyval ;
bool isNull ;
/* combine successive hashkeys by rotating */
hashkey = pg_rotate_left32 ( hashkey , 1 ) ;
/*
* Get the join attribute value of the tuple
*/
keyval = ExecEvalExpr ( keyexpr , econtext , & isNull ) ;
/*
* If the attribute is NULL , and the join operator is strict , then
* this tuple cannot pass the join qual so we can reject it
* immediately ( unless we ' re scanning the outside of an outer join , in
* which case we must not reject it ) . Otherwise we act like the
* hashcode of NULL is zero ( this will support operators that act like
* IS NOT DISTINCT , though not any more - random behavior ) . We treat
* the hash support function as strict even if the operator is not .
*
* Note : currently , all hashjoinable operators must be strict since
* the hash index AM assumes that . However , it takes so little extra
* code here to allow non - strict that we may as well do it .
*/
if ( isNull )
{
if ( hashtable - > hashStrict [ i ] & & ! keep_nulls )
{
MemoryContextSwitchTo ( oldContext ) ;
return false ; /* cannot match */
}
/* else, leave hashkey unmodified, equivalent to hashcode 0 */
}
else
{
/* Compute the hash function */
uint32 hkey ;
hkey = DatumGetUInt32 ( FunctionCall1Coll ( & hashfunctions [ i ] , hashtable - > collations [ i ] , keyval ) ) ;
hashkey ^ = hkey ;
}
i + + ;
}
MemoryContextSwitchTo ( oldContext ) ;
* hashvalue = hashkey ;
return true ;
}
/*
/*
* ExecHashGetBucketAndBatch
* ExecHashGetBucketAndBatch
@ -2372,7 +2259,8 @@ ExecReScanHash(HashState *node)
* based on available memory .
* based on available memory .
*/
*/
static void
static void
ExecHashBuildSkewHash ( HashJoinTable hashtable , Hash * node , int mcvsToUse )
ExecHashBuildSkewHash ( HashState * hashstate , HashJoinTable hashtable ,
Hash * node , int mcvsToUse )
{
{
HeapTupleData * statsTuple ;
HeapTupleData * statsTuple ;
AttStatsSlot sslot ;
AttStatsSlot sslot ;
@ -2400,7 +2288,6 @@ ExecHashBuildSkewHash(HashJoinTable hashtable, Hash *node, int mcvsToUse)
{
{
double frac ;
double frac ;
int nbuckets ;
int nbuckets ;
FmgrInfo * hashfunctions ;
int i ;
int i ;
if ( mcvsToUse > sslot . nvalues )
if ( mcvsToUse > sslot . nvalues )
@ -2468,15 +2355,14 @@ ExecHashBuildSkewHash(HashJoinTable hashtable, Hash *node, int mcvsToUse)
* ExecHashRemoveNextSkewBucket ) and we want the least common MCVs to
* ExecHashRemoveNextSkewBucket ) and we want the least common MCVs to
* be removed first .
* be removed first .
*/
*/
hashfunctions = hashtable - > outer_hashfunctions ;
for ( i = 0 ; i < mcvsToUse ; i + + )
for ( i = 0 ; i < mcvsToUse ; i + + )
{
{
uint32 hashvalue ;
uint32 hashvalue ;
int bucket ;
int bucket ;
hashvalue = DatumGetUInt32 ( FunctionCall1Coll ( & hashfunctions [ 0 ] ,
hashvalue = DatumGetUInt32 ( FunctionCall1Coll ( hashstate - > skew_hashfunction ,
hashtabl e - > collations [ 0 ] ,
hashstat e - > skew_ collation,
sslot . values [ i ] ) ) ;
sslot . values [ i ] ) ) ;
/*
/*