12
12
* relation. Then it is a simple matter to emit the output demanded by the
13
13
* SQL spec for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
14
14
*
15
- * In SETOP_HASHED mode, the input is delivered in no particular order.
16
- * We build a hash table in memory with one entry for each group of
17
- * identical tuples, and count the number of tuples in the group from
18
- * each relation. After seeing all the input, we scan the hashtable and
19
- * generate the correct output using those counts.
15
+ * In SETOP_HASHED mode, the input is delivered in no particular order,
16
+ * except that we know all the tuples from one input relation will come before
17
+ * all the tuples of the other. The planner guarantees that the first input
18
+ * relation is the left-hand one for EXCEPT, and tries to make the smaller
19
+ * input relation come first for INTERSECT. We build a hash table in memory
20
+ * with one entry for each group of identical tuples, and count the number of
21
+ * tuples in the group from each relation. After seeing all the input, we
22
+ * scan the hashtable and generate the correct output using those counts.
23
+ * We can avoid making hashtable entries for any tuples appearing only in the
24
+ * second input relation, since they cannot result in any output.
20
25
*
21
26
* This node type is not used for UNION or UNION ALL, since those can be
22
27
* implemented more cheaply (there's no need for the junk attribute to
32
37
*
33
38
*
34
39
* IDENTIFICATION
35
- * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.26 2008/08/07 03:04:03 tgl Exp $
40
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.27 2008/08/07 19:35:02 tgl Exp $
36
41
*
37
42
*-------------------------------------------------------------------------
38
43
*/
@@ -82,18 +87,30 @@ static TupleTableSlot *setop_retrieve_hash_table(SetOpState *setopstate);
82
87
/*
83
88
* Initialize state for a new group of input values.
84
89
*/
85
- static void
86
- initialize_counts (SetOpState * setopstate , SetOpStatePerGroup pergroup )
90
+ static inline void
91
+ initialize_counts (SetOpStatePerGroup pergroup )
87
92
{
88
93
pergroup -> numLeft = pergroup -> numRight = 0 ;
89
94
}
90
95
91
96
/*
92
97
* Advance the appropriate counter for one input tuple.
93
98
*/
94
- static void
95
- advance_counts (SetOpState * setopstate , SetOpStatePerGroup pergroup ,
96
- TupleTableSlot * inputslot )
99
+ static inline void
100
+ advance_counts (SetOpStatePerGroup pergroup , int flag )
101
+ {
102
+ if (flag )
103
+ pergroup -> numRight ++ ;
104
+ else
105
+ pergroup -> numLeft ++ ;
106
+ }
107
+
108
+ /*
109
+ * Fetch the "flag" column from an input tuple.
110
+ * This is an integer column with value 0 for left side, 1 for right side.
111
+ */
112
+ static int
113
+ fetch_tuple_flag (SetOpState * setopstate , TupleTableSlot * inputslot )
97
114
{
98
115
SetOp * node = (SetOp * ) setopstate -> ps .plan ;
99
116
int flag ;
@@ -103,10 +120,8 @@ advance_counts(SetOpState *setopstate, SetOpStatePerGroup pergroup,
103
120
node -> flagColIdx ,
104
121
& isNull ));
105
122
Assert (!isNull );
106
- if (flag )
107
- pergroup -> numRight ++ ;
108
- else
109
- pergroup -> numLeft ++ ;
123
+ Assert (flag == 0 || flag == 1 );
124
+ return flag ;
110
125
}
111
126
112
127
/*
@@ -130,33 +145,6 @@ build_hash_table(SetOpState *setopstate)
130
145
setopstate -> tempContext );
131
146
}
132
147
133
- /*
134
- * Find or create a hashtable entry for the tuple group containing the
135
- * given tuple.
136
- */
137
- static SetOpHashEntry
138
- lookup_hash_entry (SetOpState * setopstate , TupleTableSlot * inputslot )
139
- {
140
- SetOpHashEntry entry ;
141
- bool isnew ;
142
-
143
- /* find or create the hashtable entry */
144
- entry = (SetOpHashEntry ) LookupTupleHashEntry (setopstate -> hashtable ,
145
- inputslot ,
146
- & isnew );
147
-
148
- if (isnew )
149
- {
150
- /* initialize counts for new tuple group */
151
- initialize_counts (setopstate , & entry -> pergroup );
152
- }
153
-
154
- /* Must reset temp context after each hashtable lookup */
155
- MemoryContextReset (setopstate -> tempContext );
156
-
157
- return entry ;
158
- }
159
-
160
148
/*
161
149
* We've completed processing a tuple group. Decide how many copies (if any)
162
150
* of its representative row to emit, and store the count into numOutput.
@@ -289,10 +277,11 @@ setop_retrieve_direct(SetOpState *setopstate)
289
277
setopstate -> grp_firstTuple = NULL ; /* don't keep two pointers */
290
278
291
279
/* Initialize working state for a new input tuple group */
292
- initialize_counts (setopstate , pergroup );
280
+ initialize_counts (pergroup );
293
281
294
282
/* Count the first input tuple */
295
- advance_counts (setopstate , pergroup , resultTupleSlot );
283
+ advance_counts (pergroup ,
284
+ fetch_tuple_flag (setopstate , resultTupleSlot ));
296
285
297
286
/*
298
287
* Scan the outer plan until we exhaust it or cross a group boundary.
@@ -324,7 +313,8 @@ setop_retrieve_direct(SetOpState *setopstate)
324
313
}
325
314
326
315
/* Still in same group, so count this tuple */
327
- advance_counts (setopstate , pergroup , outerslot );
316
+ advance_counts (pergroup ,
317
+ fetch_tuple_flag (setopstate , outerslot ));
328
318
}
329
319
330
320
/*
@@ -351,30 +341,73 @@ setop_retrieve_direct(SetOpState *setopstate)
351
341
static void
352
342
setop_fill_hash_table (SetOpState * setopstate )
353
343
{
344
+ SetOp * node = (SetOp * ) setopstate -> ps .plan ;
354
345
PlanState * outerPlan ;
355
- SetOpHashEntry entry ;
356
- TupleTableSlot * outerslot ;
346
+ int firstFlag ;
347
+ bool in_first_rel ;
357
348
358
349
/*
359
350
* get state info from node
360
351
*/
361
352
outerPlan = outerPlanState (setopstate );
353
+ firstFlag = node -> firstFlag ;
354
+ /* verify planner didn't mess up */
355
+ Assert (firstFlag == 0 ||
356
+ (firstFlag == 1 &&
357
+ (node -> cmd == SETOPCMD_INTERSECT ||
358
+ node -> cmd == SETOPCMD_INTERSECT_ALL )));
362
359
363
360
/*
364
361
* Process each outer-plan tuple, and then fetch the next one, until we
365
362
* exhaust the outer plan.
366
363
*/
364
+ in_first_rel = true;
367
365
for (;;)
368
366
{
367
+ TupleTableSlot * outerslot ;
368
+ int flag ;
369
+ SetOpHashEntry entry ;
370
+ bool isnew ;
371
+
369
372
outerslot = ExecProcNode (outerPlan );
370
373
if (TupIsNull (outerslot ))
371
374
break ;
372
375
373
- /* Find or build hashtable entry for this tuple's group */
374
- entry = lookup_hash_entry (setopstate , outerslot );
376
+ /* Identify whether it's left or right input */
377
+ flag = fetch_tuple_flag (setopstate , outerslot );
378
+
379
+ if (flag == firstFlag )
380
+ {
381
+ /* (still) in first input relation */
382
+ Assert (in_first_rel );
383
+
384
+ /* Find or build hashtable entry for this tuple's group */
385
+ entry = (SetOpHashEntry )
386
+ LookupTupleHashEntry (setopstate -> hashtable , outerslot , & isnew );
387
+
388
+ /* If new tuple group, initialize counts */
389
+ if (isnew )
390
+ initialize_counts (& entry -> pergroup );
391
+
392
+ /* Advance the counts */
393
+ advance_counts (& entry -> pergroup , flag );
394
+ }
395
+ else
396
+ {
397
+ /* reached second relation */
398
+ in_first_rel = false;
399
+
400
+ /* For tuples not seen previously, do not make hashtable entry */
401
+ entry = (SetOpHashEntry )
402
+ LookupTupleHashEntry (setopstate -> hashtable , outerslot , NULL );
403
+
404
+ /* Advance the counts if entry is already present */
405
+ if (entry )
406
+ advance_counts (& entry -> pergroup , flag );
407
+ }
375
408
376
- /* Advance the counts */
377
- advance_counts (setopstate , & entry -> pergroup , outerslot );
409
+ /* Must reset temp context after each hashtable lookup */
410
+ MemoryContextReset (setopstate -> tempContext );
378
411
}
379
412
380
413
setopstate -> table_filled = true;
0 commit comments