diff options
Diffstat (limited to 'src/backend/executor/nodeSetOp.c')
-rw-r--r-- | src/backend/executor/nodeSetOp.c | 341 |
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); +} |