Fix a compiler warning in initStringInfo().
[pgsql.git] / src / backend / executor / nodeGroup.c
blob05fdd96f83584032501d947f84d1eada7ec652c7
1 /*-------------------------------------------------------------------------
3 * nodeGroup.c
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * DESCRIPTION
11 * The Group node is designed for handling queries with a GROUP BY clause.
12 * Its outer plan must deliver tuples that are sorted in the order
13 * specified by the grouping columns (ie. tuples from the same group are
14 * consecutive). That way, we just have to compare adjacent tuples to
15 * locate group boundaries.
17 * IDENTIFICATION
18 * src/backend/executor/nodeGroup.c
20 *-------------------------------------------------------------------------
23 #include "postgres.h"
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
27 #include "miscadmin.h"
31 * ExecGroup -
33 * Return one tuple for each group of matching input tuples.
35 static TupleTableSlot *
36 ExecGroup(PlanState *pstate)
38 GroupState *node = castNode(GroupState, pstate);
39 ExprContext *econtext;
40 TupleTableSlot *firsttupleslot;
41 TupleTableSlot *outerslot;
43 CHECK_FOR_INTERRUPTS();
46 * get state info from node
48 if (node->grp_done)
49 return NULL;
50 econtext = node->ss.ps.ps_ExprContext;
53 * The ScanTupleSlot holds the (copied) first tuple of each group.
55 firsttupleslot = node->ss.ss_ScanTupleSlot;
58 * We need not call ResetExprContext here because ExecQualAndReset() will
59 * reset the per-tuple memory context once per input tuple.
63 * If first time through, acquire first input tuple and determine whether
64 * to return it or not.
66 if (TupIsNull(firsttupleslot))
68 outerslot = ExecProcNode(outerPlanState(node));
69 if (TupIsNull(outerslot))
71 /* empty input, so return nothing */
72 node->grp_done = true;
73 return NULL;
75 /* Copy tuple into firsttupleslot */
76 ExecCopySlot(firsttupleslot, outerslot);
79 * Set it up as input for qual test and projection. The expressions
80 * will access the input tuple as varno OUTER.
82 econtext->ecxt_outertuple = firsttupleslot;
85 * Check the qual (HAVING clause); if the group does not match, ignore
86 * it and fall into scan loop.
88 if (ExecQual(node->ss.ps.qual, econtext))
91 * Form and return a projection tuple using the first input tuple.
93 return ExecProject(node->ss.ps.ps_ProjInfo);
95 else
96 InstrCountFiltered1(node, 1);
100 * This loop iterates once per input tuple group. At the head of the
101 * loop, we have finished processing the first tuple of the group and now
102 * need to scan over all the other group members.
104 for (;;)
107 * Scan over all remaining tuples that belong to this group
109 for (;;)
111 outerslot = ExecProcNode(outerPlanState(node));
112 if (TupIsNull(outerslot))
114 /* no more groups, so we're done */
115 node->grp_done = true;
116 return NULL;
120 * Compare with first tuple and see if this tuple is of the same
121 * group. If so, ignore it and keep scanning.
123 econtext->ecxt_innertuple = firsttupleslot;
124 econtext->ecxt_outertuple = outerslot;
125 if (!ExecQualAndReset(node->eqfunction, econtext))
126 break;
130 * We have the first tuple of the next input group. See if we want to
131 * return it.
133 /* Copy tuple, set up as input for qual test and projection */
134 ExecCopySlot(firsttupleslot, outerslot);
135 econtext->ecxt_outertuple = firsttupleslot;
138 * Check the qual (HAVING clause); if the group does not match, ignore
139 * it and loop back to scan the rest of the group.
141 if (ExecQual(node->ss.ps.qual, econtext))
144 * Form and return a projection tuple using the first input tuple.
146 return ExecProject(node->ss.ps.ps_ProjInfo);
148 else
149 InstrCountFiltered1(node, 1);
153 /* -----------------
154 * ExecInitGroup
156 * Creates the run-time information for the group node produced by the
157 * planner and initializes its outer subtree
158 * -----------------
160 GroupState *
161 ExecInitGroup(Group *node, EState *estate, int eflags)
163 GroupState *grpstate;
164 const TupleTableSlotOps *tts_ops;
166 /* check for unsupported flags */
167 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
170 * create state structure
172 grpstate = makeNode(GroupState);
173 grpstate->ss.ps.plan = (Plan *) node;
174 grpstate->ss.ps.state = estate;
175 grpstate->ss.ps.ExecProcNode = ExecGroup;
176 grpstate->grp_done = false;
179 * create expression context
181 ExecAssignExprContext(estate, &grpstate->ss.ps);
184 * initialize child nodes
186 outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
189 * Initialize scan slot and type.
191 tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
192 ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
195 * Initialize result slot, type and projection.
197 ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
198 ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
201 * initialize child expressions
203 grpstate->ss.ps.qual =
204 ExecInitQual(node->plan.qual, (PlanState *) grpstate);
207 * Precompute fmgr lookup data for inner loop
209 grpstate->eqfunction =
210 execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
211 node->numCols,
212 node->grpColIdx,
213 node->grpOperators,
214 node->grpCollations,
215 &grpstate->ss.ps);
217 return grpstate;
220 /* ------------------------
221 * ExecEndGroup(node)
223 * -----------------------
225 void
226 ExecEndGroup(GroupState *node)
228 PlanState *outerPlan;
230 outerPlan = outerPlanState(node);
231 ExecEndNode(outerPlan);
234 void
235 ExecReScanGroup(GroupState *node)
237 PlanState *outerPlan = outerPlanState(node);
239 node->grp_done = false;
240 /* must clear first tuple */
241 ExecClearTuple(node->ss.ss_ScanTupleSlot);
244 * if chgParam of subnode is not null then plan will be re-scanned by
245 * first ExecProcNode.
247 if (outerPlan->chgParam == NULL)
248 ExecReScan(outerPlan);