Age Owner Branch data TLA 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-2026, 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/instrument.h"
27 : : #include "executor/nodeGroup.h"
28 : : #include "miscadmin.h"
29 : :
30 : :
31 : : /*
32 : : * ExecGroup -
33 : : *
34 : : * Return one tuple for each group of matching input tuples.
35 : : */
36 : : static TupleTableSlot *
3214 andres@anarazel.de 37 :CBC 4977 : ExecGroup(PlanState *pstate)
38 : : {
39 : 4977 : GroupState *node = castNode(GroupState, pstate);
40 : : ExprContext *econtext;
41 : : TupleTableSlot *firsttupleslot;
42 : : TupleTableSlot *outerslot;
43 : :
3206 44 [ - + ]: 4977 : CHECK_FOR_INTERRUPTS();
45 : :
46 : : /*
47 : : * get state info from node
48 : : */
8552 tgl@sss.pgh.pa.us 49 [ - + ]: 4977 : if (node->grp_done)
10467 bruce@momjian.us 50 :UBC 0 : return NULL;
8552 tgl@sss.pgh.pa.us 51 :CBC 4977 : econtext = node->ss.ps.ps_ExprContext;
52 : :
53 : : /*
54 : : * The ScanTupleSlot holds the (copied) first tuple of each group.
55 : : */
7720 56 : 4977 : firsttupleslot = node->ss.ss_ScanTupleSlot;
57 : :
58 : : /*
59 : : * We need not call ResetExprContext here because ExecQualAndReset() will
60 : : * reset the per-tuple memory context once per input tuple.
61 : : */
62 : :
63 : : /*
64 : : * If first time through, acquire first input tuple and determine whether
65 : : * to return it or not.
66 : : */
67 [ + - + + ]: 4977 : if (TupIsNull(firsttupleslot))
68 : : {
8552 69 : 112 : outerslot = ExecProcNode(outerPlanState(node));
10021 vadim4o@yahoo.com 70 [ + + + + ]: 112 : if (TupIsNull(outerslot))
71 : : {
72 : : /* empty input, so return nothing */
3184 peter_e@gmx.net 73 : 25 : node->grp_done = true;
10467 bruce@momjian.us 74 : 25 : return NULL;
75 : : }
76 : : /* Copy tuple into firsttupleslot */
7720 tgl@sss.pgh.pa.us 77 : 87 : ExecCopySlot(firsttupleslot, outerslot);
78 : :
79 : : /*
80 : : * Set it up as input for qual test and projection. The expressions
81 : : * will access the input tuple as varno OUTER.
82 : : */
7012 83 : 87 : econtext->ecxt_outertuple = firsttupleslot;
84 : :
85 : : /*
86 : : * Check the qual (HAVING clause); if the group does not match, ignore
87 : : * it and fall into scan loop.
88 : : */
3339 andres@anarazel.de 89 [ + - ]: 87 : if (ExecQual(node->ss.ps.qual, econtext))
90 : : {
91 : : /*
92 : : * Form and return a projection tuple using the first input tuple.
93 : : */
3393 94 : 87 : return ExecProject(node->ss.ps.ps_ProjInfo);
95 : : }
96 : : else
5339 tgl@sss.pgh.pa.us 97 [ # # ]:UBC 0 : InstrCountFiltered1(node, 1);
98 : : }
99 : :
100 : : /*
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.
104 : : */
10467 bruce@momjian.us 105 : 0 : for (;;)
106 : : {
107 : : /*
108 : : * Scan over all remaining tuples that belong to this group
109 : : */
110 : : for (;;)
111 : : {
7726 tgl@sss.pgh.pa.us 112 :CBC 43975 : outerslot = ExecProcNode(outerPlanState(node));
113 [ + + + + ]: 24420 : if (TupIsNull(outerslot))
114 : : {
115 : : /* no more groups, so we're done */
3184 peter_e@gmx.net 116 : 87 : node->grp_done = true;
7726 tgl@sss.pgh.pa.us 117 : 87 : return NULL;
118 : : }
119 : :
120 : : /*
121 : : * Compare with first tuple and see if this tuple is of the same
122 : : * group. If so, ignore it and keep scanning.
123 : : */
3001 andres@anarazel.de 124 : 24333 : econtext->ecxt_innertuple = firsttupleslot;
125 : 24333 : econtext->ecxt_outertuple = outerslot;
126 [ + + ]: 24333 : if (!ExecQualAndReset(node->eqfunction, econtext))
7726 tgl@sss.pgh.pa.us 127 : 4778 : break;
128 : : }
129 : :
130 : : /*
131 : : * We have the first tuple of the next input group. See if we want to
132 : : * return it.
133 : : */
134 : : /* Copy tuple, set up as input for qual test and projection */
7720 135 : 4778 : ExecCopySlot(firsttupleslot, outerslot);
7012 136 : 4778 : econtext->ecxt_outertuple = firsttupleslot;
137 : :
138 : : /*
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.
141 : : */
3339 andres@anarazel.de 142 [ + - ]: 4778 : if (ExecQual(node->ss.ps.qual, econtext))
143 : : {
144 : : /*
145 : : * Form and return a projection tuple using the first input tuple.
146 : : */
3393 147 : 4778 : return ExecProject(node->ss.ps.ps_ProjInfo);
148 : : }
149 : : else
5339 tgl@sss.pgh.pa.us 150 [ # # ]:UBC 0 : InstrCountFiltered1(node, 1);
151 : : }
152 : : }
153 : :
154 : : /* -----------------
155 : : * ExecInitGroup
156 : : *
157 : : * Creates the run-time information for the group node produced by the
158 : : * planner and initializes its outer subtree
159 : : * -----------------
160 : : */
161 : : GroupState *
7371 tgl@sss.pgh.pa.us 162 :CBC 166 : ExecInitGroup(Group *node, EState *estate, int eflags)
163 : : {
164 : : GroupState *grpstate;
165 : : const TupleTableSlotOps *tts_ops;
166 : :
167 : : /* check for unsupported flags */
168 [ - + ]: 166 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
169 : :
170 : : /*
171 : : * create state structure
172 : : */
10467 bruce@momjian.us 173 : 166 : grpstate = makeNode(GroupState);
8552 tgl@sss.pgh.pa.us 174 : 166 : grpstate->ss.ps.plan = (Plan *) node;
175 : 166 : grpstate->ss.ps.state = estate;
3214 andres@anarazel.de 176 : 166 : grpstate->ss.ps.ExecProcNode = ExecGroup;
3184 peter_e@gmx.net 177 : 166 : grpstate->grp_done = false;
178 : :
179 : : /*
180 : : * create expression context
181 : : */
8552 tgl@sss.pgh.pa.us 182 : 166 : ExecAssignExprContext(estate, &grpstate->ss.ps);
183 : :
184 : : /*
185 : : * initialize child nodes
186 : : */
7371 187 : 166 : outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
188 : :
189 : : /*
190 : : * Initialize scan slot and type.
191 : : */
2728 andres@anarazel.de 192 : 166 : tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
193 : 166 : ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
194 : :
195 : : /*
196 : : * Initialize result slot, type and projection.
197 : : */
198 : 166 : ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
7032 tgl@sss.pgh.pa.us 199 : 166 : ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
200 : :
201 : : /*
202 : : * initialize child expressions
203 : : */
3000 andres@anarazel.de 204 : 166 : grpstate->ss.ps.qual =
205 : 166 : ExecInitQual(node->plan.qual, (PlanState *) grpstate);
206 : :
207 : : /*
208 : : * Precompute fmgr lookup data for inner loop
209 : : */
3001 210 : 166 : grpstate->eqfunction =
211 : 166 : execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
212 : : node->numCols,
2998 tgl@sss.pgh.pa.us 213 : 166 : node->grpColIdx,
3001 andres@anarazel.de 214 : 166 : node->grpOperators,
2601 peter@eisentraut.org 215 : 166 : node->grpCollations,
216 : : &grpstate->ss.ps);
217 : :
8552 tgl@sss.pgh.pa.us 218 : 166 : return grpstate;
219 : : }
220 : :
221 : : /* ------------------------
222 : : * ExecEndGroup(node)
223 : : *
224 : : * -----------------------
225 : : */
226 : : void
227 : 166 : ExecEndGroup(GroupState *node)
228 : : {
229 : : PlanState *outerPlan;
230 : :
8542 231 : 166 : outerPlan = outerPlanState(node);
232 : 166 : ExecEndNode(outerPlan);
10892 scrappy@hub.org 233 : 166 : }
234 : :
235 : : void
5776 tgl@sss.pgh.pa.us 236 : 15 : ExecReScanGroup(GroupState *node)
237 : : {
4000 bruce@momjian.us 238 : 15 : PlanState *outerPlan = outerPlanState(node);
239 : :
3184 peter_e@gmx.net 240 : 15 : node->grp_done = false;
241 : : /* must clear first tuple */
7720 tgl@sss.pgh.pa.us 242 : 15 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
243 : :
244 : : /*
245 : : * if chgParam of subnode is not null then plan will be re-scanned by
246 : : * first ExecProcNode.
247 : : */
4019 rhaas@postgresql.org 248 [ + - ]: 15 : if (outerPlan->chgParam == NULL)
249 : 15 : ExecReScan(outerPlan);
9595 tgl@sss.pgh.pa.us 250 : 15 : }
|