PostgreSQL Source Code git master
nodeRecursiveunion.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * nodeRecursiveunion.c
4 * routines to handle RecursiveUnion nodes.
5 *
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.
8 *
9 *
10 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
12 *
13 *
14 * IDENTIFICATION
15 * src/backend/executor/nodeRecursiveunion.c
16 *
17 *-------------------------------------------------------------------------
18 */
19#include "postgres.h"
20
21#include "executor/executor.h"
23#include "miscadmin.h"
24#include "utils/memutils.h"
25
26
27
28/*
29 * Initialize the hash table to empty.
30 */
31static void
33{
34 RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan;
36
37 Assert(node->numCols > 0);
38
39 /*
40 * If both child plans deliver the same fixed tuple slot type, we can tell
41 * BuildTupleHashTable to expect that slot type as input. Otherwise,
42 * we'll pass NULL denoting that any slot type is possible.
43 */
44 rustate->hashtable = BuildTupleHashTable(&rustate->ps,
45 desc,
47 node->numCols,
48 node->dupColIdx,
49 rustate->eqfuncoids,
50 rustate->hashfunctions,
51 node->dupCollations,
52 node->numGroups,
53 0,
54 rustate->ps.state->es_query_cxt,
55 rustate->tuplesContext,
56 rustate->tempContext,
57 false);
58}
59
60
61/* ----------------------------------------------------------------
62 * ExecRecursiveUnion(node)
63 *
64 * Scans the recursive query sequentially and returns the next
65 * qualifying tuple.
66 *
67 * 1. evaluate non recursive term and assign the result to RT
68 *
69 * 2. execute recursive terms
70 *
71 * 2.1 WT := RT
72 * 2.2 while WT is not empty repeat 2.3 to 2.6. if WT is empty returns RT
73 * 2.3 replace the name of recursive term with WT
74 * 2.4 evaluate the recursive term and store into WT
75 * 2.5 append WT to RT
76 * 2.6 go back to 2.2
77 * ----------------------------------------------------------------
78 */
79static TupleTableSlot *
81{
86 TupleTableSlot *slot;
87 bool isnew;
88
90
91 /* 1. Evaluate non-recursive term */
92 if (!node->recursing)
93 {
94 for (;;)
95 {
96 slot = ExecProcNode(outerPlan);
97 if (TupIsNull(slot))
98 break;
99 if (plan->numCols > 0)
100 {
101 /* Find or build hashtable entry for this tuple's group */
102 LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
103 /* Must reset temp context after each hashtable lookup */
105 /* Ignore tuple if already seen */
106 if (!isnew)
107 continue;
108 }
109 /* Each non-duplicate tuple goes to the working table ... */
111 /* ... and to the caller */
112 return slot;
113 }
114 node->recursing = true;
115 }
116
117 /* 2. Execute recursive term */
118 for (;;)
119 {
120 slot = ExecProcNode(innerPlan);
121 if (TupIsNull(slot))
122 {
123 Tuplestorestate *swaptemp;
124
125 /* Done if there's nothing in the intermediate table */
126 if (node->intermediate_empty)
127 break;
128
129 /*
130 * Now we let the intermediate table become the work table. We
131 * need a fresh intermediate table, so delete the tuples from the
132 * current working table and use that as the new intermediate
133 * table. This saves a round of free/malloc from creating a new
134 * tuple store.
135 */
137
138 swaptemp = node->working_table;
139 node->working_table = node->intermediate_table;
140 node->intermediate_table = swaptemp;
141
142 /* mark the intermediate table as empty */
143 node->intermediate_empty = true;
144
145 /* reset the recursive term */
146 innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
147 plan->wtParam);
148
149 /* and continue fetching from recursive term */
150 continue;
151 }
152
153 if (plan->numCols > 0)
154 {
155 /* Find or build hashtable entry for this tuple's group */
156 LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
157 /* Must reset temp context after each hashtable lookup */
159 /* Ignore tuple if already seen */
160 if (!isnew)
161 continue;
162 }
163
164 /* Else, tuple is good; stash it in intermediate table ... */
165 node->intermediate_empty = false;
167 /* ... and return it */
168 return slot;
169 }
170
171 return NULL;
172}
173
174/* ----------------------------------------------------------------
175 * ExecInitRecursiveUnion
176 * ----------------------------------------------------------------
177 */
180{
181 RecursiveUnionState *rustate;
182 ParamExecData *prmdata;
183
184 /* check for unsupported flags */
186
187 /*
188 * create state structure
189 */
190 rustate = makeNode(RecursiveUnionState);
191 rustate->ps.plan = (Plan *) node;
192 rustate->ps.state = estate;
194
195 rustate->eqfuncoids = NULL;
196 rustate->hashfunctions = NULL;
197 rustate->hashtable = NULL;
198 rustate->tempContext = NULL;
199 rustate->tuplesContext = NULL;
200
201 /* initialize processing state */
202 rustate->recursing = false;
203 rustate->intermediate_empty = true;
204 rustate->working_table = tuplestore_begin_heap(false, false, work_mem);
205 rustate->intermediate_table = tuplestore_begin_heap(false, false, work_mem);
206
207 /*
208 * If hashing, we need a per-tuple memory context for comparisons, and a
209 * longer-lived context to store the hash table. The table can't just be
210 * kept in the per-query context because we want to be able to throw it
211 * away when rescanning. We can use a BumpContext to save storage,
212 * because we will have no need to delete individual table entries.
213 */
214 if (node->numCols > 0)
215 {
216 rustate->tempContext =
218 "RecursiveUnion",
220 rustate->tuplesContext =
222 "RecursiveUnion hashed tuples",
224 }
225
226 /*
227 * Make the state structure available to descendant WorkTableScan nodes
228 * via the Param slot reserved for it.
229 */
230 prmdata = &(estate->es_param_exec_vals[node->wtParam]);
231 Assert(prmdata->execPlan == NULL);
232 prmdata->value = PointerGetDatum(rustate);
233 prmdata->isnull = false;
234
235 /*
236 * Miscellaneous initialization
237 *
238 * RecursiveUnion plans don't have expression contexts because they never
239 * call ExecQual or ExecProject.
240 */
241 Assert(node->plan.qual == NIL);
242
243 /*
244 * RecursiveUnion nodes still have Result slots, which hold pointers to
245 * tuples, so we have to initialize them.
246 */
247 ExecInitResultTypeTL(&rustate->ps);
248
249 /*
250 * Initialize result tuple type. (Note: we have to set up the result type
251 * before initializing child nodes, because nodeWorktablescan.c expects it
252 * to be valid.)
253 */
254 rustate->ps.ps_ProjInfo = NULL;
255
256 /*
257 * initialize child nodes
258 */
259 outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags);
260 innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags);
261
262 /*
263 * If hashing, precompute fmgr lookup data for inner loop, and create the
264 * hash table.
265 */
266 if (node->numCols > 0)
267 {
269 node->dupOperators,
270 &rustate->eqfuncoids,
271 &rustate->hashfunctions);
272 build_hash_table(rustate);
273 }
274
275 return rustate;
276}
277
278/* ----------------------------------------------------------------
279 * ExecEndRecursiveUnion
280 *
281 * frees any storage allocated through C routines.
282 * ----------------------------------------------------------------
283 */
284void
286{
287 /* Release tuplestores */
290
291 /* free subsidiary stuff including hashtable data */
292 if (node->tempContext)
294 if (node->tuplesContext)
296
297 /*
298 * close down subplans
299 */
302}
303
304/* ----------------------------------------------------------------
305 * ExecReScanRecursiveUnion
306 *
307 * Rescans the relation.
308 * ----------------------------------------------------------------
309 */
310void
312{
316
317 /*
318 * Set recursive term's chgParam to tell it that we'll modify the working
319 * table and therefore it has to rescan.
320 */
321 innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam);
322
323 /*
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.
327 */
328 if (outerPlan->chgParam == NULL)
330
331 /* Empty hashtable if needed */
332 if (plan->numCols > 0)
334
335 /* reset processing state */
336 node->recursing = false;
337 node->intermediate_empty = true;
340}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
MemoryContext BumpContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: bump.c:133
void ExecReScan(PlanState *node)
Definition: execAmi.c:77
void execTuplesHashPrepare(int numCols, const Oid *eqOperators, Oid **eqFuncOids, FmgrInfo **hashFunctions)
Definition: execGrouping.c:100
TupleHashTable BuildTupleHashTable(PlanState *parent, TupleDesc inputDesc, const TupleTableSlotOps *inputOps, int numCols, AttrNumber *keyColIdx, const Oid *eqfuncoids, FmgrInfo *hashfunctions, Oid *collations, double nelements, Size additionalsize, MemoryContext metacxt, MemoryContext tuplescxt, MemoryContext tempcxt, bool use_variable_hash_iv)
Definition: execGrouping.c:184
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, bool *isnew, uint32 *hash)
Definition: execGrouping.c:382
void ResetTupleHashTable(TupleHashTable hashtable)
Definition: execGrouping.c:302
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1944
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:495
const TupleTableSlotOps * ExecGetCommonChildSlotOps(PlanState *ps)
Definition: execUtils.c:563
#define outerPlanState(node)
Definition: execnodes.h:1261
#define innerPlanState(node)
Definition: execnodes.h:1260
#define EXEC_FLAG_BACKWARD
Definition: executor.h:69
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:314
#define EXEC_FLAG_MARK
Definition: executor.h:70
int work_mem
Definition: globals.c:131
Assert(PointerIsAligned(start, uint64))
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
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 makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define NIL
Definition: pg_list.h:68
#define plan(x)
Definition: pg_regress.c:161
#define innerPlan(node)
Definition: plannodes.h:260
#define outerPlan(node)
Definition: plannodes.h:261
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
ParamExecData * es_param_exec_vals
Definition: execnodes.h:705
MemoryContext es_query_cxt
Definition: execnodes.h:710
bool isnull
Definition: params.h:149
Datum value
Definition: params.h:148
void * execPlan
Definition: params.h:147
Plan * plan
Definition: execnodes.h:1165
EState * state
Definition: execnodes.h:1167
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1205
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1171
List * qual
Definition: plannodes.h:231
MemoryContext tempContext
Definition: execnodes.h:1573
Tuplestorestate * working_table
Definition: execnodes.h:1568
MemoryContext tuplesContext
Definition: execnodes.h:1575
FmgrInfo * hashfunctions
Definition: execnodes.h:1572
Tuplestorestate * intermediate_table
Definition: execnodes.h:1569
TupleHashTable hashtable
Definition: execnodes.h:1574
Cardinality numGroups
Definition: plannodes.h:478
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:742
void tuplestore_clear(Tuplestorestate *state)
Definition: tuplestore.c:430
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492
#define TupIsNull(slot)
Definition: tuptable.h:309