]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Several changes here, not very related but touching some of the same files.
[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.30 1999/09/24 00:24:23 tgl 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                 /* note we rely on subplan to hold ownership of the tuple
95                  * for as long as we need it; we don't copy it.
96                  */
97                 ExecStoreTuple(grpstate->grp_firstTuple,
98                                            grpstate->csstate.css_ScanTupleSlot,
99                                            InvalidBuffer, false);
100         }
101         else
102         {
103                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
104                 if (TupIsNull(outerslot))
105                 {
106                         grpstate->grp_done = TRUE;
107                         return NULL;
108                 }
109                 outerTuple = outerslot->val;
110
111                 firsttuple = grpstate->grp_firstTuple;
112                 /* this should occur on the first call only */
113                 if (firsttuple == NULL)
114                         grpstate->grp_firstTuple = heap_copytuple(outerTuple);
115                 else
116                 {
117
118                         /*
119                          * Compare with first tuple and see if this tuple is of the
120                          * same group.
121                          */
122                         if (!sameGroup(firsttuple, outerTuple,
123                                                    node->numCols, node->grpColIdx,
124                                                    ExecGetScanType(&grpstate->csstate)))
125                         {
126                                 grpstate->grp_useFirstTuple = TRUE;
127                                 pfree(firsttuple);
128                                 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
129
130                                 return NULL;    /* signifies the end of the group */
131                         }
132                 }
133
134                 /* note we rely on subplan to hold ownership of the tuple
135                  * for as long as we need it; we don't copy it.
136                  */
137                 ExecStoreTuple(outerTuple,
138                                            grpstate->csstate.css_ScanTupleSlot,
139                                            InvalidBuffer, 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 (TupIsNull(outerslot))
193                 {
194                         grpstate->grp_done = TRUE;
195                         return NULL;
196                 }
197                 grpstate->grp_firstTuple = firsttuple =
198                         heap_copytuple(outerslot->val);
199         }
200
201         /*
202          * find all tuples that belong to a group
203          */
204         for (;;)
205         {
206                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
207                 if (TupIsNull(outerslot))
208                 {
209                         grpstate->grp_done = TRUE;
210                         outerTuple = NULL;
211                         break;
212                 }
213                 outerTuple = outerslot->val;
214
215                 /* ----------------
216                  *      Compare with first tuple and see if this tuple is of
217                  *      the same group.
218                  * ----------------
219                  */
220                 if ((!sameGroup(firsttuple, outerTuple,
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         /* note we rely on subplan to hold ownership of the tuple
234          * for as long as we need it; we don't copy it.
235          */
236         ExecStoreTuple(firsttuple,
237                                    grpstate->csstate.css_ScanTupleSlot,
238                                    InvalidBuffer, false);
239         econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
240         resultSlot = ExecProject(projInfo, &isDone);
241
242         /* save outerTuple if we are not done yet */
243         if (!grpstate->grp_done)
244         {
245                 pfree(firsttuple);
246                 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
247         }
248
249         return resultSlot;
250 }
251
252 /* -----------------
253  * ExecInitGroup
254  *
255  *      Creates the run-time information for the group node produced by the
256  *      planner and initializes its outer subtree
257  * -----------------
258  */
259 bool
260 ExecInitGroup(Group *node, EState *estate, Plan *parent)
261 {
262         GroupState *grpstate;
263         Plan       *outerPlan;
264
265         /*
266          * assign the node's execution state
267          */
268         node->plan.state = estate;
269
270         /*
271          * create state structure
272          */
273         grpstate = makeNode(GroupState);
274         node->grpstate = grpstate;
275         grpstate->grp_useFirstTuple = FALSE;
276         grpstate->grp_done = FALSE;
277         grpstate->grp_firstTuple = NULL;
278
279         /*
280          * assign node's base id and create expression context
281          */
282         ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
283                                                    (Plan *) parent);
284         ExecAssignExprContext(estate, &grpstate->csstate.cstate);
285
286 #define GROUP_NSLOTS 2
287
288         /*
289          * tuple table initialization
290          */
291         ExecInitScanTupleSlot(estate, &grpstate->csstate);
292         ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
293
294         /*
295          * initializes child nodes
296          */
297         outerPlan = outerPlan(node);
298         ExecInitNode(outerPlan, estate, (Plan *) node);
299
300         /* ----------------
301          *      initialize tuple type.
302          * ----------------
303          */
304         ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
305
306         /*
307          * Initialize tuple type for both result and scan. This node does no
308          * projection
309          */
310         ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
311         ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
312
313         return TRUE;
314 }
315
316 int
317 ExecCountSlotsGroup(Group *node)
318 {
319         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
320 }
321
322 /* ------------------------
323  *              ExecEndGroup(node)
324  *
325  * -----------------------
326  */
327 void
328 ExecEndGroup(Group *node)
329 {
330         GroupState *grpstate;
331         Plan       *outerPlan;
332
333         grpstate = node->grpstate;
334
335         ExecFreeProjectionInfo(&grpstate->csstate.cstate);
336
337         outerPlan = outerPlan(node);
338         ExecEndNode(outerPlan, (Plan *) node);
339
340         /* clean up tuple table */
341         ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
342         if (grpstate->grp_firstTuple != NULL)
343         {
344                 pfree(grpstate->grp_firstTuple);
345                 grpstate->grp_firstTuple = NULL;
346         }
347 }
348
349 /*****************************************************************************
350  *
351  *****************************************************************************/
352
353 /*
354  * code swiped from nodeUnique.c
355  */
356 static bool
357 sameGroup(HeapTuple oldtuple,
358                   HeapTuple newtuple,
359                   int numCols,
360                   AttrNumber *grpColIdx,
361                   TupleDesc tupdesc)
362 {
363         bool            isNull1,
364                                 isNull2;
365         Datum           attr1,
366                                 attr2;
367         char       *val1,
368                            *val2;
369         int                     i;
370         AttrNumber      att;
371         Oid                     typoutput,
372                                 typelem;
373
374         for (i = 0; i < numCols; i++)
375         {
376                 att = grpColIdx[i];
377                 getTypeOutAndElem((Oid) tupdesc->attrs[att - 1]->atttypid,
378                                                   &typoutput, &typelem);
379
380                 attr1 = heap_getattr(oldtuple,
381                                                          att,
382                                                          tupdesc,
383                                                          &isNull1);
384
385                 attr2 = heap_getattr(newtuple,
386                                                          att,
387                                                          tupdesc,
388                                                          &isNull2);
389
390                 if (isNull1 == isNull2)
391                 {
392                         if (isNull1)            /* both are null, they are equal */
393                                 continue;
394
395                         val1 = fmgr(typoutput, attr1, typelem,
396                                                 tupdesc->attrs[att - 1]->atttypmod);
397                         val2 = fmgr(typoutput, attr2, typelem,
398                                                 tupdesc->attrs[att - 1]->atttypmod);
399
400                         /*
401                          * now, val1 and val2 are ascii representations so we can use
402                          * strcmp for comparison
403                          */
404                         if (strcmp(val1, val2) != 0)
405                         {
406                                 pfree(val1);
407                                 pfree(val2);
408                                 return FALSE;
409                         }
410                         pfree(val1);
411                         pfree(val2);
412                 }
413                 else
414                 {
415                         /* one is null and the other isn't, they aren't equal */
416                         return FALSE;
417                 }
418         }
419
420         return TRUE;
421 }
422
423 void
424 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
425 {
426         GroupState *grpstate = node->grpstate;
427
428         grpstate->grp_useFirstTuple = FALSE;
429         grpstate->grp_done = FALSE;
430         if (grpstate->grp_firstTuple != NULL)
431         {
432                 pfree(grpstate->grp_firstTuple);
433                 grpstate->grp_firstTuple = NULL;
434         }
435
436         if (((Plan *) node)->lefttree &&
437                 ((Plan *) node)->lefttree->chgParam == NULL)
438                 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
439 }