PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
nodeRecursiveunion.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nodeRecursiveunion.c
4  * routines to handle RecursiveUnion nodes.
5  *
6  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/executor/nodeRecursiveunion.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "executor/execdebug.h"
19 #include "miscadmin.h"
20 #include "utils/memutils.h"
21 
22 
23 /*
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.
26  */
27 typedef struct RUHashEntryData *RUHashEntry;
28 
29 typedef struct RUHashEntryData
30 {
31  TupleHashEntryData shared; /* common header for hash table entries */
33 
34 
35 /*
36  * Initialize the hash table to empty.
37  */
38 static void
40 {
41  RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan;
42 
43  Assert(node->numCols > 0);
44  Assert(node->numGroups > 0);
45 
46  rustate->hashtable = BuildTupleHashTable(node->numCols,
47  node->dupColIdx,
48  rustate->eqfunctions,
49  rustate->hashfunctions,
50  node->numGroups,
51  sizeof(RUHashEntryData),
52  rustate->tableContext,
53  rustate->tempContext);
54 }
55 
56 
57 /* ----------------------------------------------------------------
58  * ExecRecursiveUnion(node)
59  *
60  * Scans the recursive query sequentially and returns the next
61  * qualifying tuple.
62  *
63  * 1. evaluate non recursive term and assign the result to RT
64  *
65  * 2. execute recursive terms
66  *
67  * 2.1 WT := RT
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
71  * 2.5 append WT to RT
72  * 2.6 go back to 2.2
73  * ----------------------------------------------------------------
74  */
77 {
80  RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
81  TupleTableSlot *slot;
82  bool isnew;
83 
84  /* 1. Evaluate non-recursive term */
85  if (!node->recursing)
86  {
87  for (;;)
88  {
89  slot = ExecProcNode(outerPlan);
90  if (TupIsNull(slot))
91  break;
92  if (plan->numCols > 0)
93  {
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 */
98  /* Ignore tuple if already seen */
99  if (!isnew)
100  continue;
101  }
102  /* Each non-duplicate tuple goes to the working table ... */
104  /* ... and to the caller */
105  return slot;
106  }
107  node->recursing = true;
108  }
109 
110  /* 2. Execute recursive term */
111  for (;;)
112  {
113  slot = ExecProcNode(innerPlan);
114  if (TupIsNull(slot))
115  {
116  /* Done if there's nothing in the intermediate table */
117  if (node->intermediate_empty)
118  break;
119 
120  /* done with old working table ... */
122 
123  /* intermediate table becomes working table */
124  node->working_table = node->intermediate_table;
125 
126  /* create new empty intermediate table */
127  node->intermediate_table = tuplestore_begin_heap(false, false,
128  work_mem);
129  node->intermediate_empty = true;
130 
131  /* reset the recursive term */
132  innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
133  plan->wtParam);
134 
135  /* and continue fetching from recursive term */
136  continue;
137  }
138 
139  if (plan->numCols > 0)
140  {
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 */
145  /* Ignore tuple if already seen */
146  if (!isnew)
147  continue;
148  }
149 
150  /* Else, tuple is good; stash it in intermediate table ... */
151  node->intermediate_empty = false;
153  /* ... and return it */
154  return slot;
155  }
156 
157  return NULL;
158 }
159 
160 /* ----------------------------------------------------------------
161  * ExecInitRecursiveUnionScan
162  * ----------------------------------------------------------------
163  */
165 ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
166 {
167  RecursiveUnionState *rustate;
168  ParamExecData *prmdata;
169 
170  /* check for unsupported flags */
171  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
172 
173  /*
174  * create state structure
175  */
176  rustate = makeNode(RecursiveUnionState);
177  rustate->ps.plan = (Plan *) node;
178  rustate->ps.state = estate;
179 
180  rustate->eqfunctions = NULL;
181  rustate->hashfunctions = NULL;
182  rustate->hashtable = NULL;
183  rustate->tempContext = NULL;
184  rustate->tableContext = NULL;
185 
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);
191 
192  /*
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.
197  */
198  if (node->numCols > 0)
199  {
200  rustate->tempContext =
202  "RecursiveUnion",
206  rustate->tableContext =
208  "RecursiveUnion hash table",
212  }
213 
214  /*
215  * Make the state structure available to descendant WorkTableScan nodes
216  * via the Param slot reserved for it.
217  */
218  prmdata = &(estate->es_param_exec_vals[node->wtParam]);
219  Assert(prmdata->execPlan == NULL);
220  prmdata->value = PointerGetDatum(rustate);
221  prmdata->isnull = false;
222 
223  /*
224  * Miscellaneous initialization
225  *
226  * RecursiveUnion plans don't have expression contexts because they never
227  * call ExecQual or ExecProject.
228  */
229  Assert(node->plan.qual == NIL);
230 
231  /*
232  * RecursiveUnion nodes still have Result slots, which hold pointers to
233  * tuples, so we have to initialize them.
234  */
235  ExecInitResultTupleSlot(estate, &rustate->ps);
236 
237  /*
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.)
241  */
242  ExecAssignResultTypeFromTL(&rustate->ps);
243  rustate->ps.ps_ProjInfo = NULL;
244 
245  /*
246  * initialize child nodes
247  */
248  outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags);
249  innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags);
250 
251  /*
252  * If hashing, precompute fmgr lookup data for inner loop, and create the
253  * hash table.
254  */
255  if (node->numCols > 0)
256  {
258  node->dupOperators,
259  &rustate->eqfunctions,
260  &rustate->hashfunctions);
261  build_hash_table(rustate);
262  }
263 
264  return rustate;
265 }
266 
267 /* ----------------------------------------------------------------
268  * ExecEndRecursiveUnionScan
269  *
270  * frees any storage allocated through C routines.
271  * ----------------------------------------------------------------
272  */
273 void
275 {
276  /* Release tuplestores */
279 
280  /* free subsidiary stuff including hashtable */
281  if (node->tempContext)
283  if (node->tableContext)
285 
286  /*
287  * clean out the upper tuple table
288  */
290 
291  /*
292  * close down subplans
293  */
296 }
297 
298 /* ----------------------------------------------------------------
299  * ExecReScanRecursiveUnion
300  *
301  * Rescans the relation.
302  * ----------------------------------------------------------------
303  */
304 void
306 {
309  RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
310 
311  /*
312  * Set recursive term's chgParam to tell it that we'll modify the working
313  * table and therefore it has to rescan.
314  */
315  innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam);
316 
317  /*
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.
321  */
322  if (outerPlan->chgParam == NULL)
323  ExecReScan(outerPlan);
324 
325  /* Release any hashtable storage */
326  if (node->tableContext)
328 
329  /* And rebuild empty hashtable if needed */
330  if (plan->numCols > 0)
331  build_hash_table(node);
332 
333  /* reset processing state */
334  node->recursing = false;
335  node->intermediate_empty = true;
338 }
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:693
#define NIL
Definition: pg_list.h:69
List * qual
Definition: plannodes.h:122
void * execPlan
Definition: params.h:99
TupleTableSlot * ExecProcNode(PlanState *node)
Definition: execProcnode.c:374
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:203
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1059
Tuplestorestate * intermediate_table
Definition: execnodes.h:1196
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:614
Definition: plannodes.h:96
#define PointerGetDatum(X)
Definition: postgres.h:564
RecursiveUnionState * ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
FmgrInfo * hashfunctions
Definition: execnodes.h:1199
void ExecReScan(PlanState *node)
Definition: execAmi.c:73
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:442
TupleTableSlot * ExecRecursiveUnion(RecursiveUnionState *node)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:138
EState * state
Definition: execnodes.h:1029
void ExecEndRecursiveUnion(RecursiveUnionState *node)
#define ALLOCSET_DEFAULT_MINSIZE
Definition: memutils.h:142
void execTuplesHashPrepare(int numCols, Oid *eqOperators, FmgrInfo **eqFunctions, FmgrInfo **hashFunctions)
Definition: execGrouping.c:218
void ExecAssignResultTypeFromTL(PlanState *planstate)
Definition: execUtils.c:435
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1057
ParamExecData * es_param_exec_vals
Definition: execnodes.h:383
bool isnull
Definition: params.h:101
void ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
Definition: execTuples.c:835
AttrNumber * dupColIdx
Definition: plannodes.h:246
void tuplestore_clear(Tuplestorestate *state)
Definition: tuplestore.c:416
#define EXEC_FLAG_BACKWARD
Definition: executor.h:59
MemoryContext tempContext
Definition: execnodes.h:1200
#define outerPlanState(node)
Definition: execnodes.h:1072
#define innerPlan(node)
Definition: plannodes.h:150
TupleHashEntryData shared
TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, FmgrInfo *eqfunctions, FmgrInfo *hashfunctions, long nbuckets, Size entrysize, MemoryContext tablecxt, MemoryContext tempcxt)
Definition: execGrouping.c:275
#define TupIsNull(slot)
Definition: tuptable.h:138
FmgrInfo * eqfunctions
Definition: execnodes.h:1198
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, bool *isnew)
Definition: execGrouping.c:331
TupleHashTable hashtable
Definition: execnodes.h:1201
Bitmapset * chgParam
Definition: execnodes.h:1052
Tuplestorestate * working_table
Definition: execnodes.h:1195
#define outerPlan(node)
Definition: plannodes.h:151
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:88
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:316
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:436
int work_mem
Definition: globals.c:110
Plan * plan
Definition: execnodes.h:1027
void ExecReScanRecursiveUnion(RecursiveUnionState *node)
#define makeNode(_type_)
Definition: nodes.h:539
MemoryContext tableContext
Definition: execnodes.h:1202
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:667
#define EXEC_FLAG_MARK
Definition: executor.h:60
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:450
Oid * dupOperators
Definition: plannodes.h:247
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:668
struct RUHashEntryData * RUHashEntry
struct RUHashEntryData RUHashEntryData
#define ALLOCSET_DEFAULT_INITSIZE
Definition: memutils.h:143
static void build_hash_table(RecursiveUnionState *rustate)
Datum value
Definition: params.h:100
#define ALLOCSET_DEFAULT_MAXSIZE
Definition: memutils.h:144
#define innerPlanState(node)
Definition: execnodes.h:1071
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:136