1212 * relation. Then it is a simple matter to emit the output demanded by the
1313 * SQL spec for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
1414 *
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.
2025 *
2126 * This node type is not used for UNION or UNION ALL, since those can be
2227 * implemented more cheaply (there's no need for the junk attribute to
3237 *
3338 *
3439 * IDENTIFICATION
35- * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.26 2008/08/0703:04:03 tgl Exp $
40+ * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.27 2008/08/0719:35:02 tgl Exp $
3641 *
3742 *-------------------------------------------------------------------------
3843 */
@@ -82,18 +87,30 @@ static TupleTableSlot *setop_retrieve_hash_table(SetOpState *setopstate);
8287/*
8388 * Initialize state for a new group of input values.
8489 */
85- static void
86- initialize_counts (SetOpState * setopstate , SetOpStatePerGroup pergroup )
90+ static inline void
91+ initialize_counts (SetOpStatePerGroup pergroup )
8792{
8893pergroup -> numLeft = pergroup -> numRight = 0 ;
8994}
9095
9196/*
9297 * Advance the appropriate counter for one input tuple.
9398 */
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 )
97114{
98115SetOp * node = (SetOp * )setopstate -> ps .plan ;
99116int flag ;
@@ -103,10 +120,8 @@ advance_counts(SetOpState *setopstate, SetOpStatePerGroup pergroup,
103120node -> flagColIdx ,
104121& isNull ));
105122Assert (!isNull );
106- if (flag )
107- pergroup -> numRight ++ ;
108- else
109- pergroup -> numLeft ++ ;
123+ Assert (flag == 0 || flag == 1 );
124+ return flag ;
110125}
111126
112127/*
@@ -130,33 +145,6 @@ build_hash_table(SetOpState *setopstate)
130145setopstate -> tempContext );
131146}
132147
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-
160148/*
161149 * We've completed processing a tuple group. Decide how many copies (if any)
162150 * of its representative row to emit, and store the count into numOutput.
@@ -289,10 +277,11 @@ setop_retrieve_direct(SetOpState *setopstate)
289277setopstate -> grp_firstTuple = NULL ;/* don't keep two pointers */
290278
291279/* Initialize working state for a new input tuple group */
292- initialize_counts (setopstate , pergroup );
280+ initialize_counts (pergroup );
293281
294282/* Count the first input tuple */
295- advance_counts (setopstate ,pergroup ,resultTupleSlot );
283+ advance_counts (pergroup ,
284+ fetch_tuple_flag (setopstate ,resultTupleSlot ));
296285
297286/*
298287 * Scan the outer plan until we exhaust it or cross a group boundary.
@@ -324,7 +313,8 @@ setop_retrieve_direct(SetOpState *setopstate)
324313}
325314
326315/* Still in same group, so count this tuple */
327- advance_counts (setopstate ,pergroup ,outerslot );
316+ advance_counts (pergroup ,
317+ fetch_tuple_flag (setopstate ,outerslot ));
328318}
329319
330320/*
@@ -351,30 +341,73 @@ setop_retrieve_direct(SetOpState *setopstate)
351341static void
352342setop_fill_hash_table (SetOpState * setopstate )
353343{
344+ SetOp * node = (SetOp * )setopstate -> ps .plan ;
354345PlanState * outerPlan ;
355- SetOpHashEntry entry ;
356- TupleTableSlot * outerslot ;
346+ int firstFlag ;
347+ bool in_first_rel ;
357348
358349/*
359350 * get state info from node
360351 */
361352outerPlan = 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 )));
362359
363360/*
364361 * Process each outer-plan tuple, and then fetch the next one, until we
365362 * exhaust the outer plan.
366363 */
364+ in_first_rel = true;
367365for (;;)
368366{
367+ TupleTableSlot * outerslot ;
368+ int flag ;
369+ SetOpHashEntry entry ;
370+ bool isnew ;
371+
369372outerslot = ExecProcNode (outerPlan );
370373if (TupIsNull (outerslot ))
371374break ;
372375
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+ }
375408
376- /*Advance the counts */
377- advance_counts (setopstate , & entry -> pergroup , outerslot );
409+ /*Must reset temp context after each hashtable lookup */
410+ MemoryContextReset (setopstate -> tempContext );
378411}
379412
380413setopstate -> table_filled = true;