]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Add typdefs to pgindent run.
[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  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * DESCRIPTION
10  *        The Group node is designed for handling queries with a GROUP BY clause.
11  *        It's outer plan must be a sort node. It assumes that the tuples it gets
12  *        back from the outer plan is sorted in the order specified by the group
13  *        columns. (ie. tuples from the same group are consecutive)
14  *
15  * IDENTIFICATION
16  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.8 1997/09/08 20:55:43 momjian Exp $
17  *
18  *-------------------------------------------------------------------------
19  */
20 #include <string.h>
21
22 #include "postgres.h"
23 #include "fmgr.h"
24
25 #include "access/heapam.h"
26 #include "catalog/catalog.h"
27 #include "access/printtup.h"
28 #include "executor/executor.h"
29 #include "executor/nodeGroup.h"
30
31 static TupleTableSlot *ExecGroupEveryTuple(Group * node);
32 static TupleTableSlot *ExecGroupOneTuple(Group * node);
33 static bool
34 sameGroup(TupleTableSlot * oldslot, TupleTableSlot * newslot,
35                   int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
36
37 /* ---------------------------------------
38  *       ExecGroup -
39  *
40  *              There are two modes in which tuples are returned by ExecGroup. If
41  *              tuplePerGroup is TRUE, every tuple from the same group will be
42  *              returned, followed by a NULL at the end of each group. This is
43  *              useful for Agg node which needs to aggregate over tuples of the same
44  *              group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
45  *
46  *              If tuplePerGroup is FALSE, only one tuple per group is returned. The
47  *              tuple returned contains only the group columns. NULL is returned only
48  *              at the end when no more groups is present. This is useful when
49  *              the query does not involve aggregates. (eg. SELECT salary FROM emp
50  *              GROUP BY salary)
51  * ------------------------------------------
52  */
53 TupleTableSlot *
54 ExecGroup(Group * node)
55 {
56         if (node->tuplePerGroup)
57                 return ExecGroupEveryTuple(node);
58         else
59                 return ExecGroupOneTuple(node);
60 }
61
62 /*
63  * ExecGroupEveryTuple -
64  *       return every tuple with a NULL between each group
65  */
66 static TupleTableSlot *
67 ExecGroupEveryTuple(Group * node)
68 {
69         GroupState *grpstate;
70         EState     *estate;
71         ExprContext *econtext;
72
73         HeapTuple       outerTuple = NULL;
74         TupleTableSlot *outerslot,
75                            *lastslot;
76         ProjectionInfo *projInfo;
77         TupleTableSlot *resultSlot;
78
79         bool            isDone;
80
81         /* ---------------------
82          *      get state info from node
83          * ---------------------
84          */
85         grpstate = node->grpstate;
86         if (grpstate->grp_done)
87                 return NULL;
88
89         estate = node->plan.state;
90
91         econtext = grpstate->csstate.cstate.cs_ExprContext;
92
93         if (grpstate->grp_useLastTuple)
94         {
95
96                 /*
97                  * we haven't returned last tuple yet because it is not of the
98                  * same group
99                  */
100                 grpstate->grp_useLastTuple = FALSE;
101
102                 ExecStoreTuple(grpstate->grp_lastSlot->val,
103                                            grpstate->csstate.css_ScanTupleSlot,
104                                            grpstate->grp_lastSlot->ttc_buffer,
105                                            false);
106         }
107         else
108         {
109                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
110                 if (outerslot)
111                         outerTuple = outerslot->val;
112                 if (!HeapTupleIsValid(outerTuple))
113                 {
114                         grpstate->grp_done = TRUE;
115                         return NULL;
116                 }
117
118                 /* ----------------
119                  *      Compare with last tuple and see if this tuple is of
120                  *      the same group.
121                  * ----------------
122                  */
123                 lastslot = grpstate->csstate.css_ScanTupleSlot;
124
125                 if (lastslot->val != NULL &&
126                         (!sameGroup(lastslot, outerslot,
127                                                 node->numCols, node->grpColIdx,
128                                                 ExecGetScanType(&grpstate->csstate))))
129                 {
130 /*                                              ExecGetResultType(&grpstate->csstate.cstate)))) {*/
131
132                         grpstate->grp_useLastTuple = TRUE;
133
134                         /* save it for next time */
135                         grpstate->grp_lastSlot = outerslot;
136
137                         /*
138                          * signifies the end of the group
139                          */
140                         return NULL;
141                 }
142
143                 ExecStoreTuple(outerTuple,
144                                            grpstate->csstate.css_ScanTupleSlot,
145                                            outerslot->ttc_buffer,
146                                            false);
147         }
148
149         /* ----------------
150          *      form a projection tuple, store it in the result tuple
151          *      slot and return it.
152          * ----------------
153          */
154         projInfo = grpstate->csstate.cstate.cs_ProjInfo;
155
156         econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
157         resultSlot = ExecProject(projInfo, &isDone);
158
159         return resultSlot;
160 }
161
162 /*
163  * ExecGroupOneTuple -
164  *        returns one tuple per group, a NULL at the end when there are no more
165  *        tuples.
166  */
167 static TupleTableSlot *
168 ExecGroupOneTuple(Group * node)
169 {
170         GroupState *grpstate;
171         EState     *estate;
172         ExprContext *econtext;
173
174         HeapTuple       outerTuple = NULL;
175         TupleTableSlot *outerslot,
176                            *lastslot;
177         ProjectionInfo *projInfo;
178         TupleTableSlot *resultSlot;
179
180         bool            isDone;
181
182         /* ---------------------
183          *      get state info from node
184          * ---------------------
185          */
186         grpstate = node->grpstate;
187         if (grpstate->grp_done)
188                 return NULL;
189
190         estate = node->plan.state;
191
192         econtext = node->grpstate->csstate.cstate.cs_ExprContext;
193
194         if (grpstate->grp_useLastTuple)
195         {
196                 grpstate->grp_useLastTuple = FALSE;
197                 ExecStoreTuple(grpstate->grp_lastSlot->val,
198                                            grpstate->csstate.css_ScanTupleSlot,
199                                            grpstate->grp_lastSlot->ttc_buffer,
200                                            false);
201         }
202         else
203         {
204                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
205                 if (outerslot)
206                         outerTuple = outerslot->val;
207                 if (!HeapTupleIsValid(outerTuple))
208                 {
209                         grpstate->grp_done = TRUE;
210                         return NULL;
211                 }
212                 ExecStoreTuple(outerTuple,
213                                            grpstate->csstate.css_ScanTupleSlot,
214                                            outerslot->ttc_buffer,
215                                            false);
216         }
217         lastslot = grpstate->csstate.css_ScanTupleSlot;
218
219         /*
220          * find all tuples that belong to a group
221          */
222         for (;;)
223         {
224                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
225                 outerTuple = (outerslot) ? outerslot->val : NULL;
226                 if (!HeapTupleIsValid(outerTuple))
227                 {
228
229                         /*
230                          * we have at least one tuple (lastslot) if we reach here
231                          */
232                         grpstate->grp_done = TRUE;
233
234                         /* return lastslot */
235                         break;
236                 }
237
238                 /* ----------------
239                  *      Compare with last tuple and see if this tuple is of
240                  *      the same group.
241                  * ----------------
242                  */
243                 if ((!sameGroup(lastslot, outerslot,
244                                                 node->numCols, node->grpColIdx,
245                                                 ExecGetScanType(&grpstate->csstate))))
246                 {
247 /*                                              ExecGetResultType(&grpstate->csstate.cstate)))) {*/
248
249                         grpstate->grp_useLastTuple = TRUE;
250
251                         /* save it for next time */
252                         grpstate->grp_lastSlot = outerslot;
253
254                         /* return lastslot */
255                         break;
256                 }
257
258                 ExecStoreTuple(outerTuple,
259                                            grpstate->csstate.css_ScanTupleSlot,
260                                            outerslot->ttc_buffer,
261                                            false);
262
263                 lastslot = grpstate->csstate.css_ScanTupleSlot;
264         }
265
266         ExecStoreTuple(lastslot->val,
267                                    grpstate->csstate.css_ScanTupleSlot,
268                                    lastslot->ttc_buffer,
269                                    false);
270
271         /* ----------------
272          *      form a projection tuple, store it in the result tuple
273          *      slot and return it.
274          * ----------------
275          */
276         projInfo = grpstate->csstate.cstate.cs_ProjInfo;
277
278         econtext->ecxt_scantuple = lastslot;
279         resultSlot = ExecProject(projInfo, &isDone);
280
281         return resultSlot;
282 }
283
284 /* -----------------
285  * ExecInitGroup
286  *
287  *      Creates the run-time information for the group node produced by the
288  *      planner and initializes its outer subtree
289  * -----------------
290  */
291 bool
292 ExecInitGroup(Group * node, EState * estate, Plan * parent)
293 {
294         GroupState *grpstate;
295         Plan       *outerPlan;
296
297         /*
298          * assign the node's execution state
299          */
300         node->plan.state = estate;
301
302         /*
303          * create state structure
304          */
305         grpstate = makeNode(GroupState);
306         node->grpstate = grpstate;
307         grpstate->grp_useLastTuple = FALSE;
308         grpstate->grp_done = FALSE;
309
310         /*
311          * assign node's base id and create expression context
312          */
313         ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
314                                                    (Plan *) parent);
315         ExecAssignExprContext(estate, &grpstate->csstate.cstate);
316
317 #define GROUP_NSLOTS 2
318
319         /*
320          * tuple table initialization
321          */
322         ExecInitScanTupleSlot(estate, &grpstate->csstate);
323         ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
324
325         /*
326          * initializes child nodes
327          */
328         outerPlan = outerPlan(node);
329         ExecInitNode(outerPlan, estate, (Plan *) node);
330
331         /* ----------------
332          *      initialize tuple type.
333          * ----------------
334          */
335         ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
336
337         /*
338          * Initialize tuple type for both result and scan. This node does no
339          * projection
340          */
341         ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
342         ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
343
344         return TRUE;
345 }
346
347 int
348 ExecCountSlotsGroup(Group * node)
349 {
350         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
351 }
352
353 /* ------------------------
354  *              ExecEndGroup(node)
355  *
356  * -----------------------
357  */
358 void
359 ExecEndGroup(Group * node)
360 {
361         GroupState *grpstate;
362         Plan       *outerPlan;
363
364         grpstate = node->grpstate;
365
366         ExecFreeProjectionInfo(&grpstate->csstate.cstate);
367
368         outerPlan = outerPlan(node);
369         ExecEndNode(outerPlan, (Plan *) node);
370
371         /* clean up tuple table */
372         ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
373 }
374
375 /*****************************************************************************
376  *
377  *****************************************************************************/
378
379 /*
380  * code swiped from nodeUnique.c
381  */
382 static bool
383 sameGroup(TupleTableSlot * oldslot,
384                   TupleTableSlot * newslot,
385                   int numCols,
386                   AttrNumber *grpColIdx,
387                   TupleDesc tupdesc)
388 {
389         bool            isNull1,
390                                 isNull2;
391         char       *attr1,
392                            *attr2;
393         char       *val1,
394                            *val2;
395         int                     i;
396         AttrNumber      att;
397         Oid                     typoutput;
398
399         for (i = 0; i < numCols; i++)
400         {
401                 att = grpColIdx[i];
402                 typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid);
403
404                 attr1 = heap_getattr(oldslot->val,
405                                                          InvalidBuffer,
406                                                          att,
407                                                          tupdesc,
408                                                          &isNull1);
409
410                 attr2 = heap_getattr(newslot->val,
411                                                          InvalidBuffer,
412                                                          att,
413                                                          tupdesc,
414                                                          &isNull2);
415
416                 if (isNull1 == isNull2)
417                 {
418                         if (isNull1)            /* both are null, they are equal */
419                                 continue;
420
421                         val1 = fmgr(typoutput, attr1,
422                                                 gettypelem(tupdesc->attrs[att - 1]->atttypid));
423                         val2 = fmgr(typoutput, attr2,
424                                                 gettypelem(tupdesc->attrs[att - 1]->atttypid));
425
426                         /*
427                          * now, val1 and val2 are ascii representations so we can use
428                          * strcmp for comparison
429                          */
430                         if (strcmp(val1, val2) != 0)
431                                 return FALSE;
432                 }
433                 else
434                 {
435                         /* one is null and the other isn't, they aren't equal */
436                         return FALSE;
437                 }
438         }
439
440         return TRUE;
441 }