8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $PostgreSQL: pgsql/src/backend/executor/nodeRecursiveunion.c,v 1.1 2008/10/04 21:56:53 tgl Exp $
11
+ * $PostgreSQL: pgsql/src/backend/executor/nodeRecursiveunion.c,v 1.2 2008/10/07 19:27:04 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
17
17
#include "executor/execdebug.h"
18
18
#include "executor/nodeRecursiveunion.h"
19
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 */
32
+ } RUHashEntryData ;
33
+
34
+
35
+ /*
36
+ * Initialize the hash table to empty.
37
+ */
38
+ static void
39
+ build_hash_table (RecursiveUnionState * rustate )
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
+ }
20
55
21
56
22
57
/* ----------------------------------------------------------------
@@ -44,49 +79,85 @@ ExecRecursiveUnion(RecursiveUnionState *node)
44
79
PlanState * innerPlan = innerPlanState (node );
45
80
RecursiveUnion * plan = (RecursiveUnion * ) node -> ps .plan ;
46
81
TupleTableSlot * slot ;
82
+ RUHashEntry entry ;
83
+ bool isnew ;
47
84
48
85
/* 1. Evaluate non-recursive term */
49
86
if (!node -> recursing )
50
87
{
51
- slot = ExecProcNode (outerPlan );
52
- if (!TupIsNull (slot ))
88
+ for (;;)
53
89
{
90
+ slot = ExecProcNode (outerPlan );
91
+ if (TupIsNull (slot ))
92
+ break ;
93
+ if (plan -> numCols > 0 )
94
+ {
95
+ /* Find or build hashtable entry for this tuple's group */
96
+ entry = (RUHashEntry )
97
+ LookupTupleHashEntry (node -> hashtable , slot , & isnew );
98
+ /* Must reset temp context after each hashtable lookup */
99
+ MemoryContextReset (node -> tempContext );
100
+ /* Ignore tuple if already seen */
101
+ if (!isnew )
102
+ continue ;
103
+ }
104
+ /* Each non-duplicate tuple goes to the working table ... */
54
105
tuplestore_puttupleslot (node -> working_table , slot );
106
+ /* ... and to the caller */
55
107
return slot ;
56
108
}
57
109
node -> recursing = true;
58
110
}
59
111
60
- retry :
61
112
/* 2. Execute recursive term */
62
- slot = ExecProcNode (innerPlan );
63
- if (TupIsNull (slot ))
113
+ for (;;)
64
114
{
65
- if (node -> intermediate_empty )
66
- return NULL ;
115
+ slot = ExecProcNode (innerPlan );
116
+ if (TupIsNull (slot ))
117
+ {
118
+ /* Done if there's nothing in the intermediate table */
119
+ if (node -> intermediate_empty )
120
+ break ;
67
121
68
- /* done with old working table ... */
69
- tuplestore_end (node -> working_table );
122
+ /* done with old working table ... */
123
+ tuplestore_end (node -> working_table );
70
124
71
- /* intermediate table becomes working table */
72
- node -> working_table = node -> intermediate_table ;
125
+ /* intermediate table becomes working table */
126
+ node -> working_table = node -> intermediate_table ;
73
127
74
- /* create new empty intermediate table */
75
- node -> intermediate_table = tuplestore_begin_heap (false, false, work_mem );
76
- node -> intermediate_empty = true;
128
+ /* create new empty intermediate table */
129
+ node -> intermediate_table = tuplestore_begin_heap (false, false,
130
+ work_mem );
131
+ node -> intermediate_empty = true;
77
132
78
- /* and reset the inner plan */
79
- innerPlan -> chgParam = bms_add_member (innerPlan -> chgParam ,
80
- plan -> wtParam );
81
- goto retry ;
82
- }
83
- else
84
- {
133
+ /* reset the recursive term */
134
+ innerPlan -> chgParam = bms_add_member (innerPlan -> chgParam ,
135
+ plan -> wtParam );
136
+
137
+ /* and continue fetching from recursive term */
138
+ continue ;
139
+ }
140
+
141
+ if (plan -> numCols > 0 )
142
+ {
143
+ /* Find or build hashtable entry for this tuple's group */
144
+ entry = (RUHashEntry )
145
+ LookupTupleHashEntry (node -> hashtable , slot , & isnew );
146
+ /* Must reset temp context after each hashtable lookup */
147
+ MemoryContextReset (node -> tempContext );
148
+ /* Ignore tuple if already seen */
149
+ if (!isnew )
150
+ continue ;
151
+ }
152
+
153
+ /* Else, tuple is good; stash it in intermediate table ... */
85
154
node -> intermediate_empty = false;
86
155
tuplestore_puttupleslot (node -> intermediate_table , slot );
87
- }
156
+ /* ... and return it */
157
+ return slot ;
158
+ }
88
159
89
- return slot ;
160
+ return NULL ;
90
161
}
91
162
92
163
/* ----------------------------------------------------------------
@@ -109,12 +180,40 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
109
180
rustate -> ps .plan = (Plan * ) node ;
110
181
rustate -> ps .state = estate ;
111
182
183
+ rustate -> eqfunctions = NULL ;
184
+ rustate -> hashfunctions = NULL ;
185
+ rustate -> hashtable = NULL ;
186
+ rustate -> tempContext = NULL ;
187
+ rustate -> tableContext = NULL ;
188
+
112
189
/* initialize processing state */
113
190
rustate -> recursing = false;
114
191
rustate -> intermediate_empty = true;
115
192
rustate -> working_table = tuplestore_begin_heap (false, false, work_mem );
116
193
rustate -> intermediate_table = tuplestore_begin_heap (false, false, work_mem );
117
194
195
+ /*
196
+ * If hashing, we need a per-tuple memory context for comparisons, and a
197
+ * longer-lived context to store the hash table. The table can't just be
198
+ * kept in the per-query context because we want to be able to throw it
199
+ * away when rescanning.
200
+ */
201
+ if (node -> numCols > 0 )
202
+ {
203
+ rustate -> tempContext =
204
+ AllocSetContextCreate (CurrentMemoryContext ,
205
+ "RecursiveUnion" ,
206
+ ALLOCSET_DEFAULT_MINSIZE ,
207
+ ALLOCSET_DEFAULT_INITSIZE ,
208
+ ALLOCSET_DEFAULT_MAXSIZE );
209
+ rustate -> tableContext =
210
+ AllocSetContextCreate (CurrentMemoryContext ,
211
+ "RecursiveUnion hash table" ,
212
+ ALLOCSET_DEFAULT_MINSIZE ,
213
+ ALLOCSET_DEFAULT_INITSIZE ,
214
+ ALLOCSET_DEFAULT_MAXSIZE );
215
+ }
216
+
118
217
/*
119
218
* Make the state structure available to descendant WorkTableScan nodes
120
219
* via the Param slot reserved for it.
@@ -154,6 +253,19 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
154
253
outerPlanState (rustate ) = ExecInitNode (outerPlan (node ), estate , eflags );
155
254
innerPlanState (rustate ) = ExecInitNode (innerPlan (node ), estate , eflags );
156
255
256
+ /*
257
+ * If hashing, precompute fmgr lookup data for inner loop, and create
258
+ * the hash table.
259
+ */
260
+ if (node -> numCols > 0 )
261
+ {
262
+ execTuplesHashPrepare (node -> numCols ,
263
+ node -> dupOperators ,
264
+ & rustate -> eqfunctions ,
265
+ & rustate -> hashfunctions );
266
+ build_hash_table (rustate );
267
+ }
268
+
157
269
return rustate ;
158
270
}
159
271
@@ -178,6 +290,12 @@ ExecEndRecursiveUnion(RecursiveUnionState *node)
178
290
tuplestore_end (node -> working_table );
179
291
tuplestore_end (node -> intermediate_table );
180
292
293
+ /* free subsidiary stuff including hashtable */
294
+ if (node -> tempContext )
295
+ MemoryContextDelete (node -> tempContext );
296
+ if (node -> tableContext )
297
+ MemoryContextDelete (node -> tableContext );
298
+
181
299
/*
182
300
* clean out the upper tuple table
183
301
*/
@@ -217,6 +335,14 @@ ExecRecursiveUnionReScan(RecursiveUnionState *node, ExprContext *exprCtxt)
217
335
if (outerPlan -> chgParam == NULL )
218
336
ExecReScan (outerPlan , exprCtxt );
219
337
338
+ /* Release any hashtable storage */
339
+ if (node -> tableContext )
340
+ MemoryContextResetAndDeleteChildren (node -> tableContext );
341
+
342
+ /* And rebuild empty hashtable if needed */
343
+ if (plan -> numCols > 0 )
344
+ build_hash_table (node );
345
+
220
346
/* reset processing state */
221
347
node -> recursing = false;
222
348
node -> intermediate_empty = true;
0 commit comments