Age Owner 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-2023, 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 : #include "utils/memutils.h"
29 :
30 :
31 : /*
32 : * ExecGroup -
33 : *
34 : * Return one tuple for each group of matching input tuples.
35 : */
36 : static TupleTableSlot *
2092 andres 37 CBC 3979 : ExecGroup(PlanState *pstate)
38 : {
39 3979 : GroupState *node = castNode(GroupState, pstate);
40 : ExprContext *econtext;
41 : TupleTableSlot *firsttupleslot;
42 : TupleTableSlot *outerslot;
43 :
2084 44 3979 : CHECK_FOR_INTERRUPTS();
45 :
46 : /*
47 : * get state info from node
48 : */
7430 tgl 49 3979 : if (node->grp_done)
9345 bruce 50 UBC 0 : return NULL;
7430 tgl 51 CBC 3979 : econtext = node->ss.ps.ps_ExprContext;
52 :
53 : /*
54 : * The ScanTupleSlot holds the (copied) first tuple of each group.
55 : */
6598 56 3979 : 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 3979 : if (TupIsNull(firsttupleslot))
68 : {
7430 69 80 : outerslot = ExecProcNode(outerPlanState(node));
8899 vadim4o 70 80 : if (TupIsNull(outerslot))
71 : {
72 : /* empty input, so return nothing */
2062 peter_e 73 19 : node->grp_done = true;
9345 bruce 74 19 : return NULL;
75 : }
76 : /* Copy tuple into firsttupleslot */
6598 tgl 77 61 : 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 : */
5890 83 61 : 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 : */
2217 andres 89 61 : if (ExecQual(node->ss.ps.qual, econtext))
90 : {
91 : /*
92 : * Form and return a projection tuple using the first input tuple.
93 : */
2271 94 61 : return ExecProject(node->ss.ps.ps_ProjInfo);
95 : }
96 : else
4217 tgl 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 : */
9345 bruce 105 0 : for (;;)
106 : {
107 : /*
108 : * Scan over all remaining tuples that belong to this group
109 : */
110 : for (;;)
111 : {
6604 tgl 112 CBC 33789 : outerslot = ExecProcNode(outerPlanState(node));
113 18844 : if (TupIsNull(outerslot))
114 : {
115 : /* no more groups, so we're done */
2062 peter_e 116 61 : node->grp_done = true;
6604 tgl 117 61 : 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 : */
1879 andres 124 18783 : econtext->ecxt_innertuple = firsttupleslot;
125 18783 : econtext->ecxt_outertuple = outerslot;
126 18783 : if (!ExecQualAndReset(node->eqfunction, econtext))
6604 tgl 127 3838 : 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 */
6598 135 3838 : ExecCopySlot(firsttupleslot, outerslot);
5890 136 3838 : 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 : */
2217 andres 142 3838 : if (ExecQual(node->ss.ps.qual, econtext))
143 : {
144 : /*
145 : * Form and return a projection tuple using the first input tuple.
146 : */
2271 147 3838 : return ExecProject(node->ss.ps.ps_ProjInfo);
148 : }
149 : else
4217 tgl 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 *
6249 tgl 162 CBC 111 : ExecInitGroup(Group *node, EState *estate, int eflags)
163 : {
164 : GroupState *grpstate;
165 : const TupleTableSlotOps *tts_ops;
166 :
167 : /* check for unsupported flags */
168 111 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
169 :
170 : /*
171 : * create state structure
172 : */
9345 bruce 173 111 : grpstate = makeNode(GroupState);
7430 tgl 174 111 : grpstate->ss.ps.plan = (Plan *) node;
175 111 : grpstate->ss.ps.state = estate;
2092 andres 176 111 : grpstate->ss.ps.ExecProcNode = ExecGroup;
2062 peter_e 177 111 : grpstate->grp_done = false;
178 :
179 : /*
180 : * create expression context
181 : */
7430 tgl 182 111 : ExecAssignExprContext(estate, &grpstate->ss.ps);
183 :
184 : /*
185 : * initialize child nodes
186 : */
6249 187 111 : outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
188 :
189 : /*
190 : * Initialize scan slot and type.
191 : */
1606 andres 192 111 : tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
193 111 : ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
194 :
195 : /*
196 : * Initialize result slot, type and projection.
197 : */
198 111 : ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
5910 tgl 199 111 : ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
200 :
201 : /*
202 : * initialize child expressions
203 : */
1878 andres 204 111 : grpstate->ss.ps.qual =
205 111 : ExecInitQual(node->plan.qual, (PlanState *) grpstate);
206 :
207 : /*
208 : * Precompute fmgr lookup data for inner loop
209 : */
1879 210 111 : grpstate->eqfunction =
211 111 : execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
212 : node->numCols,
1876 tgl 213 111 : node->grpColIdx,
1879 andres 214 111 : node->grpOperators,
1479 peter 215 111 : node->grpCollations,
216 : &grpstate->ss.ps);
217 :
7430 tgl 218 111 : return grpstate;
219 : }
220 :
221 : /* ------------------------
222 : * ExecEndGroup(node)
223 : *
224 : * -----------------------
225 : */
226 : void
227 111 : ExecEndGroup(GroupState *node)
228 : {
229 : PlanState *outerPlan;
230 :
231 111 : ExecFreeExprContext(&node->ss.ps);
232 :
233 : /* clean up tuple table */
234 111 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
235 :
7420 236 111 : outerPlan = outerPlanState(node);
237 111 : ExecEndNode(outerPlan);
9770 scrappy 238 111 : }
239 :
240 : void
4654 tgl 241 12 : ExecReScanGroup(GroupState *node)
242 : {
2878 bruce 243 12 : PlanState *outerPlan = outerPlanState(node);
244 :
2062 peter_e 245 12 : node->grp_done = false;
246 : /* must clear first tuple */
6598 tgl 247 12 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
248 :
249 : /*
250 : * if chgParam of subnode is not null then plan will be re-scanned by
251 : * first ExecProcNode.
252 : */
2897 rhaas 253 12 : if (outerPlan->chgParam == NULL)
254 12 : ExecReScan(outerPlan);
8473 tgl 255 12 : }
|