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-2025, 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 *
2973 andres@anarazel.de 36 :CBC 3985 : ExecGroup(PlanState *pstate)
37 : : {
38 : 3985 : GroupState *node = castNode(GroupState, pstate);
39 : : ExprContext *econtext;
40 : : TupleTableSlot *firsttupleslot;
41 : : TupleTableSlot *outerslot;
42 : :
2965 43 [ - + ]: 3985 : CHECK_FOR_INTERRUPTS();
44 : :
45 : : /*
46 : : * get state info from node
47 : : */
8311 tgl@sss.pgh.pa.us 48 [ - + ]: 3985 : if (node->grp_done)
10226 bruce@momjian.us 49 :UBC 0 : return NULL;
8311 tgl@sss.pgh.pa.us 50 :CBC 3985 : econtext = node->ss.ps.ps_ExprContext;
51 : :
52 : : /*
53 : : * The ScanTupleSlot holds the (copied) first tuple of each group.
54 : : */
7479 55 : 3985 : 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 [ + - + + ]: 3985 : if (TupIsNull(firsttupleslot))
67 : : {
8311 68 : 83 : outerslot = ExecProcNode(outerPlanState(node));
9780 vadim4o@yahoo.com 69 [ + + + + ]: 83 : if (TupIsNull(outerslot))
70 : : {
71 : : /* empty input, so return nothing */
2943 peter_e@gmx.net 72 : 19 : node->grp_done = true;
10226 bruce@momjian.us 73 : 19 : return NULL;
74 : : }
75 : : /* Copy tuple into firsttupleslot */
7479 tgl@sss.pgh.pa.us 76 : 64 : 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 : : */
6771 82 : 64 : 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 : : */
3098 andres@anarazel.de 88 [ + - ]: 64 : if (ExecQual(node->ss.ps.qual, econtext))
89 : : {
90 : : /*
91 : : * Form and return a projection tuple using the first input tuple.
92 : : */
3152 93 : 64 : return ExecProject(node->ss.ps.ps_ProjInfo);
94 : : }
95 : : else
5098 tgl@sss.pgh.pa.us 96 [ # # ]:UBC 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 : : */
10226 bruce@momjian.us 104 : 0 : for (;;)
105 : : {
106 : : /*
107 : : * Scan over all remaining tuples that belong to this group
108 : : */
109 : : for (;;)
110 : : {
7485 tgl@sss.pgh.pa.us 111 :CBC 33792 : outerslot = ExecProcNode(outerPlanState(node));
112 [ + + + + ]: 18847 : if (TupIsNull(outerslot))
113 : : {
114 : : /* no more groups, so we're done */
2943 peter_e@gmx.net 115 : 64 : node->grp_done = true;
7485 tgl@sss.pgh.pa.us 116 : 64 : 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 : : */
2760 andres@anarazel.de 123 : 18783 : econtext->ecxt_innertuple = firsttupleslot;
124 : 18783 : econtext->ecxt_outertuple = outerslot;
125 [ + + ]: 18783 : if (!ExecQualAndReset(node->eqfunction, econtext))
7485 tgl@sss.pgh.pa.us 126 : 3838 : 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 */
7479 134 : 3838 : ExecCopySlot(firsttupleslot, outerslot);
6771 135 : 3838 : 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 : : */
3098 andres@anarazel.de 141 [ + - ]: 3838 : if (ExecQual(node->ss.ps.qual, econtext))
142 : : {
143 : : /*
144 : : * Form and return a projection tuple using the first input tuple.
145 : : */
3152 146 : 3838 : return ExecProject(node->ss.ps.ps_ProjInfo);
147 : : }
148 : : else
5098 tgl@sss.pgh.pa.us 149 [ # # ]:UBC 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 *
7130 tgl@sss.pgh.pa.us 161 :CBC 123 : ExecInitGroup(Group *node, EState *estate, int eflags)
162 : : {
163 : : GroupState *grpstate;
164 : : const TupleTableSlotOps *tts_ops;
165 : :
166 : : /* check for unsupported flags */
167 [ - + ]: 123 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
168 : :
169 : : /*
170 : : * create state structure
171 : : */
10226 bruce@momjian.us 172 : 123 : grpstate = makeNode(GroupState);
8311 tgl@sss.pgh.pa.us 173 : 123 : grpstate->ss.ps.plan = (Plan *) node;
174 : 123 : grpstate->ss.ps.state = estate;
2973 andres@anarazel.de 175 : 123 : grpstate->ss.ps.ExecProcNode = ExecGroup;
2943 peter_e@gmx.net 176 : 123 : grpstate->grp_done = false;
177 : :
178 : : /*
179 : : * create expression context
180 : : */
8311 tgl@sss.pgh.pa.us 181 : 123 : ExecAssignExprContext(estate, &grpstate->ss.ps);
182 : :
183 : : /*
184 : : * initialize child nodes
185 : : */
7130 186 : 123 : outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
187 : :
188 : : /*
189 : : * Initialize scan slot and type.
190 : : */
2487 andres@anarazel.de 191 : 123 : tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
192 : 123 : ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
193 : :
194 : : /*
195 : : * Initialize result slot, type and projection.
196 : : */
197 : 123 : ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
6791 tgl@sss.pgh.pa.us 198 : 123 : ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
199 : :
200 : : /*
201 : : * initialize child expressions
202 : : */
2759 andres@anarazel.de 203 : 123 : grpstate->ss.ps.qual =
204 : 123 : ExecInitQual(node->plan.qual, (PlanState *) grpstate);
205 : :
206 : : /*
207 : : * Precompute fmgr lookup data for inner loop
208 : : */
2760 209 : 123 : grpstate->eqfunction =
210 : 123 : execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
211 : : node->numCols,
2757 tgl@sss.pgh.pa.us 212 : 123 : node->grpColIdx,
2760 andres@anarazel.de 213 : 123 : node->grpOperators,
2360 peter@eisentraut.org 214 : 123 : node->grpCollations,
215 : : &grpstate->ss.ps);
216 : :
8311 tgl@sss.pgh.pa.us 217 : 123 : return grpstate;
218 : : }
219 : :
220 : : /* ------------------------
221 : : * ExecEndGroup(node)
222 : : *
223 : : * -----------------------
224 : : */
225 : : void
226 : 123 : ExecEndGroup(GroupState *node)
227 : : {
228 : : PlanState *outerPlan;
229 : :
8301 230 : 123 : outerPlan = outerPlanState(node);
231 : 123 : ExecEndNode(outerPlan);
10651 scrappy@hub.org 232 : 123 : }
233 : :
234 : : void
5535 tgl@sss.pgh.pa.us 235 : 12 : ExecReScanGroup(GroupState *node)
236 : : {
3759 bruce@momjian.us 237 : 12 : PlanState *outerPlan = outerPlanState(node);
238 : :
2943 peter_e@gmx.net 239 : 12 : node->grp_done = false;
240 : : /* must clear first tuple */
7479 tgl@sss.pgh.pa.us 241 : 12 : 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 : : */
3778 rhaas@postgresql.org 247 [ + - ]: 12 : if (outerPlan->chgParam == NULL)
248 : 12 : ExecReScan(outerPlan);
9354 tgl@sss.pgh.pa.us 249 : 12 : }
|