Create accessor functions for TupleHashEntry.

Refactor for upcoming optimizations.

Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/1cc3b400a0e8eead18ff967436fa9e42c0c14cfb.camel@j-davis.com
This commit is contained in:
Jeff Davis 2025-03-24 22:05:41 -07:00
parent cc721c459d
commit 4d143509cb
6 changed files with 83 additions and 33 deletions

View File

@ -174,13 +174,15 @@ BuildTupleHashTable(PlanState *parent,
bool use_variable_hash_iv) bool use_variable_hash_iv)
{ {
TupleHashTable hashtable; TupleHashTable hashtable;
Size entrysize = sizeof(TupleHashEntryData) + additionalsize; Size entrysize;
Size hash_mem_limit; Size hash_mem_limit;
MemoryContext oldcontext; MemoryContext oldcontext;
bool allow_jit; bool allow_jit;
uint32 hash_iv = 0; uint32 hash_iv = 0;
Assert(nbuckets > 0); Assert(nbuckets > 0);
additionalsize = MAXALIGN(additionalsize);
entrysize = sizeof(TupleHashEntryData) + additionalsize;
/* Limit initial table size request to not more than hash_mem */ /* Limit initial table size request to not more than hash_mem */
hash_mem_limit = get_hash_memory_limit() / entrysize; hash_mem_limit = get_hash_memory_limit() / entrysize;
@ -196,6 +198,7 @@ BuildTupleHashTable(PlanState *parent,
hashtable->tab_collations = collations; hashtable->tab_collations = collations;
hashtable->tablecxt = tablecxt; hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt; hashtable->tempcxt = tempcxt;
hashtable->additionalsize = additionalsize;
hashtable->tableslot = NULL; /* will be made on first lookup */ hashtable->tableslot = NULL; /* will be made on first lookup */
hashtable->inputslot = NULL; hashtable->inputslot = NULL;
hashtable->in_hash_expr = NULL; hashtable->in_hash_expr = NULL;
@ -479,11 +482,14 @@ LookupTupleHashEntry_internal(TupleHashTable hashtable, TupleTableSlot *slot,
{ {
/* created new entry */ /* created new entry */
*isnew = true; *isnew = true;
/* zero caller data */
entry->additional = NULL;
MemoryContextSwitchTo(hashtable->tablecxt); MemoryContextSwitchTo(hashtable->tablecxt);
/* Copy the first tuple into the table context */
entry->firstTuple = ExecCopySlotMinimalTuple(slot); entry->firstTuple = ExecCopySlotMinimalTuple(slot);
if (hashtable->additionalsize > 0)
entry->additional = palloc0(hashtable->additionalsize);
else
entry->additional = NULL;
} }
} }
else else

View File

@ -1491,7 +1491,7 @@ build_hash_tables(AggState *aggstate)
#ifdef USE_INJECTION_POINTS #ifdef USE_INJECTION_POINTS
if (IS_INJECTION_POINT_ATTACHED("hash-aggregate-oversize-table")) if (IS_INJECTION_POINT_ATTACHED("hash-aggregate-oversize-table"))
{ {
nbuckets = memory / sizeof(TupleHashEntryData); nbuckets = memory / TupleHashEntrySize();
INJECTION_POINT_CACHED("hash-aggregate-oversize-table"); INJECTION_POINT_CACHED("hash-aggregate-oversize-table");
} }
#endif #endif
@ -1724,7 +1724,7 @@ hash_agg_entry_size(int numTrans, Size tupleWidth, Size transitionSpace)
transitionChunkSize = 0; transitionChunkSize = 0;
return return
sizeof(TupleHashEntryData) + TupleHashEntrySize() +
tupleChunkSize + tupleChunkSize +
pergroupChunkSize + pergroupChunkSize +
transitionChunkSize; transitionChunkSize;
@ -1988,7 +1988,7 @@ hash_agg_update_metrics(AggState *aggstate, bool from_tape, int npartitions)
if (aggstate->hash_ngroups_current > 0) if (aggstate->hash_ngroups_current > 0)
{ {
aggstate->hashentrysize = aggstate->hashentrysize =
sizeof(TupleHashEntryData) + TupleHashEntrySize() +
(hashkey_mem / (double) aggstate->hash_ngroups_current); (hashkey_mem / (double) aggstate->hash_ngroups_current);
} }
} }
@ -2147,11 +2147,7 @@ initialize_hash_entry(AggState *aggstate, TupleHashTable hashtable,
if (aggstate->numtrans == 0) if (aggstate->numtrans == 0)
return; return;
pergroup = (AggStatePerGroup) pergroup = (AggStatePerGroup) TupleHashEntryGetAdditional(hashtable, entry);
MemoryContextAlloc(hashtable->tablecxt,
sizeof(AggStatePerGroupData) * aggstate->numtrans);
entry->additional = pergroup;
/* /*
* Initialize aggregates for new tuple group, lookup_hash_entries() * Initialize aggregates for new tuple group, lookup_hash_entries()
@ -2213,7 +2209,7 @@ lookup_hash_entries(AggState *aggstate)
{ {
if (isnew) if (isnew)
initialize_hash_entry(aggstate, hashtable, entry); initialize_hash_entry(aggstate, hashtable, entry);
pergroup[setno] = entry->additional; pergroup[setno] = TupleHashEntryGetAdditional(hashtable, entry);
} }
else else
{ {
@ -2748,6 +2744,7 @@ agg_refill_hash_table(AggState *aggstate)
{ {
TupleTableSlot *spillslot = aggstate->hash_spill_rslot; TupleTableSlot *spillslot = aggstate->hash_spill_rslot;
TupleTableSlot *hashslot = perhash->hashslot; TupleTableSlot *hashslot = perhash->hashslot;
TupleHashTable hashtable = perhash->hashtable;
TupleHashEntry entry; TupleHashEntry entry;
MinimalTuple tuple; MinimalTuple tuple;
uint32 hash; uint32 hash;
@ -2766,14 +2763,14 @@ agg_refill_hash_table(AggState *aggstate)
prepare_hash_slot(perhash, prepare_hash_slot(perhash,
aggstate->tmpcontext->ecxt_outertuple, aggstate->tmpcontext->ecxt_outertuple,
hashslot); hashslot);
entry = LookupTupleHashEntryHash(perhash->hashtable, hashslot, entry = LookupTupleHashEntryHash(hashtable, hashslot,
p_isnew, hash); p_isnew, hash);
if (entry != NULL) if (entry != NULL)
{ {
if (isnew) if (isnew)
initialize_hash_entry(aggstate, perhash->hashtable, entry); initialize_hash_entry(aggstate, hashtable, entry);
aggstate->hash_pergroup[batch->setno] = entry->additional; aggstate->hash_pergroup[batch->setno] = TupleHashEntryGetAdditional(hashtable, entry);
advance_aggregates(aggstate); advance_aggregates(aggstate);
} }
else else
@ -2865,7 +2862,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
ExprContext *econtext; ExprContext *econtext;
AggStatePerAgg peragg; AggStatePerAgg peragg;
AggStatePerGroup pergroup; AggStatePerGroup pergroup;
TupleHashEntryData *entry; TupleHashEntry entry;
TupleTableSlot *firstSlot; TupleTableSlot *firstSlot;
TupleTableSlot *result; TupleTableSlot *result;
AggStatePerHash perhash; AggStatePerHash perhash;
@ -2892,6 +2889,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
for (;;) for (;;)
{ {
TupleTableSlot *hashslot = perhash->hashslot; TupleTableSlot *hashslot = perhash->hashslot;
TupleHashTable hashtable = perhash->hashtable;
int i; int i;
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
@ -2899,7 +2897,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
/* /*
* Find the next entry in the hash table * Find the next entry in the hash table
*/ */
entry = ScanTupleHashTable(perhash->hashtable, &perhash->hashiter); entry = ScanTupleHashTable(hashtable, &perhash->hashiter);
if (entry == NULL) if (entry == NULL)
{ {
int nextset = aggstate->current_set + 1; int nextset = aggstate->current_set + 1;
@ -2914,7 +2912,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
perhash = &aggstate->perhash[aggstate->current_set]; perhash = &aggstate->perhash[aggstate->current_set];
ResetTupleHashIterator(perhash->hashtable, &perhash->hashiter); ResetTupleHashIterator(hashtable, &perhash->hashiter);
continue; continue;
} }
@ -2937,7 +2935,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
* Transform representative tuple back into one with the right * Transform representative tuple back into one with the right
* columns. * columns.
*/ */
ExecStoreMinimalTuple(entry->firstTuple, hashslot, false); ExecStoreMinimalTuple(TupleHashEntryGetTuple(entry), hashslot, false);
slot_getallattrs(hashslot); slot_getallattrs(hashslot);
ExecClearTuple(firstSlot); ExecClearTuple(firstSlot);
@ -2953,7 +2951,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
} }
ExecStoreVirtualTuple(firstSlot); ExecStoreVirtualTuple(firstSlot);
pergroup = (AggStatePerGroup) entry->additional; pergroup = (AggStatePerGroup) TupleHashEntryGetAdditional(hashtable, entry);
/* /*
* Use the representative input tuple for any references to * Use the representative input tuple for any references to

View File

@ -424,7 +424,9 @@ setop_fill_hash_table(SetOpState *setopstate)
for (;;) for (;;)
{ {
TupleTableSlot *outerslot; TupleTableSlot *outerslot;
TupleHashTable hashtable = setopstate->hashtable;
TupleHashEntryData *entry; TupleHashEntryData *entry;
SetOpStatePerGroup pergroup;
bool isnew; bool isnew;
outerslot = ExecProcNode(outerPlan); outerslot = ExecProcNode(outerPlan);
@ -433,20 +435,20 @@ setop_fill_hash_table(SetOpState *setopstate)
have_tuples = true; have_tuples = true;
/* Find or build hashtable entry for this tuple's group */ /* Find or build hashtable entry for this tuple's group */
entry = LookupTupleHashEntry(setopstate->hashtable, entry = LookupTupleHashEntry(hashtable,
outerslot, outerslot,
&isnew, NULL); &isnew, NULL);
pergroup = TupleHashEntryGetAdditional(hashtable, entry);
/* If new tuple group, initialize counts to zero */ /* If new tuple group, initialize counts to zero */
if (isnew) if (isnew)
{ {
entry->additional = (SetOpStatePerGroup) pergroup->numLeft = 0;
MemoryContextAllocZero(setopstate->hashtable->tablecxt, pergroup->numRight = 0;
sizeof(SetOpStatePerGroupData));
} }
/* Advance the counts */ /* Advance the counts */
((SetOpStatePerGroup) entry->additional)->numLeft++; pergroup->numLeft++;
/* Must reset expression context after each hashtable lookup */ /* Must reset expression context after each hashtable lookup */
ResetExprContext(econtext); ResetExprContext(econtext);
@ -465,6 +467,7 @@ setop_fill_hash_table(SetOpState *setopstate)
for (;;) for (;;)
{ {
TupleTableSlot *innerslot; TupleTableSlot *innerslot;
TupleHashTable hashtable = setopstate->hashtable;
TupleHashEntryData *entry; TupleHashEntryData *entry;
innerslot = ExecProcNode(innerPlan); innerslot = ExecProcNode(innerPlan);
@ -472,13 +475,17 @@ setop_fill_hash_table(SetOpState *setopstate)
break; break;
/* For tuples not seen previously, do not make hashtable entry */ /* For tuples not seen previously, do not make hashtable entry */
entry = LookupTupleHashEntry(setopstate->hashtable, entry = LookupTupleHashEntry(hashtable,
innerslot, innerslot,
NULL, NULL); NULL, NULL);
/* Advance the counts if entry is already present */ /* Advance the counts if entry is already present */
if (entry) if (entry)
((SetOpStatePerGroup) entry->additional)->numRight++; {
SetOpStatePerGroup pergroup = TupleHashEntryGetAdditional(hashtable, entry);
pergroup->numRight++;
}
/* Must reset expression context after each hashtable lookup */ /* Must reset expression context after each hashtable lookup */
ResetExprContext(econtext); ResetExprContext(econtext);
@ -496,7 +503,7 @@ setop_fill_hash_table(SetOpState *setopstate)
static TupleTableSlot * static TupleTableSlot *
setop_retrieve_hash_table(SetOpState *setopstate) setop_retrieve_hash_table(SetOpState *setopstate)
{ {
TupleHashEntryData *entry; TupleHashEntry entry;
TupleTableSlot *resultTupleSlot; TupleTableSlot *resultTupleSlot;
/* /*
@ -509,12 +516,15 @@ setop_retrieve_hash_table(SetOpState *setopstate)
*/ */
while (!setopstate->setop_done) while (!setopstate->setop_done)
{ {
TupleHashTable hashtable = setopstate->hashtable;
SetOpStatePerGroup pergroup;
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
/* /*
* Find the next entry in the hash table * Find the next entry in the hash table
*/ */
entry = ScanTupleHashTable(setopstate->hashtable, &setopstate->hashiter); entry = ScanTupleHashTable(hashtable, &setopstate->hashiter);
if (entry == NULL) if (entry == NULL)
{ {
/* No more entries in hashtable, so done */ /* No more entries in hashtable, so done */
@ -526,12 +536,13 @@ setop_retrieve_hash_table(SetOpState *setopstate)
* See if we should emit any copies of this tuple, and if so return * See if we should emit any copies of this tuple, and if so return
* the first copy. * the first copy.
*/ */
set_output_count(setopstate, (SetOpStatePerGroup) entry->additional); pergroup = TupleHashEntryGetAdditional(hashtable, entry);
set_output_count(setopstate, pergroup);
if (setopstate->numOutput > 0) if (setopstate->numOutput > 0)
{ {
setopstate->numOutput--; setopstate->numOutput--;
return ExecStoreMinimalTuple(entry->firstTuple, return ExecStoreMinimalTuple(TupleHashEntryGetTuple(entry),
resultTupleSlot, resultTupleSlot,
false); false);
} }

View File

@ -753,7 +753,7 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
{ {
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
ExecStoreMinimalTuple(entry->firstTuple, hashtable->tableslot, false); ExecStoreMinimalTuple(TupleHashEntryGetTuple(entry), hashtable->tableslot, false);
if (!execTuplesUnequal(slot, hashtable->tableslot, if (!execTuplesUnequal(slot, hashtable->tableslot,
numCols, keyColIdx, numCols, keyColIdx,
eqfunctions, eqfunctions,

View File

@ -158,6 +158,40 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable,
ExprState *hashexpr); ExprState *hashexpr);
extern void ResetTupleHashTable(TupleHashTable hashtable); extern void ResetTupleHashTable(TupleHashTable hashtable);
#ifndef FRONTEND
/*
* Return size of the hash bucket. Useful for estimating memory usage.
*/
static inline size_t
TupleHashEntrySize(void)
{
return sizeof(TupleHashEntryData);
}
/*
* Return tuple from hash entry.
*/
static inline MinimalTuple
TupleHashEntryGetTuple(TupleHashEntry entry)
{
return entry->firstTuple;
}
/*
* Get a pointer into the additional space allocated for this entry. The
* memory will be maxaligned and zeroed.
*
* The amount of space available is the additionalsize requested in the call
* to BuildTupleHashTable(). If additionalsize was specified as zero, return
* NULL.
*/
static inline void *
TupleHashEntryGetAdditional(TupleHashTable hashtable, TupleHashEntry entry)
{
return entry->additional;
}
#endif
/* /*
* prototypes from functions in execJunk.c * prototypes from functions in execJunk.c
*/ */

View File

@ -863,6 +863,7 @@ typedef struct TupleHashTableData
Oid *tab_collations; /* collations for hash and comparison */ Oid *tab_collations; /* collations for hash and comparison */
MemoryContext tablecxt; /* memory context containing table */ MemoryContext tablecxt; /* memory context containing table */
MemoryContext tempcxt; /* context for function evaluations */ MemoryContext tempcxt; /* context for function evaluations */
Size additionalsize; /* size of additional data */
TupleTableSlot *tableslot; /* slot for referencing table entries */ TupleTableSlot *tableslot; /* slot for referencing table entries */
/* The following fields are set transiently for each table search: */ /* The following fields are set transiently for each table search: */
TupleTableSlot *inputslot; /* current input tuple's slot */ TupleTableSlot *inputslot; /* current input tuple's slot */