Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeGroup.c
4 : * Routines to handle group nodes (used for queries with GROUP BY clause).
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
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.
16 : *
17 : * IDENTIFICATION
18 : * src/backend/executor/nodeGroup.c
19 : *
20 : *-------------------------------------------------------------------------
21 : */
22 :
23 : #include "postgres.h"
24 :
25 : #include "executor/executor.h"
26 : #include "executor/nodeGroup.h"
27 : #include "miscadmin.h"
28 :
29 :
30 : /*
31 : * ExecGroup -
32 : *
33 : * Return one tuple for each group of matching input tuples.
34 : */
35 : static TupleTableSlot *
36 7970 : ExecGroup(PlanState *pstate)
37 : {
38 7970 : GroupState *node = castNode(GroupState, pstate);
39 : ExprContext *econtext;
40 : TupleTableSlot *firsttupleslot;
41 : TupleTableSlot *outerslot;
42 :
43 7970 : CHECK_FOR_INTERRUPTS();
44 :
45 : /*
46 : * get state info from node
47 : */
48 7970 : if (node->grp_done)
49 0 : return NULL;
50 7970 : econtext = node->ss.ps.ps_ExprContext;
51 :
52 : /*
53 : * The ScanTupleSlot holds the (copied) first tuple of each group.
54 : */
55 7970 : firsttupleslot = node->ss.ss_ScanTupleSlot;
56 :
57 : /*
58 : * We need not call ResetExprContext here because ExecQualAndReset() will
59 : * reset the per-tuple memory context once per input tuple.
60 : */
61 :
62 : /*
63 : * If first time through, acquire first input tuple and determine whether
64 : * to return it or not.
65 : */
66 7970 : if (TupIsNull(firsttupleslot))
67 : {
68 166 : outerslot = ExecProcNode(outerPlanState(node));
69 166 : if (TupIsNull(outerslot))
70 : {
71 : /* empty input, so return nothing */
72 38 : node->grp_done = true;
73 38 : return NULL;
74 : }
75 : /* Copy tuple into firsttupleslot */
76 128 : ExecCopySlot(firsttupleslot, outerslot);
77 :
78 : /*
79 : * Set it up as input for qual test and projection. The expressions
80 : * will access the input tuple as varno OUTER.
81 : */
82 128 : econtext->ecxt_outertuple = firsttupleslot;
83 :
84 : /*
85 : * Check the qual (HAVING clause); if the group does not match, ignore
86 : * it and fall into scan loop.
87 : */
88 128 : if (ExecQual(node->ss.ps.qual, econtext))
89 : {
90 : /*
91 : * Form and return a projection tuple using the first input tuple.
92 : */
93 128 : return ExecProject(node->ss.ps.ps_ProjInfo);
94 : }
95 : else
96 0 : InstrCountFiltered1(node, 1);
97 : }
98 :
99 : /*
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.
103 : */
104 0 : for (;;)
105 : {
106 : /*
107 : * Scan over all remaining tuples that belong to this group
108 : */
109 : for (;;)
110 : {
111 37694 : outerslot = ExecProcNode(outerPlanState(node));
112 37694 : if (TupIsNull(outerslot))
113 : {
114 : /* no more groups, so we're done */
115 128 : node->grp_done = true;
116 128 : return NULL;
117 : }
118 :
119 : /*
120 : * Compare with first tuple and see if this tuple is of the same
121 : * group. If so, ignore it and keep scanning.
122 : */
123 37566 : econtext->ecxt_innertuple = firsttupleslot;
124 37566 : econtext->ecxt_outertuple = outerslot;
125 37566 : if (!ExecQualAndReset(node->eqfunction, econtext))
126 7676 : break;
127 : }
128 :
129 : /*
130 : * We have the first tuple of the next input group. See if we want to
131 : * return it.
132 : */
133 : /* Copy tuple, set up as input for qual test and projection */
134 7676 : ExecCopySlot(firsttupleslot, outerslot);
135 7676 : econtext->ecxt_outertuple = firsttupleslot;
136 :
137 : /*
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.
140 : */
141 7676 : if (ExecQual(node->ss.ps.qual, econtext))
142 : {
143 : /*
144 : * Form and return a projection tuple using the first input tuple.
145 : */
146 7676 : return ExecProject(node->ss.ps.ps_ProjInfo);
147 : }
148 : else
149 0 : InstrCountFiltered1(node, 1);
150 : }
151 : }
152 :
153 : /* -----------------
154 : * ExecInitGroup
155 : *
156 : * Creates the run-time information for the group node produced by the
157 : * planner and initializes its outer subtree
158 : * -----------------
159 : */
160 : GroupState *
161 240 : ExecInitGroup(Group *node, EState *estate, int eflags)
162 : {
163 : GroupState *grpstate;
164 : const TupleTableSlotOps *tts_ops;
165 :
166 : /* check for unsupported flags */
167 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
168 :
169 : /*
170 : * create state structure
171 : */
172 240 : grpstate = makeNode(GroupState);
173 240 : grpstate->ss.ps.plan = (Plan *) node;
174 240 : grpstate->ss.ps.state = estate;
175 240 : grpstate->ss.ps.ExecProcNode = ExecGroup;
176 240 : grpstate->grp_done = false;
177 :
178 : /*
179 : * create expression context
180 : */
181 240 : ExecAssignExprContext(estate, &grpstate->ss.ps);
182 :
183 : /*
184 : * initialize child nodes
185 : */
186 240 : outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
187 :
188 : /*
189 : * Initialize scan slot and type.
190 : */
191 240 : tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
192 240 : ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
193 :
194 : /*
195 : * Initialize result slot, type and projection.
196 : */
197 240 : ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
198 240 : ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
199 :
200 : /*
201 : * initialize child expressions
202 : */
203 240 : grpstate->ss.ps.qual =
204 240 : ExecInitQual(node->plan.qual, (PlanState *) grpstate);
205 :
206 : /*
207 : * Precompute fmgr lookup data for inner loop
208 : */
209 240 : grpstate->eqfunction =
210 240 : execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
211 : node->numCols,
212 240 : node->grpColIdx,
213 240 : node->grpOperators,
214 240 : node->grpCollations,
215 : &grpstate->ss.ps);
216 :
217 240 : return grpstate;
218 : }
219 :
220 : /* ------------------------
221 : * ExecEndGroup(node)
222 : *
223 : * -----------------------
224 : */
225 : void
226 240 : ExecEndGroup(GroupState *node)
227 : {
228 : PlanState *outerPlan;
229 :
230 240 : outerPlan = outerPlanState(node);
231 240 : ExecEndNode(outerPlan);
232 240 : }
233 :
234 : void
235 24 : ExecReScanGroup(GroupState *node)
236 : {
237 24 : PlanState *outerPlan = outerPlanState(node);
238 :
239 24 : node->grp_done = false;
240 : /* must clear first tuple */
241 24 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
242 :
243 : /*
244 : * if chgParam of subnode is not null then plan will be re-scanned by
245 : * first ExecProcNode.
246 : */
247 24 : if (outerPlan->chgParam == NULL)
248 24 : ExecReScan(outerPlan);
249 24 : }
|