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