summaryrefslogtreecommitdiffstats
path: root/src/backend/executor/nodeGroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeGroup.c')
-rw-r--r--src/backend/executor/nodeGroup.c255
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);
+}