1 /*-------------------------------------------------------------------------
4 * routines to handle RecursiveUnion nodes.
6 * Portions Copyright (c) 1996-2013, 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);