1
/*-------------------------------------------------------------------------
4
* Routines to handle INTERSECT and EXCEPT selection
6
* The input of a SetOp node consists of tuples from two relations,
7
* which have been combined into one dataset and sorted on all the nonjunk
8
* attributes. In addition there is a junk attribute that shows which
9
* relation each tuple came from. The SetOp node scans each group of
10
* identical tuples to determine how many came from each input relation.
11
* Then it is a simple matter to emit the output demanded by the SQL spec
12
* for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
14
* This node type is not used for UNION or UNION ALL, since those can be
15
* implemented more cheaply (there's no need for the junk attribute to
16
* identify the source relation).
19
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
20
* Portions Copyright (c) 1994, Regents of the University of California
24
* $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.15 2004-12-31 21:59:45 pgsql Exp $
26
*-------------------------------------------------------------------------
30
* ExecSetOp - filter input to generate INTERSECT/EXCEPT results
31
* ExecInitSetOp - initialize node and subnodes..
32
* ExecEndSetOp - shutdown node and subnodes
37
#include "access/heapam.h"
38
#include "executor/executor.h"
39
#include "executor/nodeSetOp.h"
42
/* ----------------------------------------------------------------
44
* ----------------------------------------------------------------
46
TupleTableSlot * /* return: a tuple or NULL */
47
ExecSetOp(SetOpState *node)
49
SetOp *plannode = (SetOp *) node->ps.plan;
50
TupleTableSlot *resultTupleSlot;
55
* get information from the node
57
outerPlan = outerPlanState(node);
58
resultTupleSlot = node->ps.ps_ResultTupleSlot;
59
tupDesc = ExecGetResultType(&node->ps);
62
* If the previously-returned tuple needs to be returned more than
63
* once, keep returning it.
65
if (node->numOutput > 0)
68
return resultTupleSlot;
71
/* Flag that we have no current tuple */
72
ExecClearTuple(resultTupleSlot);
75
* Absorb groups of duplicate tuples, counting them, and saving the
76
* first of each group as a possible return value. At the end of each
77
* group, decide whether to return anything.
79
* We assume that the tuples arrive in sorted order so we can detect
84
TupleTableSlot *inputTupleSlot;
88
* fetch a tuple from the outer subplan, unless we already did.
90
if (node->ps.ps_OuterTupleSlot == NULL &&
93
node->ps.ps_OuterTupleSlot =
94
ExecProcNode(outerPlan);
95
if (TupIsNull(node->ps.ps_OuterTupleSlot))
96
node->subplan_done = true;
98
inputTupleSlot = node->ps.ps_OuterTupleSlot;
100
if (TupIsNull(resultTupleSlot))
103
* First of group: save a copy in result slot, and reset
104
* duplicate-counters for new group.
106
if (node->subplan_done)
107
return NULL; /* no more tuples */
108
ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
111
true); /* free copied tuple at
117
else if (node->subplan_done)
120
* Reached end of input, so finish processing final group
127
* Else test if the new tuple and the previously saved tuple
130
if (execTuplesMatch(inputTupleSlot->val,
131
resultTupleSlot->val,
133
plannode->numCols, plannode->dupColIdx,
144
* We've reached the end of the group containing resultTuple.
145
* Decide how many copies (if any) to emit. This logic is
146
* straight from the SQL92 specification.
148
switch (plannode->cmd)
150
case SETOPCMD_INTERSECT:
151
if (node->numLeft > 0 && node->numRight > 0)
156
case SETOPCMD_INTERSECT_ALL:
158
(node->numLeft < node->numRight) ?
159
node->numLeft : node->numRight;
161
case SETOPCMD_EXCEPT:
162
if (node->numLeft > 0 && node->numRight == 0)
167
case SETOPCMD_EXCEPT_ALL:
169
(node->numLeft < node->numRight) ?
170
0 : (node->numLeft - node->numRight);
173
elog(ERROR, "unrecognized set op: %d",
174
(int) plannode->cmd);
177
/* Fall out of for-loop if we have tuples to emit */
178
if (node->numOutput > 0)
180
/* Else flag that we have no current tuple, and loop around */
181
ExecClearTuple(resultTupleSlot);
186
* Current tuple is member of same group as resultTuple. Count
187
* it in the appropriate counter.
192
flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
193
plannode->flagColIdx,
201
/* Set flag to fetch a new input tuple, and loop around */
202
node->ps.ps_OuterTupleSlot = NULL;
207
* If we fall out of loop, then we need to emit at least one copy of
210
Assert(node->numOutput > 0);
212
return resultTupleSlot;
215
/* ----------------------------------------------------------------
218
* This initializes the setop node state structures and
219
* the node's subplan.
220
* ----------------------------------------------------------------
223
ExecInitSetOp(SetOp *node, EState *estate)
225
SetOpState *setopstate;
228
* create state structure
230
setopstate = makeNode(SetOpState);
231
setopstate->ps.plan = (Plan *) node;
232
setopstate->ps.state = estate;
234
setopstate->ps.ps_OuterTupleSlot = NULL;
235
setopstate->subplan_done = false;
236
setopstate->numOutput = 0;
239
* Miscellaneous initialization
241
* SetOp nodes have no ExprContext initialization because they never call
242
* ExecQual or ExecProject. But they do need a per-tuple memory
243
* context anyway for calling execTuplesMatch.
245
setopstate->tempContext =
246
AllocSetContextCreate(CurrentMemoryContext,
248
ALLOCSET_DEFAULT_MINSIZE,
249
ALLOCSET_DEFAULT_INITSIZE,
250
ALLOCSET_DEFAULT_MAXSIZE);
252
#define SETOP_NSLOTS 1
255
* Tuple table initialization
257
ExecInitResultTupleSlot(estate, &setopstate->ps);
260
* then initialize outer plan
262
outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate);
265
* setop nodes do no projections, so initialize projection info for
266
* this node appropriately
268
ExecAssignResultTypeFromOuterPlan(&setopstate->ps);
269
setopstate->ps.ps_ProjInfo = NULL;
272
* Precompute fmgr lookup data for inner loop
274
setopstate->eqfunctions =
275
execTuplesMatchPrepare(ExecGetResultType(&setopstate->ps),
283
ExecCountSlotsSetOp(SetOp *node)
285
return ExecCountSlotsNode(outerPlan(node)) +
286
ExecCountSlotsNode(innerPlan(node)) +
290
/* ----------------------------------------------------------------
293
* This shuts down the subplan and frees resources allocated
295
* ----------------------------------------------------------------
298
ExecEndSetOp(SetOpState *node)
300
/* clean up tuple table */
301
ExecClearTuple(node->ps.ps_ResultTupleSlot);
302
node->ps.ps_OuterTupleSlot = NULL;
304
MemoryContextDelete(node->tempContext);
306
ExecEndNode(outerPlanState(node));
311
ExecReScanSetOp(SetOpState *node, ExprContext *exprCtxt)
313
ExecClearTuple(node->ps.ps_ResultTupleSlot);
314
node->ps.ps_OuterTupleSlot = NULL;
315
node->subplan_done = false;
319
* if chgParam of subnode is not null then plan will be re-scanned by
320
* first ExecProcNode.
322
if (((PlanState *) node)->lefttree->chgParam == NULL)
323
ExecReScan(((PlanState *) node)->lefttree, exprCtxt);