PostgreSQL Source Code  git master
nodeSetOp.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nodeSetOp.c
4  * Routines to handle INTERSECT and EXCEPT selection
5  *
6  * The input of a SetOp node consists of tuples from two relations,
7  * which have been combined into one dataset, with a junk attribute added
8  * that shows which relation each tuple came from. In SETOP_SORTED mode,
9  * the input has furthermore been sorted according to all the grouping
10  * columns (ie, all the non-junk attributes). The SetOp node scans each
11  * group of identical tuples to determine how many came from each input
12  * relation. Then it is a simple matter to emit the output demanded by the
13  * SQL spec for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
14  *
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.
25  *
26  * This node type is not used for UNION or UNION ALL, since those can be
27  * implemented more cheaply (there's no need for the junk attribute to
28  * identify the source relation).
29  *
30  * Note that SetOp does no qual checking nor projection. The delivered
31  * output tuples are just copies of the first-to-arrive tuple in each
32  * input group.
33  *
34  *
35  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
36  * Portions Copyright (c) 1994, Regents of the University of California
37  *
38  *
39  * IDENTIFICATION
40  * src/backend/executor/nodeSetOp.c
41  *
42  *-------------------------------------------------------------------------
43  */
44 
45 #include "postgres.h"
46 
47 #include "access/htup_details.h"
48 #include "executor/executor.h"
49 #include "executor/nodeSetOp.h"
50 #include "miscadmin.h"
51 #include "utils/memutils.h"
52 
53 
54 /*
55  * SetOpStatePerGroupData - per-group working state
56  *
57  * These values are working state that is initialized at the start of
58  * an input tuple group and updated for each input tuple.
59  *
60  * In SETOP_SORTED mode, we need only one of these structs, and it's kept in
61  * the plan state node. In SETOP_HASHED mode, the hash table contains one
62  * of these for each tuple group.
63  */
64 typedef struct SetOpStatePerGroupData
65 {
66  long numLeft; /* number of left-input dups in group */
67  long numRight; /* number of right-input dups in group */
69 
70 
72 static void setop_fill_hash_table(SetOpState *setopstate);
74 
75 
76 /*
77  * Initialize state for a new group of input values.
78  */
79 static inline void
81 {
82  pergroup->numLeft = pergroup->numRight = 0;
83 }
84 
85 /*
86  * Advance the appropriate counter for one input tuple.
87  */
88 static inline void
90 {
91  if (flag)
92  pergroup->numRight++;
93  else
94  pergroup->numLeft++;
95 }
96 
97 /*
98  * Fetch the "flag" column from an input tuple.
99  * This is an integer column with value 0 for left side, 1 for right side.
100  */
101 static int
103 {
104  SetOp *node = (SetOp *) setopstate->ps.plan;
105  int flag;
106  bool isNull;
107 
108  flag = DatumGetInt32(slot_getattr(inputslot,
109  node->flagColIdx,
110  &isNull));
111  Assert(!isNull);
112  Assert(flag == 0 || flag == 1);
113  return flag;
114 }
115 
116 /*
117  * Initialize the hash table to empty.
118  */
119 static void
121 {
122  SetOp *node = (SetOp *) setopstate->ps.plan;
123  ExprContext *econtext = setopstate->ps.ps_ExprContext;
124  TupleDesc desc = ExecGetResultType(outerPlanState(setopstate));
125 
126  Assert(node->strategy == SETOP_HASHED);
127  Assert(node->numGroups > 0);
128 
129  setopstate->hashtable = BuildTupleHashTable(&setopstate->ps,
130  desc,
131  node->numCols,
132  node->dupColIdx,
133  setopstate->eqfuncoids,
134  setopstate->hashfunctions,
135  node->numGroups,
136  0,
137  setopstate->tableContext,
138  econtext->ecxt_per_tuple_memory,
139  false);
140 }
141 
142 /*
143  * We've completed processing a tuple group. Decide how many copies (if any)
144  * of its representative row to emit, and store the count into numOutput.
145  * This logic is straight from the SQL92 specification.
146  */
147 static void
149 {
150  SetOp *plannode = (SetOp *) setopstate->ps.plan;
151 
152  switch (plannode->cmd)
153  {
154  case SETOPCMD_INTERSECT:
155  if (pergroup->numLeft > 0 && pergroup->numRight > 0)
156  setopstate->numOutput = 1;
157  else
158  setopstate->numOutput = 0;
159  break;
161  setopstate->numOutput =
162  (pergroup->numLeft < pergroup->numRight) ?
163  pergroup->numLeft : pergroup->numRight;
164  break;
165  case SETOPCMD_EXCEPT:
166  if (pergroup->numLeft > 0 && pergroup->numRight == 0)
167  setopstate->numOutput = 1;
168  else
169  setopstate->numOutput = 0;
170  break;
171  case SETOPCMD_EXCEPT_ALL:
172  setopstate->numOutput =
173  (pergroup->numLeft < pergroup->numRight) ?
174  0 : (pergroup->numLeft - pergroup->numRight);
175  break;
176  default:
177  elog(ERROR, "unrecognized set op: %d", (int) plannode->cmd);
178  break;
179  }
180 }
181 
182 
183 /* ----------------------------------------------------------------
184  * ExecSetOp
185  * ----------------------------------------------------------------
186  */
187 static TupleTableSlot * /* return: a tuple or NULL */
189 {
190  SetOpState *node = castNode(SetOpState, pstate);
191  SetOp *plannode = (SetOp *) node->ps.plan;
192  TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot;
193 
195 
196  /*
197  * If the previously-returned tuple needs to be returned more than once,
198  * keep returning it.
199  */
200  if (node->numOutput > 0)
201  {
202  node->numOutput--;
203  return resultTupleSlot;
204  }
205 
206  /* Otherwise, we're done if we are out of groups */
207  if (node->setop_done)
208  return NULL;
209 
210  /* Fetch the next tuple group according to the correct strategy */
211  if (plannode->strategy == SETOP_HASHED)
212  {
213  if (!node->table_filled)
214  setop_fill_hash_table(node);
215  return setop_retrieve_hash_table(node);
216  }
217  else
218  return setop_retrieve_direct(node);
219 }
220 
221 /*
222  * ExecSetOp for non-hashed case
223  */
224 static TupleTableSlot *
226 {
228  SetOpStatePerGroup pergroup;
229  TupleTableSlot *outerslot;
230  TupleTableSlot *resultTupleSlot;
231  ExprContext *econtext = setopstate->ps.ps_ExprContext;
232 
233  /*
234  * get state info from node
235  */
236  outerPlan = outerPlanState(setopstate);
237  pergroup = (SetOpStatePerGroup) setopstate->pergroup;
238  resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;
239 
240  /*
241  * We loop retrieving groups until we find one we should return
242  */
243  while (!setopstate->setop_done)
244  {
245  /*
246  * If we don't already have the first tuple of the new group, fetch it
247  * from the outer plan.
248  */
249  if (setopstate->grp_firstTuple == NULL)
250  {
251  outerslot = ExecProcNode(outerPlan);
252  if (!TupIsNull(outerslot))
253  {
254  /* Make a copy of the first input tuple */
255  setopstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
256  }
257  else
258  {
259  /* outer plan produced no tuples at all */
260  setopstate->setop_done = true;
261  return NULL;
262  }
263  }
264 
265  /*
266  * Store the copied first input tuple in the tuple table slot reserved
267  * for it. The tuple will be deleted when it is cleared from the
268  * slot.
269  */
270  ExecStoreTuple(setopstate->grp_firstTuple,
271  resultTupleSlot,
273  true);
274  setopstate->grp_firstTuple = NULL; /* don't keep two pointers */
275 
276  /* Initialize working state for a new input tuple group */
277  initialize_counts(pergroup);
278 
279  /* Count the first input tuple */
280  advance_counts(pergroup,
281  fetch_tuple_flag(setopstate, resultTupleSlot));
282 
283  /*
284  * Scan the outer plan until we exhaust it or cross a group boundary.
285  */
286  for (;;)
287  {
288  outerslot = ExecProcNode(outerPlan);
289  if (TupIsNull(outerslot))
290  {
291  /* no more outer-plan tuples available */
292  setopstate->setop_done = true;
293  break;
294  }
295 
296  /*
297  * Check whether we've crossed a group boundary.
298  */
299  econtext->ecxt_outertuple = resultTupleSlot;
300  econtext->ecxt_innertuple = outerslot;
301 
302  if (!ExecQualAndReset(setopstate->eqfunction, econtext))
303  {
304  /*
305  * Save the first input tuple of the next group.
306  */
307  setopstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
308  break;
309  }
310 
311  /* Still in same group, so count this tuple */
312  advance_counts(pergroup,
313  fetch_tuple_flag(setopstate, outerslot));
314  }
315 
316  /*
317  * Done scanning input tuple group. See if we should emit any copies
318  * of result tuple, and if so return the first copy.
319  */
320  set_output_count(setopstate, pergroup);
321 
322  if (setopstate->numOutput > 0)
323  {
324  setopstate->numOutput--;
325  return resultTupleSlot;
326  }
327  }
328 
329  /* No more groups */
330  ExecClearTuple(resultTupleSlot);
331  return NULL;
332 }
333 
334 /*
335  * ExecSetOp for hashed case: phase 1, read input and build hash table
336  */
337 static void
339 {
340  SetOp *node = (SetOp *) setopstate->ps.plan;
342  int firstFlag;
343  bool in_first_rel PG_USED_FOR_ASSERTS_ONLY;
344  ExprContext *econtext = setopstate->ps.ps_ExprContext;
345 
346  /*
347  * get state info from node
348  */
349  outerPlan = outerPlanState(setopstate);
350  firstFlag = node->firstFlag;
351  /* verify planner didn't mess up */
352  Assert(firstFlag == 0 ||
353  (firstFlag == 1 &&
354  (node->cmd == SETOPCMD_INTERSECT ||
355  node->cmd == SETOPCMD_INTERSECT_ALL)));
356 
357  /*
358  * Process each outer-plan tuple, and then fetch the next one, until we
359  * exhaust the outer plan.
360  */
361  in_first_rel = true;
362  for (;;)
363  {
364  TupleTableSlot *outerslot;
365  int flag;
366  TupleHashEntryData *entry;
367  bool isnew;
368 
369  outerslot = ExecProcNode(outerPlan);
370  if (TupIsNull(outerslot))
371  break;
372 
373  /* Identify whether it's left or right input */
374  flag = fetch_tuple_flag(setopstate, outerslot);
375 
376  if (flag == firstFlag)
377  {
378  /* (still) in first input relation */
379  Assert(in_first_rel);
380 
381  /* Find or build hashtable entry for this tuple's group */
382  entry = LookupTupleHashEntry(setopstate->hashtable, outerslot,
383  &isnew);
384 
385  /* If new tuple group, initialize counts */
386  if (isnew)
387  {
388  entry->additional = (SetOpStatePerGroup)
390  sizeof(SetOpStatePerGroupData));
392  }
393 
394  /* Advance the counts */
396  }
397  else
398  {
399  /* reached second relation */
400  in_first_rel = false;
401 
402  /* For tuples not seen previously, do not make hashtable entry */
403  entry = LookupTupleHashEntry(setopstate->hashtable, outerslot,
404  NULL);
405 
406  /* Advance the counts if entry is already present */
407  if (entry)
409  }
410 
411  /* Must reset expression context after each hashtable lookup */
412  ResetExprContext(econtext);
413  }
414 
415  setopstate->table_filled = true;
416  /* Initialize to walk the hash table */
417  ResetTupleHashIterator(setopstate->hashtable, &setopstate->hashiter);
418 }
419 
420 /*
421  * ExecSetOp for hashed case: phase 2, retrieving groups from hash table
422  */
423 static TupleTableSlot *
425 {
426  TupleHashEntryData *entry;
427  TupleTableSlot *resultTupleSlot;
428 
429  /*
430  * get state info from node
431  */
432  resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;
433 
434  /*
435  * We loop retrieving groups until we find one we should return
436  */
437  while (!setopstate->setop_done)
438  {
440 
441  /*
442  * Find the next entry in the hash table
443  */
444  entry = ScanTupleHashTable(setopstate->hashtable, &setopstate->hashiter);
445  if (entry == NULL)
446  {
447  /* No more entries in hashtable, so done */
448  setopstate->setop_done = true;
449  return NULL;
450  }
451 
452  /*
453  * See if we should emit any copies of this tuple, and if so return
454  * the first copy.
455  */
456  set_output_count(setopstate, (SetOpStatePerGroup) entry->additional);
457 
458  if (setopstate->numOutput > 0)
459  {
460  setopstate->numOutput--;
461  return ExecStoreMinimalTuple(entry->firstTuple,
462  resultTupleSlot,
463  false);
464  }
465  }
466 
467  /* No more groups */
468  ExecClearTuple(resultTupleSlot);
469  return NULL;
470 }
471 
472 /* ----------------------------------------------------------------
473  * ExecInitSetOp
474  *
475  * This initializes the setop node state structures and
476  * the node's subplan.
477  * ----------------------------------------------------------------
478  */
479 SetOpState *
480 ExecInitSetOp(SetOp *node, EState *estate, int eflags)
481 {
482  SetOpState *setopstate;
483  TupleDesc outerDesc;
484 
485  /* check for unsupported flags */
486  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
487 
488  /*
489  * create state structure
490  */
491  setopstate = makeNode(SetOpState);
492  setopstate->ps.plan = (Plan *) node;
493  setopstate->ps.state = estate;
494  setopstate->ps.ExecProcNode = ExecSetOp;
495 
496  setopstate->eqfuncoids = NULL;
497  setopstate->hashfunctions = NULL;
498  setopstate->setop_done = false;
499  setopstate->numOutput = 0;
500  setopstate->pergroup = NULL;
501  setopstate->grp_firstTuple = NULL;
502  setopstate->hashtable = NULL;
503  setopstate->tableContext = NULL;
504 
505  /*
506  * create expression context
507  */
508  ExecAssignExprContext(estate, &setopstate->ps);
509 
510  /*
511  * If hashing, we also need a longer-lived context to store the hash
512  * table. The table can't just be kept in the per-query context because
513  * we want to be able to throw it away in ExecReScanSetOp.
514  */
515  if (node->strategy == SETOP_HASHED)
516  setopstate->tableContext =
518  "SetOp hash table",
520 
521  /*
522  * initialize child nodes
523  *
524  * If we are hashing then the child plan does not need to handle REWIND
525  * efficiently; see ExecReScanSetOp.
526  */
527  if (node->strategy == SETOP_HASHED)
528  eflags &= ~EXEC_FLAG_REWIND;
529  outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags);
530  outerDesc = ExecGetResultType(outerPlanState(setopstate));
531 
532  /*
533  * Initialize result slot and type. Setop nodes do no projections, so
534  * initialize projection info for this node appropriately.
535  */
536  ExecInitResultTupleSlotTL(estate, &setopstate->ps);
537  setopstate->ps.ps_ProjInfo = NULL;
538 
539  /*
540  * Precompute fmgr lookup data for inner loop. We need both equality and
541  * hashing functions to do it by hashing, but only equality if not
542  * hashing.
543  */
544  if (node->strategy == SETOP_HASHED)
546  node->dupOperators,
547  &setopstate->eqfuncoids,
548  &setopstate->hashfunctions);
549  else
550  setopstate->eqfunction =
551  execTuplesMatchPrepare(outerDesc,
552  node->numCols,
553  node->dupColIdx,
554  node->dupOperators,
555  &setopstate->ps);
556 
557  if (node->strategy == SETOP_HASHED)
558  {
559  build_hash_table(setopstate);
560  setopstate->table_filled = false;
561  }
562  else
563  {
564  setopstate->pergroup =
566  }
567 
568  return setopstate;
569 }
570 
571 /* ----------------------------------------------------------------
572  * ExecEndSetOp
573  *
574  * This shuts down the subplan and frees resources allocated
575  * to this node.
576  * ----------------------------------------------------------------
577  */
578 void
580 {
581  /* clean up tuple table */
583 
584  /* free subsidiary stuff including hashtable */
585  if (node->tableContext)
587  ExecFreeExprContext(&node->ps);
588 
590 }
591 
592 
593 void
595 {
597  node->setop_done = false;
598  node->numOutput = 0;
599 
600  if (((SetOp *) node->ps.plan)->strategy == SETOP_HASHED)
601  {
602  /*
603  * In the hashed case, if we haven't yet built the hash table then we
604  * can just return; nothing done yet, so nothing to undo. If subnode's
605  * chgParam is not NULL then it will be re-scanned by ExecProcNode,
606  * else no reason to re-scan it at all.
607  */
608  if (!node->table_filled)
609  return;
610 
611  /*
612  * If we do have the hash table and the subplan does not have any
613  * parameter changes, then we can just rescan the existing hash table;
614  * no need to build it again.
615  */
616  if (node->ps.lefttree->chgParam == NULL)
617  {
619  return;
620  }
621  }
622 
623  /* Release first tuple of group, if we have made a copy */
624  if (node->grp_firstTuple != NULL)
625  {
627  node->grp_firstTuple = NULL;
628  }
629 
630  /* Release any hashtable storage */
631  if (node->tableContext)
633 
634  /* And rebuild empty hashtable if needed */
635  if (((SetOp *) node->ps.plan)->strategy == SETOP_HASHED)
636  {
637  build_hash_table(node);
638  node->table_filled = false;
639  }
640 
641  /*
642  * if chgParam of subnode is not null then plan will be re-scanned by
643  * first ExecProcNode.
644  */
645  if (node->ps.lefttree->chgParam == NULL)
646  ExecReScan(node->ps.lefttree);
647 }
void ExecEndSetOp(SetOpState *node)
Definition: nodeSetOp.c:579
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:356
#define ScanTupleHashTable(htable, iter)
Definition: execnodes.h:697
void execTuplesHashPrepare(int numCols, Oid *eqOperators, Oid **eqFuncOids, FmgrInfo **hashFunctions)
Definition: execGrouping.c:95
SetOpStrategy strategy
Definition: plannodes.h:913
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
SetOpCmd cmd
Definition: plannodes.h:912
TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:420
MemoryContext tableContext
Definition: execnodes.h:2165
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:948
struct SetOpStatePerGroupData * SetOpStatePerGroup
Definition: execnodes.h:2150
ExprState * eqfunction
Definition: execnodes.h:2155
#define DatumGetInt32(X)
Definition: postgres.h:457
long numGroups
Definition: plannodes.h:920
Oid * eqfuncoids
Definition: execnodes.h:2156
#define castNode(_type_, nodeptr)
Definition: nodes.h:586
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:538
static void set_output_count(SetOpState *setopstate, SetOpStatePerGroup pergroup)
Definition: nodeSetOp.c:148
ExprContext * ps_ExprContext
Definition: execnodes.h:947
MinimalTuple firstTuple
Definition: execnodes.h:651
void ExecReScan(PlanState *node)
Definition: execAmi.c:76
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
PlanState ps
Definition: execnodes.h:2154
#define InvalidBuffer
Definition: buf.h:25
AttrNumber * dupColIdx
Definition: plannodes.h:916
HeapTuple grp_firstTuple
Definition: execnodes.h:2162
EState * state
Definition: execnodes.h:914
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1773
int numCols
Definition: plannodes.h:914
void ExecReScanSetOp(SetOpState *node)
Definition: nodeSetOp.c:594
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:566
struct PlanState * lefttree
Definition: execnodes.h:931
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:946
#define ERROR
Definition: elog.h:43
static void initialize_counts(SetOpStatePerGroup pergroup)
Definition: nodeSetOp.c:80
AttrNumber flagColIdx
Definition: plannodes.h:918
static void build_hash_table(SetOpState *setopstate)
Definition: nodeSetOp.c:120
Oid * dupOperators
Definition: plannodes.h:917
static void setop_fill_hash_table(SetOpState *setopstate)
Definition: nodeSetOp.c:338
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define EXEC_FLAG_BACKWARD
Definition: executor.h:60
#define outerPlanState(node)
Definition: execnodes.h:966
MemoryContext tablecxt
Definition: execnodes.h:672
bool table_filled
Definition: execnodes.h:2166
char * flag(int b)
Definition: test-ctype.c:33
static TupleTableSlot * setop_retrieve_direct(SetOpState *setopstate)
Definition: nodeSetOp.c:225
static int fetch_tuple_flag(SetOpState *setopstate, TupleTableSlot *inputslot)
Definition: nodeSetOp.c:102
long numOutput
Definition: execnodes.h:2159
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:220
#define TupIsNull(slot)
Definition: tuptable.h:146
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
SetOpState * ExecInitSetOp(SetOp *node, EState *estate, int eflags)
Definition: nodeSetOp.c:480
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, bool *isnew)
Definition: execGrouping.c:233
#define EXEC_FLAG_REWIND
Definition: executor.h:59
TupleHashIterator hashiter
Definition: execnodes.h:2167
Bitmapset * chgParam
Definition: execnodes.h:941
#define outerPlan(node)
Definition: plannodes.h:176
static TupleTableSlot * ExecSetOp(PlanState *pstate)
Definition: nodeSetOp.c:188
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
HeapTuple ExecCopySlotTuple(TupleTableSlot *slot)
Definition: execTuples.c:581
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:388
void * palloc0(Size size)
Definition: mcxt.c:955
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:918
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:233
void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
Definition: execTuples.c:890
FmgrInfo * hashfunctions
Definition: execnodes.h:2157
static TupleTableSlot * setop_retrieve_hash_table(SetOpState *setopstate)
Definition: nodeSetOp.c:424
Plan * plan
Definition: execnodes.h:912
#define ResetTupleHashIterator(htable, iter)
Definition: execnodes.h:695
int firstFlag
Definition: plannodes.h:919
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, AttrNumber *keyColIdx, Oid *eqOperators, PlanState *parent)
Definition: execGrouping.c:60
#define makeNode(_type_)
Definition: nodes.h:565
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define Assert(condition)
Definition: c.h:699
#define EXEC_FLAG_MARK
Definition: executor.h:61
bool setop_done
Definition: execnodes.h:2158
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:428
SetOpStatePerGroup pergroup
Definition: execnodes.h:2161
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:438
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:771
TupleHashTable BuildTupleHashTable(PlanState *parent, TupleDesc inputDesc, int numCols, AttrNumber *keyColIdx, Oid *eqfuncoids, FmgrInfo *hashfunctions, long nbuckets, Size additionalsize, MemoryContext tablecxt, MemoryContext tempcxt, bool use_variable_hash_iv)
Definition: execGrouping.c:152
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1518
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:123
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
static void advance_counts(SetOpStatePerGroup pergroup, int flag)
Definition: nodeSetOp.c:89
struct SetOpStatePerGroupData SetOpStatePerGroupData
TupleHashTable hashtable
Definition: execnodes.h:2164
#define ResetExprContext(econtext)
Definition: executor.h:483