diff options
Diffstat (limited to 'src/backend/executor/nodeGroup.c')
-rw-r--r-- | src/backend/executor/nodeGroup.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c new file mode 100644 index 0000000..1721b2a --- /dev/null +++ b/src/backend/executor/nodeGroup.c @@ -0,0 +1,255 @@ +/*------------------------------------------------------------------------- + * + * nodeGroup.c + * Routines to handle group nodes (used for queries with GROUP BY clause). + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * DESCRIPTION + * The Group node is designed for handling queries with a GROUP BY clause. + * Its outer plan must deliver tuples that are sorted in the order + * specified by the grouping columns (ie. tuples from the same group are + * consecutive). That way, we just have to compare adjacent tuples to + * locate group boundaries. + * + * IDENTIFICATION + * src/backend/executor/nodeGroup.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "executor/executor.h" +#include "executor/nodeGroup.h" +#include "miscadmin.h" +#include "utils/memutils.h" + + +/* + * ExecGroup - + * + * Return one tuple for each group of matching input tuples. + */ +static TupleTableSlot * +ExecGroup(PlanState *pstate) +{ + GroupState *node = castNode(GroupState, pstate); + ExprContext *econtext; + TupleTableSlot *firsttupleslot; + TupleTableSlot *outerslot; + + CHECK_FOR_INTERRUPTS(); + + /* + * get state info from node + */ + if (node->grp_done) + return NULL; + econtext = node->ss.ps.ps_ExprContext; + + /* + * The ScanTupleSlot holds the (copied) first tuple of each group. + */ + firsttupleslot = node->ss.ss_ScanTupleSlot; + + /* + * We need not call ResetExprContext here because ExecQualAndReset() will + * reset the per-tuple memory context once per input tuple. + */ + + /* + * If first time through, acquire first input tuple and determine whether + * to return it or not. + */ + if (TupIsNull(firsttupleslot)) + { + outerslot = ExecProcNode(outerPlanState(node)); + if (TupIsNull(outerslot)) + { + /* empty input, so return nothing */ + node->grp_done = true; + return NULL; + } + /* Copy tuple into firsttupleslot */ + ExecCopySlot(firsttupleslot, outerslot); + + /* + * Set it up as input for qual test and projection. The expressions + * will access the input tuple as varno OUTER. + */ + econtext->ecxt_outertuple = firsttupleslot; + + /* + * Check the qual (HAVING clause); if the group does not match, ignore + * it and fall into scan loop. + */ + if (ExecQual(node->ss.ps.qual, econtext)) + { + /* + * Form and return a projection tuple using the first input tuple. + */ + return ExecProject(node->ss.ps.ps_ProjInfo); + } + else + InstrCountFiltered1(node, 1); + } + + /* + * This loop iterates once per input tuple group. At the head of the + * loop, we have finished processing the first tuple of the group and now + * need to scan over all the other group members. + */ + for (;;) + { + /* + * Scan over all remaining tuples that belong to this group + */ + for (;;) + { + outerslot = ExecProcNode(outerPlanState(node)); + if (TupIsNull(outerslot)) + { + /* no more groups, so we're done */ + node->grp_done = true; + return NULL; + } + + /* + * Compare with first tuple and see if this tuple is of the same + * group. If so, ignore it and keep scanning. + */ + econtext->ecxt_innertuple = firsttupleslot; + econtext->ecxt_outertuple = outerslot; + if (!ExecQualAndReset(node->eqfunction, econtext)) + break; + } + + /* + * We have the first tuple of the next input group. See if we want to + * return it. + */ + /* Copy tuple, set up as input for qual test and projection */ + ExecCopySlot(firsttupleslot, outerslot); + econtext->ecxt_outertuple = firsttupleslot; + + /* + * Check the qual (HAVING clause); if the group does not match, ignore + * it and loop back to scan the rest of the group. + */ + if (ExecQual(node->ss.ps.qual, econtext)) + { + /* + * Form and return a projection tuple using the first input tuple. + */ + return ExecProject(node->ss.ps.ps_ProjInfo); + } + else + InstrCountFiltered1(node, 1); + } +} + +/* ----------------- + * ExecInitGroup + * + * Creates the run-time information for the group node produced by the + * planner and initializes its outer subtree + * ----------------- + */ +GroupState * +ExecInitGroup(Group *node, EState *estate, int eflags) +{ + GroupState *grpstate; + const TupleTableSlotOps *tts_ops; + + /* check for unsupported flags */ + Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); + + /* + * create state structure + */ + grpstate = makeNode(GroupState); + grpstate->ss.ps.plan = (Plan *) node; + grpstate->ss.ps.state = estate; + grpstate->ss.ps.ExecProcNode = ExecGroup; + grpstate->grp_done = false; + + /* + * create expression context + */ + ExecAssignExprContext(estate, &grpstate->ss.ps); + + /* + * initialize child nodes + */ + outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags); + + /* + * Initialize scan slot and type. + */ + tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL); + ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops); + + /* + * Initialize result slot, type and projection. + */ + ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual); + ExecAssignProjectionInfo(&grpstate->ss.ps, NULL); + + /* + * initialize child expressions + */ + grpstate->ss.ps.qual = + ExecInitQual(node->plan.qual, (PlanState *) grpstate); + + /* + * Precompute fmgr lookup data for inner loop + */ + grpstate->eqfunction = + execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)), + node->numCols, + node->grpColIdx, + node->grpOperators, + node->grpCollations, + &grpstate->ss.ps); + + return grpstate; +} + +/* ------------------------ + * ExecEndGroup(node) + * + * ----------------------- + */ +void +ExecEndGroup(GroupState *node) +{ + PlanState *outerPlan; + + ExecFreeExprContext(&node->ss.ps); + + /* clean up tuple table */ + ExecClearTuple(node->ss.ss_ScanTupleSlot); + + outerPlan = outerPlanState(node); + ExecEndNode(outerPlan); +} + +void +ExecReScanGroup(GroupState *node) +{ + PlanState *outerPlan = outerPlanState(node); + + node->grp_done = false; + /* must clear first tuple */ + ExecClearTuple(node->ss.ss_ScanTupleSlot); + + /* + * if chgParam of subnode is not null then plan will be re-scanned by + * first ExecProcNode. + */ + if (outerPlan->chgParam == NULL) + ExecReScan(outerPlan); +} |