]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
SQL-language functions are now callable in ordinary fmgr contexts ...
[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-2000, PostgreSQL, Inc
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  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.38 2000/08/24 03:29:03 tgl Exp $
19  *
20  *-------------------------------------------------------------------------
21  */
22
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "catalog/pg_operator.h"
27 #include "executor/executor.h"
28 #include "executor/nodeGroup.h"
29 #include "parser/parse_oper.h"
30 #include "parser/parse_type.h"
31
32 static TupleTableSlot *ExecGroupEveryTuple(Group *node);
33 static TupleTableSlot *ExecGroupOneTuple(Group *node);
34
35 /* ---------------------------------------
36  *       ExecGroup -
37  *
38  *              There are two modes in which tuples are returned by ExecGroup. If
39  *              tuplePerGroup is TRUE, every tuple from the same group will be
40  *              returned, followed by a NULL at the end of each group. This is
41  *              useful for Agg node which needs to aggregate over tuples of the same
42  *              group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
43  *
44  *              If tuplePerGroup is FALSE, only one tuple per group is returned. The
45  *              tuple returned contains only the group columns. NULL is returned only
46  *              at the end when no more groups are present. This is useful when
47  *              the query does not involve aggregates. (eg. SELECT salary FROM emp
48  *              GROUP BY salary)
49  * ------------------------------------------
50  */
51 TupleTableSlot *
52 ExecGroup(Group *node)
53 {
54         if (node->tuplePerGroup)
55                 return ExecGroupEveryTuple(node);
56         else
57                 return ExecGroupOneTuple(node);
58 }
59
60 /*
61  * ExecGroupEveryTuple -
62  *       return every tuple with a NULL between each group
63  */
64 static TupleTableSlot *
65 ExecGroupEveryTuple(Group *node)
66 {
67         GroupState *grpstate;
68         EState     *estate;
69         ExprContext *econtext;
70         TupleDesc       tupdesc;
71         HeapTuple       outerTuple = NULL;
72         HeapTuple       firsttuple;
73         TupleTableSlot *outerslot;
74         ProjectionInfo *projInfo;
75         TupleTableSlot *resultSlot;
76
77         /* ---------------------
78          *      get state info from node
79          * ---------------------
80          */
81         grpstate = node->grpstate;
82         if (grpstate->grp_done)
83                 return NULL;
84         estate = node->plan.state;
85         econtext = grpstate->csstate.cstate.cs_ExprContext;
86         tupdesc = ExecGetScanType(&grpstate->csstate);
87
88         /*
89          *      We need not call ResetExprContext here because execTuplesMatch
90          *      will reset the per-tuple memory context once per input tuple.
91          */
92
93         /* if we haven't returned first tuple of a new group yet ... */
94         if (grpstate->grp_useFirstTuple)
95         {
96                 grpstate->grp_useFirstTuple = FALSE;
97
98                 /*
99                  * note we rely on subplan to hold ownership of the tuple for as
100                  * long as we need it; we don't copy it.
101                  */
102                 ExecStoreTuple(grpstate->grp_firstTuple,
103                                            grpstate->csstate.css_ScanTupleSlot,
104                                            InvalidBuffer, false);
105         }
106         else
107         {
108                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
109                 if (TupIsNull(outerslot))
110                 {
111                         grpstate->grp_done = TRUE;
112                         return NULL;
113                 }
114                 outerTuple = outerslot->val;
115
116                 firsttuple = grpstate->grp_firstTuple;
117                 if (firsttuple == NULL)
118                 {
119                         /* this should occur on the first call only */
120                         grpstate->grp_firstTuple = heap_copytuple(outerTuple);
121                 }
122                 else
123                 {
124
125                         /*
126                          * Compare with first tuple and see if this tuple is of the
127                          * same group.
128                          */
129                         if (!execTuplesMatch(firsttuple, outerTuple,
130                                                                  tupdesc,
131                                                                  node->numCols, node->grpColIdx,
132                                                                  grpstate->eqfunctions,
133                                                                  econtext->ecxt_per_tuple_memory))
134                         {
135
136                                 /*
137                                  * No; save the tuple to return it next time, and return
138                                  * NULL
139                                  */
140                                 grpstate->grp_useFirstTuple = TRUE;
141                                 heap_freetuple(firsttuple);
142                                 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
143
144                                 return NULL;    /* signifies the end of the group */
145                         }
146                 }
147
148                 /*
149                  * note we rely on subplan to hold ownership of the tuple for as
150                  * long as we need it; we don't copy it.
151                  */
152                 ExecStoreTuple(outerTuple,
153                                            grpstate->csstate.css_ScanTupleSlot,
154                                            InvalidBuffer, false);
155         }
156
157         /* ----------------
158          *      form a projection tuple, store it in the result tuple
159          *      slot and return it.
160          * ----------------
161          */
162         projInfo = grpstate->csstate.cstate.cs_ProjInfo;
163
164         econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
165         resultSlot = ExecProject(projInfo, NULL);
166
167         return resultSlot;
168 }
169
170 /*
171  * ExecGroupOneTuple -
172  *        returns one tuple per group, a NULL at the end when there are no more
173  *        tuples.
174  */
175 static TupleTableSlot *
176 ExecGroupOneTuple(Group *node)
177 {
178         GroupState *grpstate;
179         EState     *estate;
180         ExprContext *econtext;
181         TupleDesc       tupdesc;
182         HeapTuple       outerTuple = NULL;
183         HeapTuple       firsttuple;
184         TupleTableSlot *outerslot;
185         ProjectionInfo *projInfo;
186         TupleTableSlot *resultSlot;
187
188         /* ---------------------
189          *      get state info from node
190          * ---------------------
191          */
192         grpstate = node->grpstate;
193         if (grpstate->grp_done)
194                 return NULL;
195         estate = node->plan.state;
196         econtext = node->grpstate->csstate.cstate.cs_ExprContext;
197         tupdesc = ExecGetScanType(&grpstate->csstate);
198
199         /*
200          *      We need not call ResetExprContext here because execTuplesMatch
201          *      will reset the per-tuple memory context once per input tuple.
202          */
203
204         firsttuple = grpstate->grp_firstTuple;
205         if (firsttuple == NULL)
206         {
207                 /* this should occur on the first call only */
208                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
209                 if (TupIsNull(outerslot))
210                 {
211                         grpstate->grp_done = TRUE;
212                         return NULL;
213                 }
214                 grpstate->grp_firstTuple = firsttuple =
215                         heap_copytuple(outerslot->val);
216         }
217
218         /*
219          * find all tuples that belong to a group
220          */
221         for (;;)
222         {
223                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
224                 if (TupIsNull(outerslot))
225                 {
226                         grpstate->grp_done = TRUE;
227                         outerTuple = NULL;
228                         break;
229                 }
230                 outerTuple = outerslot->val;
231
232                 /*
233                  * Compare with first tuple and see if this tuple is of the same
234                  * group.
235                  */
236                 if (!execTuplesMatch(firsttuple, outerTuple,
237                                                          tupdesc,
238                                                          node->numCols, node->grpColIdx,
239                                                          grpstate->eqfunctions,
240                                                          econtext->ecxt_per_tuple_memory))
241                         break;
242         }
243
244         /* ----------------
245          *      form a projection tuple, store it in the result tuple
246          *      slot and return it.
247          * ----------------
248          */
249         projInfo = grpstate->csstate.cstate.cs_ProjInfo;
250
251         /*
252          * note we rely on subplan to hold ownership of the tuple for as long
253          * as we need it; we don't copy it.
254          */
255         ExecStoreTuple(firsttuple,
256                                    grpstate->csstate.css_ScanTupleSlot,
257                                    InvalidBuffer, false);
258         econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
259         resultSlot = ExecProject(projInfo, NULL);
260
261         /* save outerTuple if we are not done yet */
262         if (!grpstate->grp_done)
263         {
264                 heap_freetuple(firsttuple);
265                 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
266         }
267
268         return resultSlot;
269 }
270
271 /* -----------------
272  * ExecInitGroup
273  *
274  *      Creates the run-time information for the group node produced by the
275  *      planner and initializes its outer subtree
276  * -----------------
277  */
278 bool
279 ExecInitGroup(Group *node, EState *estate, Plan *parent)
280 {
281         GroupState *grpstate;
282         Plan       *outerPlan;
283
284         /*
285          * assign the node's execution state
286          */
287         node->plan.state = estate;
288
289         /*
290          * create state structure
291          */
292         grpstate = makeNode(GroupState);
293         node->grpstate = grpstate;
294         grpstate->grp_useFirstTuple = FALSE;
295         grpstate->grp_done = FALSE;
296         grpstate->grp_firstTuple = NULL;
297
298         /*
299          * create expression context
300          */
301         ExecAssignExprContext(estate, &grpstate->csstate.cstate);
302
303 #define GROUP_NSLOTS 2
304
305         /*
306          * tuple table initialization
307          */
308         ExecInitScanTupleSlot(estate, &grpstate->csstate);
309         ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
310
311         /*
312          * initializes child nodes
313          */
314         outerPlan = outerPlan(node);
315         ExecInitNode(outerPlan, estate, (Plan *) node);
316
317         /* ----------------
318          *      initialize tuple type.
319          * ----------------
320          */
321         ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
322
323         /*
324          * Initialize tuple type for both result and scan. This node does no
325          * projection
326          */
327         ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
328         ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
329
330         /*
331          * Precompute fmgr lookup data for inner loop
332          */
333         grpstate->eqfunctions =
334                 execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
335                                                            node->numCols,
336                                                            node->grpColIdx);
337
338         return TRUE;
339 }
340
341 int
342 ExecCountSlotsGroup(Group *node)
343 {
344         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
345 }
346
347 /* ------------------------
348  *              ExecEndGroup(node)
349  *
350  * -----------------------
351  */
352 void
353 ExecEndGroup(Group *node)
354 {
355         GroupState *grpstate;
356         Plan       *outerPlan;
357
358         grpstate = node->grpstate;
359
360         ExecFreeProjectionInfo(&grpstate->csstate.cstate);
361         ExecFreeExprContext(&grpstate->csstate.cstate);
362
363         outerPlan = outerPlan(node);
364         ExecEndNode(outerPlan, (Plan *) node);
365
366         /* clean up tuple table */
367         ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
368         if (grpstate->grp_firstTuple != NULL)
369         {
370                 heap_freetuple(grpstate->grp_firstTuple);
371                 grpstate->grp_firstTuple = NULL;
372         }
373 }
374
375 void
376 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
377 {
378         GroupState *grpstate = node->grpstate;
379
380         grpstate->grp_useFirstTuple = FALSE;
381         grpstate->grp_done = FALSE;
382         if (grpstate->grp_firstTuple != NULL)
383         {
384                 heap_freetuple(grpstate->grp_firstTuple);
385                 grpstate->grp_firstTuple = NULL;
386         }
387
388         if (((Plan *) node)->lefttree &&
389                 ((Plan *) node)->lefttree->chgParam == NULL)
390                 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
391 }
392
393 /*****************************************************************************
394  *              Code shared with nodeUnique.c
395  *****************************************************************************/
396
397 /*
398  * execTuplesMatch
399  *              Return true if two tuples match in all the indicated fields.
400  *              This is used to detect group boundaries in nodeGroup, and to
401  *              decide whether two tuples are distinct or not in nodeUnique.
402  *
403  * tuple1, tuple2: the tuples to compare
404  * tupdesc: tuple descriptor applying to both tuples
405  * numCols: the number of attributes to be examined
406  * matchColIdx: array of attribute column numbers
407  * eqFunctions: array of fmgr lookup info for the equality functions to use
408  * evalContext: short-term memory context for executing the functions
409  *
410  * NB: evalContext is reset each time!
411  */
412 bool
413 execTuplesMatch(HeapTuple tuple1,
414                                 HeapTuple tuple2,
415                                 TupleDesc tupdesc,
416                                 int numCols,
417                                 AttrNumber *matchColIdx,
418                                 FmgrInfo *eqfunctions,
419                                 MemoryContext evalContext)
420 {
421         MemoryContext oldContext;
422         bool            result;
423         int                     i;
424
425         /* Reset and switch into the temp context. */
426         MemoryContextReset(evalContext);
427         oldContext = MemoryContextSwitchTo(evalContext);
428
429         /*
430          * We cannot report a match without checking all the fields, but we
431          * can report a non-match as soon as we find unequal fields.  So,
432          * start comparing at the last field (least significant sort key).
433          * That's the most likely to be different...
434          */
435         result = true;
436
437         for (i = numCols; --i >= 0;)
438         {
439                 AttrNumber      att = matchColIdx[i];
440                 Datum           attr1,
441                                         attr2;
442                 bool            isNull1,
443                                         isNull2;
444
445                 attr1 = heap_getattr(tuple1,
446                                                          att,
447                                                          tupdesc,
448                                                          &isNull1);
449
450                 attr2 = heap_getattr(tuple2,
451                                                          att,
452                                                          tupdesc,
453                                                          &isNull2);
454
455                 if (isNull1 != isNull2)
456                 {
457                         result = false;         /* one null and one not; they aren't equal */
458                         break;
459                 }
460
461                 if (isNull1)
462                         continue;                       /* both are null, treat as equal */
463
464                 /* Apply the type-specific equality function */
465
466                 if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
467                                                                                  attr1, attr2)))
468                 {
469                         result = false;         /* they aren't equal */
470                         break;
471                 }
472         }
473
474         MemoryContextSwitchTo(oldContext);
475
476         return result;
477 }
478
479 /*
480  * execTuplesMatchPrepare
481  *              Look up the equality functions needed for execTuplesMatch.
482  *              The result is a palloc'd array.
483  */
484 FmgrInfo   *
485 execTuplesMatchPrepare(TupleDesc tupdesc,
486                                            int numCols,
487                                            AttrNumber *matchColIdx)
488 {
489         FmgrInfo   *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
490         int                     i;
491
492         for (i = 0; i < numCols; i++)
493         {
494                 AttrNumber      att = matchColIdx[i];
495                 Oid                     typid = tupdesc->attrs[att - 1]->atttypid;
496                 Operator        eq_operator;
497                 Form_pg_operator pgopform;
498
499                 eq_operator = oper("=", typid, typid, true);
500                 if (!HeapTupleIsValid(eq_operator))
501                 {
502                         elog(ERROR, "Unable to identify an equality operator for type '%s'",
503                                  typeidTypeName(typid));
504                 }
505                 pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
506                 fmgr_info(pgopform->oprcode, &eqfunctions[i]);
507         }
508
509         return eqfunctions;
510 }