mirror of
https://github.com/postgres/postgres.git
synced 2026-10-25 01:39:28 +03:00
In a plain join, we can just summarily discard an input tuple with null join key(s), since it cannot match anything from the other side of the join (assuming a strict join operator). However, if the tuple comes from the outer side of an outer join then we have to emit it with null-extension of the other side. Up to now, hash joins did that by inserting the tuple into the hash table as though it were a normal tuple. This is unnecessarily inefficient though, since the required processing is far simpler than for a potentially-matchable tuple. Worse, if there are a lot of such tuples they will bloat the hash bucket they go into, possibly causing useless repeated attempts to split that bucket or increase the number of batches. We have a report of a large join vainly creating many thousands of batches when faced with such input. This patch improves the situation by keeping such tuples out of the hash table altogether, instead pushing them into a separate tuplestore from which we return them later. (One might consider trying to return them immediately; but that would require substantial refactoring, and it doesn't work anyway for cases where we rescan an unmodified hash table.) This works even in parallel hash joins, because whichever worker reads a null-keyed tuple can just return it; there's no need for consultation with other workers. Thus the tuplestores are local storage even in a parallel join. A pre-existing buglet that I noticed while analyzing the code's behavior is that ExecHashRemoveNextSkewBucket fails to decrement hashtable->skewTuples for tuples moved into the main hash table from the skew hash table. This invalidates ExecHashTableInsert's calculation of the number of main-hash-table tuples, though probably not by a lot since we expect the skew table to be small relative to the main one. Nonetheless, let's fix that too while we're here. Bug: #18909 Reported-by: Sergey Koposov <Sergey.Koposov@ed.ac.uk> Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Chao Li <li.evan.chao@gmail.com> Discussion: https://postgr.es/m/3061845.1746486714@sss.pgh.pa.us
77 lines
3.2 KiB
C
77 lines
3.2 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeHash.h
|
|
* prototypes for nodeHash.c
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/executor/nodeHash.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef NODEHASH_H
|
|
#define NODEHASH_H
|
|
|
|
#include "access/parallel.h"
|
|
#include "nodes/execnodes.h"
|
|
|
|
struct SharedHashJoinBatch;
|
|
|
|
extern HashState *ExecInitHash(Hash *node, EState *estate, int eflags);
|
|
extern Node *MultiExecHash(HashState *node);
|
|
extern void ExecEndHash(HashState *node);
|
|
extern void ExecReScanHash(HashState *node);
|
|
|
|
extern HashJoinTable ExecHashTableCreate(HashState *state);
|
|
extern void ExecParallelHashTableAlloc(HashJoinTable hashtable,
|
|
int batchno);
|
|
extern void ExecHashTableDestroy(HashJoinTable hashtable);
|
|
extern void ExecHashTableDetach(HashJoinTable hashtable);
|
|
extern void ExecHashTableDetachBatch(HashJoinTable hashtable);
|
|
extern void ExecParallelHashTableSetCurrentBatch(HashJoinTable hashtable,
|
|
int batchno);
|
|
|
|
extern void ExecHashTableInsert(HashJoinTable hashtable,
|
|
TupleTableSlot *slot,
|
|
uint32 hashvalue);
|
|
extern void ExecParallelHashTableInsert(HashJoinTable hashtable,
|
|
TupleTableSlot *slot,
|
|
uint32 hashvalue);
|
|
extern void ExecParallelHashTableInsertCurrentBatch(HashJoinTable hashtable,
|
|
TupleTableSlot *slot,
|
|
uint32 hashvalue);
|
|
extern void ExecHashGetBucketAndBatch(HashJoinTable hashtable,
|
|
uint32 hashvalue,
|
|
int *bucketno,
|
|
int *batchno);
|
|
extern bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext);
|
|
extern bool ExecParallelScanHashBucket(HashJoinState *hjstate, ExprContext *econtext);
|
|
extern void ExecPrepHashTableForUnmatched(HashJoinState *hjstate);
|
|
extern bool ExecParallelPrepHashTableForUnmatched(HashJoinState *hjstate);
|
|
extern bool ExecScanHashTableForUnmatched(HashJoinState *hjstate,
|
|
ExprContext *econtext);
|
|
extern bool ExecParallelScanHashTableForUnmatched(HashJoinState *hjstate,
|
|
ExprContext *econtext);
|
|
extern void ExecHashTableReset(HashJoinTable hashtable);
|
|
extern void ExecHashTableResetMatchFlags(HashJoinTable hashtable);
|
|
extern void ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
|
|
bool try_combined_hash_mem,
|
|
int parallel_workers,
|
|
size_t *space_allowed,
|
|
int *numbuckets,
|
|
int *numbatches,
|
|
int *num_skew_mcvs);
|
|
extern int ExecHashGetSkewBucket(HashJoinTable hashtable, uint32 hashvalue);
|
|
extern Tuplestorestate *ExecHashBuildNullTupleStore(HashJoinTable hashtable);
|
|
extern void ExecHashEstimate(HashState *node, ParallelContext *pcxt);
|
|
extern void ExecHashInitializeDSM(HashState *node, ParallelContext *pcxt);
|
|
extern void ExecHashInitializeWorker(HashState *node, ParallelWorkerContext *pwcxt);
|
|
extern void ExecHashRetrieveInstrumentation(HashState *node);
|
|
extern void ExecShutdownHash(HashState *node);
|
|
extern void ExecHashAccumInstrumentation(HashInstrumentation *instrument,
|
|
HashJoinTable hashtable);
|
|
|
|
#endif /* NODEHASH_H */
|