/*------------------------------------------------------------------------- * * nodeGroup.c * Routines to handle group nodes (used for queries with GROUP BY clause). * * Portions Copyright (c) 1996-2020, 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); }