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