1 /*-------------------------------------------------------------------------
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
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.
18 * src/backend/executor/nodeGroup.c
20 *-------------------------------------------------------------------------
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
27 #include "miscadmin.h"
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
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;
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
);
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.
107 * Scan over all remaining tuples that belong to this group
111 outerslot
= ExecProcNode(outerPlanState(node
));
112 if (TupIsNull(outerslot
))
114 /* no more groups, so we're done */
115 node
->grp_done
= true;
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
))
130 * We have the first tuple of the next input group. See if we want to
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
);
149 InstrCountFiltered1(node
, 1);
156 * Creates the run-time information for the group node produced by the
157 * planner and initializes its outer subtree
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
)),
220 /* ------------------------
223 * -----------------------
226 ExecEndGroup(GroupState
*node
)
228 PlanState
*outerPlan
;
230 outerPlan
= outerPlanState(node
);
231 ExecEndNode(outerPlan
);
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
);