1
/*-------------------------------------------------------------------------
4
* routines to handle RecursiveUnion nodes.
6
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7
* Portions Copyright (c) 1994, Regents of the University of California
11
* src/backend/executor/nodeRecursiveunion.c
13
*-------------------------------------------------------------------------
17
#include "executor/execdebug.h"
18
#include "executor/nodeRecursiveunion.h"
19
#include "miscadmin.h"
20
#include "utils/memutils.h"
24
* To implement UNION (without ALL), we need a hashtable that stores tuples
25
* already seen. The hash key is computed from the grouping columns.
27
typedef struct RUHashEntryData *RUHashEntry;
29
typedef struct RUHashEntryData
31
TupleHashEntryData shared; /* common header for hash table entries */
36
* Initialize the hash table to empty.
39
build_hash_table(RecursiveUnionState *rustate)
41
RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan;
43
Assert(node->numCols > 0);
44
Assert(node->numGroups > 0);
46
rustate->hashtable = BuildTupleHashTable(node->numCols,
49
rustate->hashfunctions,
51
sizeof(RUHashEntryData),
52
rustate->tableContext,
53
rustate->tempContext);
57
/* ----------------------------------------------------------------
58
* ExecRecursiveUnion(node)
60
* Scans the recursive query sequentially and returns the next
63
* 1. evaluate non recursive term and assign the result to RT
65
* 2. execute recursive terms
68
* 2.2 while WT is not empty repeat 2.3 to 2.6. if WT is empty returns RT
69
* 2.3 replace the name of recursive term with WT
70
* 2.4 evaluate the recursive term and store into WT
73
* ----------------------------------------------------------------
76
ExecRecursiveUnion(RecursiveUnionState *node)
78
PlanState *outerPlan = outerPlanState(node);
79
PlanState *innerPlan = innerPlanState(node);
80
RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
84
/* 1. Evaluate non-recursive term */
89
slot = ExecProcNode(outerPlan);
92
if (plan->numCols > 0)
94
/* Find or build hashtable entry for this tuple's group */
95
LookupTupleHashEntry(node->hashtable, slot, &isnew);
96
/* Must reset temp context after each hashtable lookup */
97
MemoryContextReset(node->tempContext);
98
/* Ignore tuple if already seen */
102
/* Each non-duplicate tuple goes to the working table ... */
103
tuplestore_puttupleslot(node->working_table, slot);
104
/* ... and to the caller */
107
node->recursing = true;
110
/* 2. Execute recursive term */
113
slot = ExecProcNode(innerPlan);
116
/* Done if there's nothing in the intermediate table */
117
if (node->intermediate_empty)
120
/* done with old working table ... */
121
tuplestore_end(node->working_table);
123
/* intermediate table becomes working table */
124
node->working_table = node->intermediate_table;
126
/* create new empty intermediate table */
127
node->intermediate_table = tuplestore_begin_heap(false, false,
129
node->intermediate_empty = true;
131
/* reset the recursive term */
132
innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
135
/* and continue fetching from recursive term */
139
if (plan->numCols > 0)
141
/* Find or build hashtable entry for this tuple's group */
142
LookupTupleHashEntry(node->hashtable, slot, &isnew);
143
/* Must reset temp context after each hashtable lookup */
144
MemoryContextReset(node->tempContext);
145
/* Ignore tuple if already seen */
150
/* Else, tuple is good; stash it in intermediate table ... */
151
node->intermediate_empty = false;
152
tuplestore_puttupleslot(node->intermediate_table, slot);
153
/* ... and return it */
160
/* ----------------------------------------------------------------
161
* ExecInitRecursiveUnionScan
162
* ----------------------------------------------------------------
164
RecursiveUnionState *
165
ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
167
RecursiveUnionState *rustate;
168
ParamExecData *prmdata;
170
/* check for unsupported flags */
171
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
174
* create state structure
176
rustate = makeNode(RecursiveUnionState);
177
rustate->ps.plan = (Plan *) node;
178
rustate->ps.state = estate;
180
rustate->eqfunctions = NULL;
181
rustate->hashfunctions = NULL;
182
rustate->hashtable = NULL;
183
rustate->tempContext = NULL;
184
rustate->tableContext = NULL;
186
/* initialize processing state */
187
rustate->recursing = false;
188
rustate->intermediate_empty = true;
189
rustate->working_table = tuplestore_begin_heap(false, false, work_mem);
190
rustate->intermediate_table = tuplestore_begin_heap(false, false, work_mem);
193
* If hashing, we need a per-tuple memory context for comparisons, and a
194
* longer-lived context to store the hash table. The table can't just be
195
* kept in the per-query context because we want to be able to throw it
196
* away when rescanning.
198
if (node->numCols > 0)
200
rustate->tempContext =
201
AllocSetContextCreate(CurrentMemoryContext,
203
ALLOCSET_DEFAULT_MINSIZE,
204
ALLOCSET_DEFAULT_INITSIZE,
205
ALLOCSET_DEFAULT_MAXSIZE);
206
rustate->tableContext =
207
AllocSetContextCreate(CurrentMemoryContext,
208
"RecursiveUnion hash table",
209
ALLOCSET_DEFAULT_MINSIZE,
210
ALLOCSET_DEFAULT_INITSIZE,
211
ALLOCSET_DEFAULT_MAXSIZE);
215
* Make the state structure available to descendant WorkTableScan nodes
216
* via the Param slot reserved for it.
218
prmdata = &(estate->es_param_exec_vals[node->wtParam]);
219
Assert(prmdata->execPlan == NULL);
220
prmdata->value = PointerGetDatum(rustate);
221
prmdata->isnull = false;
224
* Miscellaneous initialization
226
* RecursiveUnion plans don't have expression contexts because they never
227
* call ExecQual or ExecProject.
229
Assert(node->plan.qual == NIL);
232
* RecursiveUnion nodes still have Result slots, which hold pointers to
233
* tuples, so we have to initialize them.
235
ExecInitResultTupleSlot(estate, &rustate->ps);
238
* Initialize result tuple type and projection info. (Note: we have to
239
* set up the result type before initializing child nodes, because
240
* nodeWorktablescan.c expects it to be valid.)
242
ExecAssignResultTypeFromTL(&rustate->ps);
243
rustate->ps.ps_ProjInfo = NULL;
246
* initialize child nodes
248
outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags);
249
innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags);
252
* If hashing, precompute fmgr lookup data for inner loop, and create the
255
if (node->numCols > 0)
257
execTuplesHashPrepare(node->numCols,
259
&rustate->eqfunctions,
260
&rustate->hashfunctions);
261
build_hash_table(rustate);
267
/* ----------------------------------------------------------------
268
* ExecEndRecursiveUnionScan
270
* frees any storage allocated through C routines.
271
* ----------------------------------------------------------------
274
ExecEndRecursiveUnion(RecursiveUnionState *node)
276
/* Release tuplestores */
277
tuplestore_end(node->working_table);
278
tuplestore_end(node->intermediate_table);
280
/* free subsidiary stuff including hashtable */
281
if (node->tempContext)
282
MemoryContextDelete(node->tempContext);
283
if (node->tableContext)
284
MemoryContextDelete(node->tableContext);
287
* clean out the upper tuple table
289
ExecClearTuple(node->ps.ps_ResultTupleSlot);
292
* close down subplans
294
ExecEndNode(outerPlanState(node));
295
ExecEndNode(innerPlanState(node));
298
/* ----------------------------------------------------------------
299
* ExecReScanRecursiveUnion
301
* Rescans the relation.
302
* ----------------------------------------------------------------
305
ExecReScanRecursiveUnion(RecursiveUnionState *node)
307
PlanState *outerPlan = outerPlanState(node);
308
PlanState *innerPlan = innerPlanState(node);
309
RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
312
* Set recursive term's chgParam to tell it that we'll modify the working
313
* table and therefore it has to rescan.
315
innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam);
318
* if chgParam of subnode is not null then plan will be re-scanned by
319
* first ExecProcNode. Because of above, we only have to do this to the
320
* non-recursive term.
322
if (outerPlan->chgParam == NULL)
323
ExecReScan(outerPlan);
325
/* Release any hashtable storage */
326
if (node->tableContext)
327
MemoryContextResetAndDeleteChildren(node->tableContext);
329
/* And rebuild empty hashtable if needed */
330
if (plan->numCols > 0)
331
build_hash_table(node);
333
/* reset processing state */
334
node->recursing = false;
335
node->intermediate_empty = true;
336
tuplestore_clear(node->working_table);
337
tuplestore_clear(node->intermediate_table);