mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +03:00
Fix LookupTupleHashEntryHash() pipeline-stall issue.
Refactor hash lookups in nodeAgg.c to improve performance. Author: Andres Freund and Jeff Davis Discussion: https://postgr.es/m/20200612213715.op4ye4q7gktqvpuo%40alap3.anarazel.de Backpatch-through: 13
This commit is contained in:
parent
21b0055359
commit
7f5f2249b2
@ -22,11 +22,11 @@
|
|||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
static int TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const MinimalTuple tuple2);
|
static int TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const MinimalTuple tuple2);
|
||||||
static uint32 TupleHashTableHash_internal(struct tuplehash_hash *tb,
|
static inline uint32 TupleHashTableHash_internal(struct tuplehash_hash *tb,
|
||||||
const MinimalTuple tuple);
|
const MinimalTuple tuple);
|
||||||
static TupleHashEntry LookupTupleHashEntry_internal(TupleHashTable hashtable,
|
static inline TupleHashEntry LookupTupleHashEntry_internal(TupleHashTable hashtable,
|
||||||
TupleTableSlot *slot,
|
TupleTableSlot *slot,
|
||||||
bool *isnew, uint32 hash);
|
bool *isnew, uint32 hash);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define parameters for tuple hash table code generation. The interface is
|
* Define parameters for tuple hash table code generation. The interface is
|
||||||
@ -291,6 +291,9 @@ ResetTupleHashTable(TupleHashTable hashtable)
|
|||||||
* If isnew is NULL, we do not create new entries; we return NULL if no
|
* If isnew is NULL, we do not create new entries; we return NULL if no
|
||||||
* match is found.
|
* match is found.
|
||||||
*
|
*
|
||||||
|
* If hash is not NULL, we set it to the calculated hash value. This allows
|
||||||
|
* callers access to the hash value even if no entry is returned.
|
||||||
|
*
|
||||||
* If isnew isn't NULL, then a new entry is created if no existing entry
|
* If isnew isn't NULL, then a new entry is created if no existing entry
|
||||||
* matches. On return, *isnew is true if the entry is newly created,
|
* matches. On return, *isnew is true if the entry is newly created,
|
||||||
* false if it existed already. ->additional_data in the new entry has
|
* false if it existed already. ->additional_data in the new entry has
|
||||||
@ -298,11 +301,11 @@ ResetTupleHashTable(TupleHashTable hashtable)
|
|||||||
*/
|
*/
|
||||||
TupleHashEntry
|
TupleHashEntry
|
||||||
LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
||||||
bool *isnew)
|
bool *isnew, uint32 *hash)
|
||||||
{
|
{
|
||||||
TupleHashEntry entry;
|
TupleHashEntry entry;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
uint32 hash;
|
uint32 local_hash;
|
||||||
|
|
||||||
/* Need to run the hash functions in short-lived context */
|
/* Need to run the hash functions in short-lived context */
|
||||||
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
|
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
|
||||||
@ -312,8 +315,13 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
|||||||
hashtable->in_hash_funcs = hashtable->tab_hash_funcs;
|
hashtable->in_hash_funcs = hashtable->tab_hash_funcs;
|
||||||
hashtable->cur_eq_func = hashtable->tab_eq_func;
|
hashtable->cur_eq_func = hashtable->tab_eq_func;
|
||||||
|
|
||||||
hash = TupleHashTableHash_internal(hashtable->hashtab, NULL);
|
local_hash = TupleHashTableHash_internal(hashtable->hashtab, NULL);
|
||||||
entry = LookupTupleHashEntry_internal(hashtable, slot, isnew, hash);
|
entry = LookupTupleHashEntry_internal(hashtable, slot, isnew, local_hash);
|
||||||
|
|
||||||
|
if (hash != NULL)
|
||||||
|
*hash = local_hash;
|
||||||
|
|
||||||
|
Assert(entry == NULL || entry->hash == local_hash);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
@ -362,6 +370,7 @@ LookupTupleHashEntryHash(TupleHashTable hashtable, TupleTableSlot *slot,
|
|||||||
hashtable->cur_eq_func = hashtable->tab_eq_func;
|
hashtable->cur_eq_func = hashtable->tab_eq_func;
|
||||||
|
|
||||||
entry = LookupTupleHashEntry_internal(hashtable, slot, isnew, hash);
|
entry = LookupTupleHashEntry_internal(hashtable, slot, isnew, hash);
|
||||||
|
Assert(entry == NULL || entry->hash == hash);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
@ -480,7 +489,7 @@ TupleHashTableHash_internal(struct tuplehash_hash *tb,
|
|||||||
* NB: This function may or may not change the memory context. Caller is
|
* NB: This function may or may not change the memory context. Caller is
|
||||||
* expected to change it back.
|
* expected to change it back.
|
||||||
*/
|
*/
|
||||||
static TupleHashEntry
|
static inline TupleHashEntry
|
||||||
LookupTupleHashEntry_internal(TupleHashTable hashtable, TupleTableSlot *slot,
|
LookupTupleHashEntry_internal(TupleHashTable hashtable, TupleTableSlot *slot,
|
||||||
bool *isnew, uint32 hash)
|
bool *isnew, uint32 hash)
|
||||||
{
|
{
|
||||||
|
@ -391,7 +391,9 @@ static void finalize_partialaggregate(AggState *aggstate,
|
|||||||
AggStatePerAgg peragg,
|
AggStatePerAgg peragg,
|
||||||
AggStatePerGroup pergroupstate,
|
AggStatePerGroup pergroupstate,
|
||||||
Datum *resultVal, bool *resultIsNull);
|
Datum *resultVal, bool *resultIsNull);
|
||||||
static void prepare_hash_slot(AggState *aggstate);
|
static inline void prepare_hash_slot(AggStatePerHash perhash,
|
||||||
|
TupleTableSlot *inputslot,
|
||||||
|
TupleTableSlot *hashslot);
|
||||||
static void prepare_projection_slot(AggState *aggstate,
|
static void prepare_projection_slot(AggState *aggstate,
|
||||||
TupleTableSlot *slot,
|
TupleTableSlot *slot,
|
||||||
int currentSet);
|
int currentSet);
|
||||||
@ -413,8 +415,9 @@ static int hash_choose_num_partitions(uint64 input_groups,
|
|||||||
double hashentrysize,
|
double hashentrysize,
|
||||||
int used_bits,
|
int used_bits,
|
||||||
int *log2_npartittions);
|
int *log2_npartittions);
|
||||||
static AggStatePerGroup lookup_hash_entry(AggState *aggstate, uint32 hash,
|
static void initialize_hash_entry(AggState *aggstate,
|
||||||
bool *in_hash_table);
|
TupleHashTable hashtable,
|
||||||
|
TupleHashEntry entry);
|
||||||
static void lookup_hash_entries(AggState *aggstate);
|
static void lookup_hash_entries(AggState *aggstate);
|
||||||
static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
|
static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
|
||||||
static void agg_fill_hash_table(AggState *aggstate);
|
static void agg_fill_hash_table(AggState *aggstate);
|
||||||
@ -1207,12 +1210,11 @@ finalize_partialaggregate(AggState *aggstate,
|
|||||||
* Extract the attributes that make up the grouping key into the
|
* Extract the attributes that make up the grouping key into the
|
||||||
* hashslot. This is necessary to compute the hash or perform a lookup.
|
* hashslot. This is necessary to compute the hash or perform a lookup.
|
||||||
*/
|
*/
|
||||||
static void
|
static inline void
|
||||||
prepare_hash_slot(AggState *aggstate)
|
prepare_hash_slot(AggStatePerHash perhash,
|
||||||
|
TupleTableSlot *inputslot,
|
||||||
|
TupleTableSlot *hashslot)
|
||||||
{
|
{
|
||||||
TupleTableSlot *inputslot = aggstate->tmpcontext->ecxt_outertuple;
|
|
||||||
AggStatePerHash perhash = &aggstate->perhash[aggstate->current_set];
|
|
||||||
TupleTableSlot *hashslot = perhash->hashslot;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* transfer just the needed columns into hashslot */
|
/* transfer just the needed columns into hashslot */
|
||||||
@ -2013,75 +2015,39 @@ hash_choose_num_partitions(uint64 input_groups, double hashentrysize,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find or create a hashtable entry for the tuple group containing the current
|
* Initialize a freshly-created TupleHashEntry.
|
||||||
* tuple (already set in tmpcontext's outertuple slot), in the current grouping
|
|
||||||
* set (which the caller must have selected - note that initialize_aggregate
|
|
||||||
* depends on this).
|
|
||||||
*
|
|
||||||
* When called, CurrentMemoryContext should be the per-query context. The
|
|
||||||
* already-calculated hash value for the tuple must be specified.
|
|
||||||
*
|
|
||||||
* If in "spill mode", then only find existing hashtable entries; don't create
|
|
||||||
* new ones. If a tuple's group is not already present in the hash table for
|
|
||||||
* the current grouping set, assign *in_hash_table=false and the caller will
|
|
||||||
* spill it to disk.
|
|
||||||
*/
|
*/
|
||||||
static AggStatePerGroup
|
static void
|
||||||
lookup_hash_entry(AggState *aggstate, uint32 hash, bool *in_hash_table)
|
initialize_hash_entry(AggState *aggstate, TupleHashTable hashtable,
|
||||||
|
TupleHashEntry entry)
|
||||||
{
|
{
|
||||||
AggStatePerHash perhash = &aggstate->perhash[aggstate->current_set];
|
AggStatePerGroup pergroup;
|
||||||
TupleTableSlot *hashslot = perhash->hashslot;
|
int transno;
|
||||||
TupleHashEntryData *entry;
|
|
||||||
bool isnew = false;
|
|
||||||
bool *p_isnew;
|
|
||||||
|
|
||||||
/* if hash table already spilled, don't create new entries */
|
aggstate->hash_ngroups_current++;
|
||||||
p_isnew = aggstate->hash_spill_mode ? NULL : &isnew;
|
hash_agg_check_limits(aggstate);
|
||||||
|
|
||||||
/* find or create the hashtable entry using the filtered tuple */
|
/* no need to allocate or initialize per-group state */
|
||||||
entry = LookupTupleHashEntryHash(perhash->hashtable, hashslot, p_isnew,
|
if (aggstate->numtrans == 0)
|
||||||
hash);
|
return;
|
||||||
|
|
||||||
if (entry == NULL)
|
pergroup = (AggStatePerGroup)
|
||||||
|
MemoryContextAlloc(hashtable->tablecxt,
|
||||||
|
sizeof(AggStatePerGroupData) * aggstate->numtrans);
|
||||||
|
|
||||||
|
entry->additional = pergroup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize aggregates for new tuple group, lookup_hash_entries()
|
||||||
|
* already has selected the relevant grouping set.
|
||||||
|
*/
|
||||||
|
for (transno = 0; transno < aggstate->numtrans; transno++)
|
||||||
{
|
{
|
||||||
*in_hash_table = false;
|
AggStatePerTrans pertrans = &aggstate->pertrans[transno];
|
||||||
return NULL;
|
AggStatePerGroup pergroupstate = &pergroup[transno];
|
||||||
|
|
||||||
|
initialize_aggregate(aggstate, pertrans, pergroupstate);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
*in_hash_table = true;
|
|
||||||
|
|
||||||
if (isnew)
|
|
||||||
{
|
|
||||||
AggStatePerGroup pergroup;
|
|
||||||
int transno;
|
|
||||||
|
|
||||||
aggstate->hash_ngroups_current++;
|
|
||||||
hash_agg_check_limits(aggstate);
|
|
||||||
|
|
||||||
/* no need to allocate or initialize per-group state */
|
|
||||||
if (aggstate->numtrans == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
pergroup = (AggStatePerGroup)
|
|
||||||
MemoryContextAlloc(perhash->hashtable->tablecxt,
|
|
||||||
sizeof(AggStatePerGroupData) * aggstate->numtrans);
|
|
||||||
|
|
||||||
entry->additional = pergroup;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize aggregates for new tuple group, lookup_hash_entries()
|
|
||||||
* already has selected the relevant grouping set.
|
|
||||||
*/
|
|
||||||
for (transno = 0; transno < aggstate->numtrans; transno++)
|
|
||||||
{
|
|
||||||
AggStatePerTrans pertrans = &aggstate->pertrans[transno];
|
|
||||||
AggStatePerGroup pergroupstate = &pergroup[transno];
|
|
||||||
|
|
||||||
initialize_aggregate(aggstate, pertrans, pergroupstate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry->additional;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2106,21 +2072,37 @@ static void
|
|||||||
lookup_hash_entries(AggState *aggstate)
|
lookup_hash_entries(AggState *aggstate)
|
||||||
{
|
{
|
||||||
AggStatePerGroup *pergroup = aggstate->hash_pergroup;
|
AggStatePerGroup *pergroup = aggstate->hash_pergroup;
|
||||||
|
TupleTableSlot *outerslot = aggstate->tmpcontext->ecxt_outertuple;
|
||||||
int setno;
|
int setno;
|
||||||
|
|
||||||
for (setno = 0; setno < aggstate->num_hashes; setno++)
|
for (setno = 0; setno < aggstate->num_hashes; setno++)
|
||||||
{
|
{
|
||||||
AggStatePerHash perhash = &aggstate->perhash[setno];
|
AggStatePerHash perhash = &aggstate->perhash[setno];
|
||||||
|
TupleHashTable hashtable = perhash->hashtable;
|
||||||
|
TupleTableSlot *hashslot = perhash->hashslot;
|
||||||
|
TupleHashEntry entry;
|
||||||
uint32 hash;
|
uint32 hash;
|
||||||
bool in_hash_table;
|
bool isnew = false;
|
||||||
|
bool *p_isnew;
|
||||||
|
|
||||||
|
/* if hash table already spilled, don't create new entries */
|
||||||
|
p_isnew = aggstate->hash_spill_mode ? NULL : &isnew;
|
||||||
|
|
||||||
select_current_set(aggstate, setno, true);
|
select_current_set(aggstate, setno, true);
|
||||||
prepare_hash_slot(aggstate);
|
prepare_hash_slot(perhash,
|
||||||
hash = TupleHashTableHash(perhash->hashtable, perhash->hashslot);
|
outerslot,
|
||||||
pergroup[setno] = lookup_hash_entry(aggstate, hash, &in_hash_table);
|
hashslot);
|
||||||
|
|
||||||
/* check to see if we need to spill the tuple for this grouping set */
|
entry = LookupTupleHashEntry(hashtable, hashslot,
|
||||||
if (!in_hash_table)
|
p_isnew, &hash);
|
||||||
|
|
||||||
|
if (entry != NULL)
|
||||||
|
{
|
||||||
|
if (isnew)
|
||||||
|
initialize_hash_entry(aggstate, hashtable, entry);
|
||||||
|
pergroup[setno] = entry->additional;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
HashAggSpill *spill = &aggstate->hash_spills[setno];
|
HashAggSpill *spill = &aggstate->hash_spills[setno];
|
||||||
TupleTableSlot *slot = aggstate->tmpcontext->ecxt_outertuple;
|
TupleTableSlot *slot = aggstate->tmpcontext->ecxt_outertuple;
|
||||||
@ -2131,6 +2113,7 @@ lookup_hash_entries(AggState *aggstate)
|
|||||||
aggstate->hashentrysize);
|
aggstate->hashentrysize);
|
||||||
|
|
||||||
hashagg_spill_tuple(aggstate, spill, slot, hash);
|
hashagg_spill_tuple(aggstate, spill, slot, hash);
|
||||||
|
pergroup[setno] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2588,6 +2571,7 @@ static bool
|
|||||||
agg_refill_hash_table(AggState *aggstate)
|
agg_refill_hash_table(AggState *aggstate)
|
||||||
{
|
{
|
||||||
HashAggBatch *batch;
|
HashAggBatch *batch;
|
||||||
|
AggStatePerHash perhash;
|
||||||
HashAggSpill spill;
|
HashAggSpill spill;
|
||||||
HashTapeInfo *tapeinfo = aggstate->hash_tapeinfo;
|
HashTapeInfo *tapeinfo = aggstate->hash_tapeinfo;
|
||||||
uint64 ngroups_estimate;
|
uint64 ngroups_estimate;
|
||||||
@ -2639,6 +2623,8 @@ agg_refill_hash_table(AggState *aggstate)
|
|||||||
|
|
||||||
select_current_set(aggstate, batch->setno, true);
|
select_current_set(aggstate, batch->setno, true);
|
||||||
|
|
||||||
|
perhash = &aggstate->perhash[aggstate->current_set];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Spilled tuples are always read back as MinimalTuples, which may be
|
* Spilled tuples are always read back as MinimalTuples, which may be
|
||||||
* different from the outer plan, so recompile the aggregate expressions.
|
* different from the outer plan, so recompile the aggregate expressions.
|
||||||
@ -2652,10 +2638,13 @@ agg_refill_hash_table(AggState *aggstate)
|
|||||||
HASHAGG_READ_BUFFER_SIZE);
|
HASHAGG_READ_BUFFER_SIZE);
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
TupleTableSlot *slot = aggstate->hash_spill_rslot;
|
TupleTableSlot *spillslot = aggstate->hash_spill_rslot;
|
||||||
|
TupleTableSlot *hashslot = perhash->hashslot;
|
||||||
|
TupleHashEntry entry;
|
||||||
MinimalTuple tuple;
|
MinimalTuple tuple;
|
||||||
uint32 hash;
|
uint32 hash;
|
||||||
bool in_hash_table;
|
bool isnew = false;
|
||||||
|
bool *p_isnew = aggstate->hash_spill_mode ? NULL : &isnew;
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
@ -2663,16 +2652,20 @@ agg_refill_hash_table(AggState *aggstate)
|
|||||||
if (tuple == NULL)
|
if (tuple == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ExecStoreMinimalTuple(tuple, slot, true);
|
ExecStoreMinimalTuple(tuple, spillslot, true);
|
||||||
aggstate->tmpcontext->ecxt_outertuple = slot;
|
aggstate->tmpcontext->ecxt_outertuple = spillslot;
|
||||||
|
|
||||||
prepare_hash_slot(aggstate);
|
prepare_hash_slot(perhash,
|
||||||
aggstate->hash_pergroup[batch->setno] =
|
aggstate->tmpcontext->ecxt_outertuple,
|
||||||
lookup_hash_entry(aggstate, hash, &in_hash_table);
|
hashslot);
|
||||||
|
entry = LookupTupleHashEntryHash(
|
||||||
|
perhash->hashtable, hashslot, p_isnew, hash);
|
||||||
|
|
||||||
if (in_hash_table)
|
if (entry != NULL)
|
||||||
{
|
{
|
||||||
/* Advance the aggregates (or combine functions) */
|
if (isnew)
|
||||||
|
initialize_hash_entry(aggstate, perhash->hashtable, entry);
|
||||||
|
aggstate->hash_pergroup[batch->setno] = entry->additional;
|
||||||
advance_aggregates(aggstate);
|
advance_aggregates(aggstate);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2688,7 +2681,9 @@ agg_refill_hash_table(AggState *aggstate)
|
|||||||
ngroups_estimate, aggstate->hashentrysize);
|
ngroups_estimate, aggstate->hashentrysize);
|
||||||
}
|
}
|
||||||
/* no memory for a new group, spill */
|
/* no memory for a new group, spill */
|
||||||
hashagg_spill_tuple(aggstate, &spill, slot, hash);
|
hashagg_spill_tuple(aggstate, &spill, spillslot, hash);
|
||||||
|
|
||||||
|
aggstate->hash_pergroup[batch->setno] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -94,7 +94,7 @@ ExecRecursiveUnion(PlanState *pstate)
|
|||||||
if (plan->numCols > 0)
|
if (plan->numCols > 0)
|
||||||
{
|
{
|
||||||
/* Find or build hashtable entry for this tuple's group */
|
/* Find or build hashtable entry for this tuple's group */
|
||||||
LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
|
||||||
/* Must reset temp context after each hashtable lookup */
|
/* Must reset temp context after each hashtable lookup */
|
||||||
MemoryContextReset(node->tempContext);
|
MemoryContextReset(node->tempContext);
|
||||||
/* Ignore tuple if already seen */
|
/* Ignore tuple if already seen */
|
||||||
@ -141,7 +141,7 @@ ExecRecursiveUnion(PlanState *pstate)
|
|||||||
if (plan->numCols > 0)
|
if (plan->numCols > 0)
|
||||||
{
|
{
|
||||||
/* Find or build hashtable entry for this tuple's group */
|
/* Find or build hashtable entry for this tuple's group */
|
||||||
LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
|
||||||
/* Must reset temp context after each hashtable lookup */
|
/* Must reset temp context after each hashtable lookup */
|
||||||
MemoryContextReset(node->tempContext);
|
MemoryContextReset(node->tempContext);
|
||||||
/* Ignore tuple if already seen */
|
/* Ignore tuple if already seen */
|
||||||
|
@ -381,7 +381,7 @@ setop_fill_hash_table(SetOpState *setopstate)
|
|||||||
|
|
||||||
/* Find or build hashtable entry for this tuple's group */
|
/* Find or build hashtable entry for this tuple's group */
|
||||||
entry = LookupTupleHashEntry(setopstate->hashtable, outerslot,
|
entry = LookupTupleHashEntry(setopstate->hashtable, outerslot,
|
||||||
&isnew);
|
&isnew, NULL);
|
||||||
|
|
||||||
/* If new tuple group, initialize counts */
|
/* If new tuple group, initialize counts */
|
||||||
if (isnew)
|
if (isnew)
|
||||||
@ -402,7 +402,7 @@ setop_fill_hash_table(SetOpState *setopstate)
|
|||||||
|
|
||||||
/* For tuples not seen previously, do not make hashtable entry */
|
/* For tuples not seen previously, do not make hashtable entry */
|
||||||
entry = LookupTupleHashEntry(setopstate->hashtable, outerslot,
|
entry = LookupTupleHashEntry(setopstate->hashtable, outerslot,
|
||||||
NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
/* Advance the counts if entry is already present */
|
/* Advance the counts if entry is already present */
|
||||||
if (entry)
|
if (entry)
|
||||||
|
@ -595,12 +595,12 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
|||||||
*/
|
*/
|
||||||
if (slotNoNulls(slot))
|
if (slotNoNulls(slot))
|
||||||
{
|
{
|
||||||
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
|
||||||
node->havehashrows = true;
|
node->havehashrows = true;
|
||||||
}
|
}
|
||||||
else if (node->hashnulls)
|
else if (node->hashnulls)
|
||||||
{
|
{
|
||||||
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
|
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew, NULL);
|
||||||
node->havenullrows = true;
|
node->havenullrows = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ extern TupleHashTable BuildTupleHashTableExt(PlanState *parent,
|
|||||||
MemoryContext tempcxt, bool use_variable_hash_iv);
|
MemoryContext tempcxt, bool use_variable_hash_iv);
|
||||||
extern TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable,
|
extern TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable,
|
||||||
TupleTableSlot *slot,
|
TupleTableSlot *slot,
|
||||||
bool *isnew);
|
bool *isnew, uint32 *hash);
|
||||||
extern uint32 TupleHashTableHash(TupleHashTable hashtable,
|
extern uint32 TupleHashTableHash(TupleHashTable hashtable,
|
||||||
TupleTableSlot *slot);
|
TupleTableSlot *slot);
|
||||||
extern TupleHashEntry LookupTupleHashEntryHash(TupleHashTable hashtable,
|
extern TupleHashEntry LookupTupleHashEntryHash(TupleHashTable hashtable,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user