aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeSetOp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeSetOp.c')
-rw-r--r--src/backend/executor/nodeSetOp.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
new file mode 100644
index 00000000000..8c285525051
--- /dev/null
+++ b/src/backend/executor/nodeSetOp.c
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSetOp.c
+ * Routines to handle INTERSECT and EXCEPT selection
+ *
+ * The input of a SetOp node consists of tuples from two relations,
+ * which have been combined into one dataset and sorted on all the nonjunk
+ * attributes. In addition there is a junk attribute that shows which
+ * relation each tuple came from. The SetOp node scans each group of
+ * identical tuples to determine how many came from each input relation.
+ * Then it is a simple matter to emit the output demanded by the SQL spec
+ * for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
+ *
+ * This node type is not used for UNION or UNION ALL, since those can be
+ * implemented more cheaply (there's no need for the junk attribute to
+ * identify the source relation).
+ *
+ *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.1 2000/10/05 19:11:26 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecSetOp - filter input to generate INTERSECT/EXCEPT results
+ * ExecInitSetOp - initialize node and subnodes..
+ * ExecEndSetOp - shutdown node and subnodes
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "executor/executor.h"
+#include "executor/nodeGroup.h"
+#include "executor/nodeSetOp.h"
+
+/* ----------------------------------------------------------------
+ * ExecSetOp
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot * /* return: a tuple or NULL */
+ExecSetOp(SetOp *node)
+{
+ SetOpState *setopstate;
+ TupleTableSlot *resultTupleSlot;
+ Plan *outerPlan;
+ TupleDesc tupDesc;
+
+ /* ----------------
+ * get information from the node
+ * ----------------
+ */
+ setopstate = node->setopstate;
+ outerPlan = outerPlan((Plan *) node);
+ resultTupleSlot = setopstate->cstate.cs_ResultTupleSlot;
+ tupDesc = ExecGetResultType(&setopstate->cstate);
+
+ /* ----------------
+ * If the previously-returned tuple needs to be returned more than
+ * once, keep returning it.
+ * ----------------
+ */
+ if (setopstate->numOutput > 0)
+ {
+ setopstate->numOutput--;
+ return resultTupleSlot;
+ }
+
+ /* Flag that we have no current tuple */
+ ExecClearTuple(resultTupleSlot);
+
+ /* ----------------
+ * Absorb groups of duplicate tuples, counting them, and
+ * saving the first of each group as a possible return value.
+ * At the end of each group, decide whether to return anything.
+ *
+ * We assume that the tuples arrive in sorted order
+ * so we can detect duplicates easily.
+ * ----------------
+ */
+ for (;;)
+ {
+ TupleTableSlot *inputTupleSlot;
+ bool endOfGroup;
+
+ /* ----------------
+ * fetch a tuple from the outer subplan, unless we already did.
+ * ----------------
+ */
+ if (setopstate->cstate.cs_OuterTupleSlot == NULL &&
+ ! setopstate->subplan_done)
+ {
+ setopstate->cstate.cs_OuterTupleSlot =
+ ExecProcNode(outerPlan, (Plan *) node);
+ if (TupIsNull(setopstate->cstate.cs_OuterTupleSlot))
+ setopstate->subplan_done = true;
+ }
+ inputTupleSlot = setopstate->cstate.cs_OuterTupleSlot;
+
+ if (TupIsNull(resultTupleSlot))
+ {
+ /*
+ * First of group: save a copy in result slot, and reset
+ * duplicate-counters for new group.
+ */
+ if (setopstate->subplan_done)
+ return NULL; /* no more tuples */
+ ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
+ resultTupleSlot,
+ InvalidBuffer,
+ true); /* free copied tuple at ExecClearTuple */
+ setopstate->numLeft = 0;
+ setopstate->numRight = 0;
+ endOfGroup = false;
+ }
+ else if (setopstate->subplan_done)
+ {
+ /*
+ * Reached end of input, so finish processing final group
+ */
+ endOfGroup = true;
+ }
+ else
+ {
+ /*
+ * Else test if the new tuple and the previously saved tuple match.
+ */
+ if (execTuplesMatch(inputTupleSlot->val,
+ resultTupleSlot->val,
+ tupDesc,
+ node->numCols, node->dupColIdx,
+ setopstate->eqfunctions,
+ setopstate->tempContext))
+ endOfGroup = false;
+ else
+ endOfGroup = true;
+ }
+
+ if (endOfGroup)
+ {
+ /*
+ * We've reached the end of the group containing resultTuple.
+ * Decide how many copies (if any) to emit. This logic is
+ * straight from the SQL92 specification.
+ */
+ switch (node->cmd)
+ {
+ case SETOPCMD_INTERSECT:
+ if (setopstate->numLeft > 0 && setopstate->numRight > 0)
+ setopstate->numOutput = 1;
+ else
+ setopstate->numOutput = 0;
+ break;
+ case SETOPCMD_INTERSECT_ALL:
+ setopstate->numOutput =
+ (setopstate->numLeft < setopstate->numRight) ?
+ setopstate->numLeft : setopstate->numRight;
+ break;
+ case SETOPCMD_EXCEPT:
+ if (setopstate->numLeft > 0 && setopstate->numRight == 0)
+ setopstate->numOutput = 1;
+ else
+ setopstate->numOutput = 0;
+ break;
+ case SETOPCMD_EXCEPT_ALL:
+ setopstate->numOutput =
+ (setopstate->numLeft < setopstate->numRight) ?
+ 0 : (setopstate->numLeft - setopstate->numRight);
+ break;
+ default:
+ elog(ERROR, "ExecSetOp: bogus command code %d",
+ (int) node->cmd);
+ break;
+ }
+ /* Fall out of for-loop if we have tuples to emit */
+ if (setopstate->numOutput > 0)
+ break;
+ /* Else flag that we have no current tuple, and loop around */
+ ExecClearTuple(resultTupleSlot);
+ }
+ else
+ {
+ /*
+ * Current tuple is member of same group as resultTuple.
+ * Count it in the appropriate counter.
+ */
+ int flag;
+ bool isNull;
+
+ flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
+ node->flagColIdx,
+ tupDesc,
+ &isNull));
+ Assert(!isNull);
+ if (flag)
+ setopstate->numRight++;
+ else
+ setopstate->numLeft++;
+ /* Set flag to fetch a new input tuple, and loop around */
+ setopstate->cstate.cs_OuterTupleSlot = NULL;
+ }
+ }
+
+ /*
+ * If we fall out of loop, then we need to emit at least one copy
+ * of resultTuple.
+ */
+ Assert(setopstate->numOutput > 0);
+ setopstate->numOutput--;
+ return resultTupleSlot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitSetOp
+ *
+ * This initializes the setop node state structures and
+ * the node's subplan.
+ * ----------------------------------------------------------------
+ */
+bool /* return: initialization status */
+ExecInitSetOp(SetOp *node, EState *estate, Plan *parent)
+{
+ SetOpState *setopstate;
+ Plan *outerPlan;
+
+ /* ----------------
+ * assign execution state to node
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create new SetOpState for node
+ * ----------------
+ */
+ setopstate = makeNode(SetOpState);
+ node->setopstate = setopstate;
+ setopstate->cstate.cs_OuterTupleSlot = NULL;
+ setopstate->subplan_done = false;
+ setopstate->numOutput = 0;
+
+ /* ----------------
+ * Miscellaneous initialization
+ *
+ * SetOp nodes have no ExprContext initialization because
+ * they never call ExecQual or ExecProject. But they do need a
+ * per-tuple memory context anyway for calling execTuplesMatch.
+ * ----------------
+ */
+ setopstate->tempContext =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "SetOp",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+#define SETOP_NSLOTS 1
+ /* ------------
+ * Tuple table initialization
+ * ------------
+ */
+ ExecInitResultTupleSlot(estate, &setopstate->cstate);
+
+ /* ----------------
+ * then initialize outer plan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecInitNode(outerPlan, estate, (Plan *) node);
+
+ /* ----------------
+ * setop nodes do no projections, so initialize
+ * projection info for this node appropriately
+ * ----------------
+ */
+ ExecAssignResultTypeFromOuterPlan((Plan *) node, &setopstate->cstate);
+ setopstate->cstate.cs_ProjInfo = NULL;
+
+ /*
+ * Precompute fmgr lookup data for inner loop
+ */
+ setopstate->eqfunctions =
+ execTuplesMatchPrepare(ExecGetResultType(&setopstate->cstate),
+ node->numCols,
+ node->dupColIdx);
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsSetOp(SetOp *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ SETOP_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndSetOp
+ *
+ * This shuts down the subplan and frees resources allocated
+ * to this node.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSetOp(SetOp *node)
+{
+ SetOpState *setopstate = node->setopstate;
+
+ ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
+
+ MemoryContextDelete(setopstate->tempContext);
+
+ /* clean up tuple table */
+ ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
+ setopstate->cstate.cs_OuterTupleSlot = NULL;
+}
+
+
+void
+ExecReScanSetOp(SetOp *node, ExprContext *exprCtxt, Plan *parent)
+{
+ SetOpState *setopstate = node->setopstate;
+
+ ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
+ setopstate->cstate.cs_OuterTupleSlot = NULL;
+ setopstate->subplan_done = false;
+ setopstate->numOutput = 0;
+
+ /*
+ * if chgParam of subnode is not null then plan will be re-scanned by
+ * first ExecProcNode.
+ */
+ if (((Plan *) node)->lefttree->chgParam == NULL)
+ ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+}