1/*-------------------------------------------------------------------------
4 * routines to handle RecursiveUnion nodes.
6 * To implement UNION (without ALL), we need a hashtable that stores tuples
7 * already seen. The hash key is computed from the grouping columns.
10 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
15 * src/backend/executor/nodeRecursiveunion.c
17 *-------------------------------------------------------------------------
29 * Initialize the hash table to empty.
41 * If both child plans deliver the same fixed tuple slot type, we can tell
42 * BuildTupleHashTable to expect that slot type as input. Otherwise,
43 * we'll pass NULL denoting that any slot type is possible.
62/* ----------------------------------------------------------------
63 * ExecRecursiveUnion(node)
65 * Scans the recursive query sequentially and returns the next
68 * 1. evaluate non recursive term and assign the result to RT
70 * 2. execute recursive terms
73 * 2.2 while WT is not empty repeat 2.3 to 2.6. if WT is empty returns RT
74 * 2.3 replace the name of recursive term with WT
75 * 2.4 evaluate the recursive term and store into WT
78 * ----------------------------------------------------------------
92 /* 1. Evaluate non-recursive term */
100 if (
plan->numCols > 0)
102 /* Find or build hashtable entry for this tuple's group */
104 /* Must reset temp context after each hashtable lookup */
106 /* Ignore tuple if already seen */
110 /* Each non-duplicate tuple goes to the working table ... */
112 /* ... and to the caller */
118 /* 2. Execute recursive term */
126 /* Done if there's nothing in the intermediate table */
131 * Now we let the intermediate table become the work table. We
132 * need a fresh intermediate table, so delete the tuples from the
133 * current working table and use that as the new intermediate
134 * table. This saves a round of free/malloc from creating a new
143 /* mark the intermediate table as empty */
146 /* reset the recursive term */
150 /* and continue fetching from recursive term */
154 if (
plan->numCols > 0)
156 /* Find or build hashtable entry for this tuple's group */
158 /* Must reset temp context after each hashtable lookup */
160 /* Ignore tuple if already seen */
165 /* Else, tuple is good; stash it in intermediate table ... */
168 /* ... and return it */
175/* ----------------------------------------------------------------
176 * ExecInitRecursiveUnion
177 * ----------------------------------------------------------------
185 /* check for unsupported flags */
189 * create state structure
202 /* initialize processing state */
209 * If hashing, we need a per-tuple memory context for comparisons, and a
210 * longer-lived context to store the hash table. The table can't just be
211 * kept in the per-query context because we want to be able to throw it
212 * away when rescanning.
222 "RecursiveUnion hash table",
227 * Make the state structure available to descendant WorkTableScan nodes
228 * via the Param slot reserved for it.
236 * Miscellaneous initialization
238 * RecursiveUnion plans don't have expression contexts because they never
239 * call ExecQual or ExecProject.
244 * RecursiveUnion nodes still have Result slots, which hold pointers to
245 * tuples, so we have to initialize them.
250 * Initialize result tuple type. (Note: we have to set up the result type
251 * before initializing child nodes, because nodeWorktablescan.c expects it
257 * initialize child nodes
263 * If hashing, precompute fmgr lookup data for inner loop, and create the
278/* ----------------------------------------------------------------
279 * ExecEndRecursiveUnion
281 * frees any storage allocated through C routines.
282 * ----------------------------------------------------------------
287 /* Release tuplestores */
291 /* free subsidiary stuff including hashtable */
298 * close down subplans
304/* ----------------------------------------------------------------
305 * ExecReScanRecursiveUnion
307 * Rescans the relation.
308 * ----------------------------------------------------------------
318 * Set recursive term's chgParam to tell it that we'll modify the working
319 * table and therefore it has to rescan.
324 * if chgParam of subnode is not null then plan will be re-scanned by
325 * first ExecProcNode. Because of above, we only have to do this to the
326 * non-recursive term.
331 /* Release any hashtable storage */
335 /* Empty hashtable if needed */
336 if (
plan->numCols > 0)
339 /* reset processing state */
Bitmapset * bms_add_member(Bitmapset *a, int x)
void ExecReScan(PlanState *node)
void execTuplesHashPrepare(int numCols, const Oid *eqOperators, Oid **eqFuncOids, FmgrInfo **hashFunctions)
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, bool *isnew, uint32 *hash)
TupleHashTable BuildTupleHashTable(PlanState *parent, TupleDesc inputDesc, const TupleTableSlotOps *inputOps, int numCols, AttrNumber *keyColIdx, const Oid *eqfuncoids, FmgrInfo *hashfunctions, Oid *collations, long nbuckets, Size additionalsize, MemoryContext metacxt, MemoryContext tablecxt, MemoryContext tempcxt, bool use_variable_hash_iv)
void ResetTupleHashTable(TupleHashTable hashtable)
void ExecEndNode(PlanState *node)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
void ExecInitResultTypeTL(PlanState *planstate)
TupleDesc ExecGetResultType(PlanState *planstate)
const TupleTableSlotOps * ExecGetCommonChildSlotOps(PlanState *ps)
#define outerPlanState(node)
#define innerPlanState(node)
#define EXEC_FLAG_BACKWARD
static TupleTableSlot * ExecProcNode(PlanState *node)
Assert(PointerIsAligned(start, uint64))
void MemoryContextReset(MemoryContext context)
MemoryContext CurrentMemoryContext
void MemoryContextDelete(MemoryContext context)
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
#define CHECK_FOR_INTERRUPTS()
static void build_hash_table(RecursiveUnionState *rustate)
static TupleTableSlot * ExecRecursiveUnion(PlanState *pstate)
void ExecEndRecursiveUnion(RecursiveUnionState *node)
RecursiveUnionState * ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
void ExecReScanRecursiveUnion(RecursiveUnionState *node)
#define castNode(_type_, nodeptr)
static Datum PointerGetDatum(const void *X)
ParamExecData * es_param_exec_vals
MemoryContext es_query_cxt
ProjectionInfo * ps_ProjInfo
ExecProcNodeMtd ExecProcNode
MemoryContext tempContext
MemoryContext tableContext
Tuplestorestate * working_table
Tuplestorestate * intermediate_table
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
void tuplestore_clear(Tuplestorestate *state)
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
void tuplestore_end(Tuplestorestate *state)