]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Don't require return slots for nodes without projection.
[postgresql] / src / backend / executor / nodeGroup.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeGroup.c
4  *        Routines to handle group nodes (used for queries with GROUP BY clause).
5  *
6  * Portions Copyright (c) 1996-2018, 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 *
37 ExecGroup(PlanState *pstate)
38 {
39         GroupState *node = castNode(GroupState, pstate);
40         ExprContext *econtext;
41         TupleTableSlot *firsttupleslot;
42         TupleTableSlot *outerslot;
43
44         CHECK_FOR_INTERRUPTS();
45
46         /*
47          * get state info from node
48          */
49         if (node->grp_done)
50                 return NULL;
51         econtext = node->ss.ps.ps_ExprContext;
52
53         /*
54          * The ScanTupleSlot holds the (copied) first tuple of each group.
55          */
56         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         if (TupIsNull(firsttupleslot))
68         {
69                 outerslot = ExecProcNode(outerPlanState(node));
70                 if (TupIsNull(outerslot))
71                 {
72                         /* empty input, so return nothing */
73                         node->grp_done = true;
74                         return NULL;
75                 }
76                 /* Copy tuple into firsttupleslot */
77                 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                  */
83                 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                  */
89                 if (ExecQual(node->ss.ps.qual, econtext))
90                 {
91                         /*
92                          * Form and return a projection tuple using the first input tuple.
93                          */
94                         return ExecProject(node->ss.ps.ps_ProjInfo);
95                 }
96                 else
97                         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          */
105         for (;;)
106         {
107                 /*
108                  * Scan over all remaining tuples that belong to this group
109                  */
110                 for (;;)
111                 {
112                         outerslot = ExecProcNode(outerPlanState(node));
113                         if (TupIsNull(outerslot))
114                         {
115                                 /* no more groups, so we're done */
116                                 node->grp_done = true;
117                                 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                          */
124                         econtext->ecxt_innertuple = firsttupleslot;
125                         econtext->ecxt_outertuple = outerslot;
126                         if (!ExecQualAndReset(node->eqfunction, econtext))
127                                 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 */
135                 ExecCopySlot(firsttupleslot, outerslot);
136                 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                  */
142                 if (ExecQual(node->ss.ps.qual, econtext))
143                 {
144                         /*
145                          * Form and return a projection tuple using the first input tuple.
146                          */
147                         return ExecProject(node->ss.ps.ps_ProjInfo);
148                 }
149                 else
150                         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 *
162 ExecInitGroup(Group *node, EState *estate, int eflags)
163 {
164         GroupState *grpstate;
165
166         /* check for unsupported flags */
167         Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
168
169         /*
170          * create state structure
171          */
172         grpstate = makeNode(GroupState);
173         grpstate->ss.ps.plan = (Plan *) node;
174         grpstate->ss.ps.state = estate;
175         grpstate->ss.ps.ExecProcNode = ExecGroup;
176         grpstate->grp_done = false;
177
178         /*
179          * create expression context
180          */
181         ExecAssignExprContext(estate, &grpstate->ss.ps);
182
183         /*
184          * initialize child nodes
185          */
186         outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
187
188         /*
189          * Initialize scan slot and type.
190          */
191         ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss);
192
193         /*
194          * Initialize result slot, type and projection.
195          */
196         ExecInitResultTupleSlotTL(&grpstate->ss.ps);
197         ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
198
199         /*
200          * initialize child expressions
201          */
202         grpstate->ss.ps.qual =
203                 ExecInitQual(node->plan.qual, (PlanState *) grpstate);
204
205         /*
206          * Precompute fmgr lookup data for inner loop
207          */
208         grpstate->eqfunction =
209                 execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
210                                                            node->numCols,
211                                                            node->grpColIdx,
212                                                            node->grpOperators,
213                                                            &grpstate->ss.ps);
214
215         return grpstate;
216 }
217
218 /* ------------------------
219  *              ExecEndGroup(node)
220  *
221  * -----------------------
222  */
223 void
224 ExecEndGroup(GroupState *node)
225 {
226         PlanState  *outerPlan;
227
228         ExecFreeExprContext(&node->ss.ps);
229
230         /* clean up tuple table */
231         ExecClearTuple(node->ss.ss_ScanTupleSlot);
232
233         outerPlan = outerPlanState(node);
234         ExecEndNode(outerPlan);
235 }
236
237 void
238 ExecReScanGroup(GroupState *node)
239 {
240         PlanState  *outerPlan = outerPlanState(node);
241
242         node->grp_done = false;
243         /* must clear first tuple */
244         ExecClearTuple(node->ss.ss_ScanTupleSlot);
245
246         /*
247          * if chgParam of subnode is not null then plan will be re-scanned by
248          * first ExecProcNode.
249          */
250         if (outerPlan->chgParam == NULL)
251                 ExecReScan(outerPlan);
252 }