1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2022, 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"
28 #include "utils/memutils.h"
34 * Return one tuple for each group of matching input tuples.
36 static TupleTableSlot
*
37 ExecGroup(PlanState
*pstate
)
39 GroupState
*node
= castNode(GroupState
, pstate
);
40 ExprContext
*econtext
;
41 TupleTableSlot
*firsttupleslot
;
42 TupleTableSlot
*outerslot
;
44 CHECK_FOR_INTERRUPTS();
47 * get state info from node
51 econtext
= node
->ss
.ps
.ps_ExprContext
;
54 * The ScanTupleSlot holds the (copied) first tuple of each group.
56 firsttupleslot
= node
->ss
.ss_ScanTupleSlot
;
59 * We need not call ResetExprContext here because ExecQualAndReset() will
60 * reset the per-tuple memory context once per input tuple.
64 * If first time through, acquire first input tuple and determine whether
65 * to return it or not.
67 if (TupIsNull(firsttupleslot
))
69 outerslot
= ExecProcNode(outerPlanState(node
));
70 if (TupIsNull(outerslot
))
72 /* empty input, so return nothing */
73 node
->grp_done
= true;
76 /* Copy tuple into firsttupleslot */
77 ExecCopySlot(firsttupleslot
, outerslot
);
80 * Set it up as input for qual test and projection. The expressions
81 * will access the input tuple as varno OUTER.
83 econtext
->ecxt_outertuple
= firsttupleslot
;
86 * Check the qual (HAVING clause); if the group does not match, ignore
87 * it and fall into scan loop.
89 if (ExecQual(node
->ss
.ps
.qual
, econtext
))
92 * Form and return a projection tuple using the first input tuple.
94 return ExecProject(node
->ss
.ps
.ps_ProjInfo
);
97 InstrCountFiltered1(node
, 1);
101 * This loop iterates once per input tuple group. At the head of the
102 * loop, we have finished processing the first tuple of the group and now
103 * need to scan over all the other group members.
108 * Scan over all remaining tuples that belong to this group
112 outerslot
= ExecProcNode(outerPlanState(node
));
113 if (TupIsNull(outerslot
))
115 /* no more groups, so we're done */
116 node
->grp_done
= true;
121 * Compare with first tuple and see if this tuple is of the same
122 * group. If so, ignore it and keep scanning.
124 econtext
->ecxt_innertuple
= firsttupleslot
;
125 econtext
->ecxt_outertuple
= outerslot
;
126 if (!ExecQualAndReset(node
->eqfunction
, econtext
))
131 * We have the first tuple of the next input group. See if we want to
134 /* Copy tuple, set up as input for qual test and projection */
135 ExecCopySlot(firsttupleslot
, outerslot
);
136 econtext
->ecxt_outertuple
= firsttupleslot
;
139 * Check the qual (HAVING clause); if the group does not match, ignore
140 * it and loop back to scan the rest of the group.
142 if (ExecQual(node
->ss
.ps
.qual
, econtext
))
145 * Form and return a projection tuple using the first input tuple.
147 return ExecProject(node
->ss
.ps
.ps_ProjInfo
);
150 InstrCountFiltered1(node
, 1);
157 * Creates the run-time information for the group node produced by the
158 * planner and initializes its outer subtree
162 ExecInitGroup(Group
*node
, EState
*estate
, int eflags
)
164 GroupState
*grpstate
;
165 const TupleTableSlotOps
*tts_ops
;
167 /* check for unsupported flags */
168 Assert(!(eflags
& (EXEC_FLAG_BACKWARD
| EXEC_FLAG_MARK
)));
171 * create state structure
173 grpstate
= makeNode(GroupState
);
174 grpstate
->ss
.ps
.plan
= (Plan
*) node
;
175 grpstate
->ss
.ps
.state
= estate
;
176 grpstate
->ss
.ps
.ExecProcNode
= ExecGroup
;
177 grpstate
->grp_done
= false;
180 * create expression context
182 ExecAssignExprContext(estate
, &grpstate
->ss
.ps
);
185 * initialize child nodes
187 outerPlanState(grpstate
) = ExecInitNode(outerPlan(node
), estate
, eflags
);
190 * Initialize scan slot and type.
192 tts_ops
= ExecGetResultSlotOps(outerPlanState(&grpstate
->ss
), NULL
);
193 ExecCreateScanSlotFromOuterPlan(estate
, &grpstate
->ss
, tts_ops
);
196 * Initialize result slot, type and projection.
198 ExecInitResultTupleSlotTL(&grpstate
->ss
.ps
, &TTSOpsVirtual
);
199 ExecAssignProjectionInfo(&grpstate
->ss
.ps
, NULL
);
202 * initialize child expressions
204 grpstate
->ss
.ps
.qual
=
205 ExecInitQual(node
->plan
.qual
, (PlanState
*) grpstate
);
208 * Precompute fmgr lookup data for inner loop
210 grpstate
->eqfunction
=
211 execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate
)),
221 /* ------------------------
224 * -----------------------
227 ExecEndGroup(GroupState
*node
)
229 PlanState
*outerPlan
;
231 ExecFreeExprContext(&node
->ss
.ps
);
233 /* clean up tuple table */
234 ExecClearTuple(node
->ss
.ss_ScanTupleSlot
);
236 outerPlan
= outerPlanState(node
);
237 ExecEndNode(outerPlan
);
241 ExecReScanGroup(GroupState
*node
)
243 PlanState
*outerPlan
= outerPlanState(node
);
245 node
->grp_done
= false;
246 /* must clear first tuple */
247 ExecClearTuple(node
->ss
.ss_ScanTupleSlot
);
250 * if chgParam of subnode is not null then plan will be re-scanned by
251 * first ExecProcNode.
253 if (outerPlan
->chgParam
== NULL
)
254 ExecReScan(outerPlan
);