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