]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeAgg.c
Change elog(WARN) to elog(ERROR) and elog(ABORT).
[postgresql] / src / backend / executor / nodeAgg.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeAgg.c--
4  *        Routines to handle aggregate nodes.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * NOTE
10  *        The implementation of Agg node has been reworked to handle legal
11  *        SQL aggregates. (Do not expect POSTQUEL semantics.)    -- ay 2/95
12  *
13  * IDENTIFICATION
14  *        /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include <string.h>
19
20 #include "postgres.h"
21 #include "fmgr.h"
22
23 #include "access/heapam.h"
24 #include "catalog/pg_aggregate.h"
25 #include "catalog/catalog.h"
26 #include "parser/parse_type.h"
27 #include "executor/executor.h"
28 #include "executor/nodeAgg.h"
29 #include "storage/bufmgr.h"
30 #include "utils/palloc.h"
31 #include "utils/syscache.h"
32
33 /*
34  * AggFuncInfo -
35  *        keeps the transition functions information around
36  */
37 typedef struct AggFuncInfo
38 {
39         Oid                     xfn1_oid;
40         Oid                     xfn2_oid;
41         Oid                     finalfn_oid;
42         func_ptr        xfn1;
43         func_ptr        xfn2;
44         func_ptr        finalfn;
45         int                     xfn1_nargs;
46         int                     xfn2_nargs;
47         int                     finalfn_nargs;
48 } AggFuncInfo;
49
50 static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull);
51
52
53 /* ---------------------------------------
54  *
55  * ExecAgg -
56  *
57  *        ExecAgg receives tuples from its outer subplan and aggregates over
58  *        the appropriate attribute for each (unique) aggregate in the target
59  *        list. (The number of tuples to aggregate over depends on whether a
60  *        GROUP BY clause is present. It might be the number of tuples in a
61  *        group or all the tuples that satisfy the qualifications.) The value of
62  *        each aggregate is stored in the expression context for ExecProject to
63  *        evaluate the result tuple.
64  *
65  *        ExecAgg evaluates each aggregate in the following steps: (initcond1,
66  *        initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
67  *        the transition functions.)
68  *
69  *               value1[i] = initcond1
70  *               value2[i] = initcond2
71  *               forall tuples do
72  *                      value1[i] = sfunc1(aggregate_attribute, value1[i])
73  *                      value2[i] = sfunc2(value2[i])
74  *               value1[i] = finalfunc(value1[i], value2[i])
75  *
76  *        If the outer subplan is a Group node, ExecAgg returns as many tuples
77  *        as there are groups.
78  *
79  *        XXX handling of NULL doesn't work
80  *
81  *        OLD COMMENTS
82  *
83  *              XXX Aggregates should probably have another option: what to do
84  *              with transfn2 if we hit a null value.  "count" (transfn1 = null,
85  *              transfn2 = increment) will want to have transfn2 called; "avg"
86  *              (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93
87  *
88  * ------------------------------------------
89  */
90 TupleTableSlot *
91 ExecAgg(Agg *node)
92 {
93         AggState   *aggstate;
94         EState     *estate;
95         Aggreg    **aggregates;
96         Plan       *outerPlan;
97         int                     i,
98                                 nagg;
99         Datum      *value1,
100                            *value2;
101         int                *noInitValue;
102         AggFuncInfo *aggFuncInfo;
103         long            nTuplesAgged = 0;
104         ExprContext *econtext;
105         ProjectionInfo *projInfo;
106         TupleTableSlot *resultSlot;
107         HeapTuple       oneTuple;
108         char       *nulls;
109         bool            isDone;
110         bool            isNull = FALSE,
111                                 isNull1 = FALSE,
112                                 isNull2 = FALSE;
113
114         /* ---------------------
115          *      get state info from node
116          * ---------------------
117          */
118         aggstate = node->aggstate;
119         if (aggstate->agg_done)
120                 return NULL;
121
122         estate = node->plan.state;
123         econtext = aggstate->csstate.cstate.cs_ExprContext;
124         aggregates = node->aggs;
125         nagg = node->numAgg;
126
127         value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
128         nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
129
130         value2 = (Datum *) palloc(sizeof(Datum) * nagg);
131         MemSet(value2, 0, sizeof(Datum) * nagg);
132
133         aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg);
134         MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
135
136         noInitValue = (int *) palloc(sizeof(int) * nagg);
137         MemSet(noInitValue, 0, sizeof(noInitValue) * nagg);
138
139         outerPlan = outerPlan(node);
140         oneTuple = NULL;
141
142         projInfo = aggstate->csstate.cstate.cs_ProjInfo;
143
144         for (i = 0; i < nagg; i++)
145         {
146                 Aggreg     *agg;
147                 char       *aggname;
148                 HeapTuple       aggTuple;
149                 Form_pg_aggregate aggp;
150                 Oid                     xfn1_oid,
151                                         xfn2_oid,
152                                         finalfn_oid;
153                 func_ptr        xfn1_ptr,
154                                         xfn2_ptr,
155                                         finalfn_ptr;
156                 int                     xfn1_nargs,
157                                         xfn2_nargs,
158                                         finalfn_nargs;
159
160                 agg = aggregates[i];
161
162                 /* ---------------------
163                  *      find transfer functions of all the aggregates and initialize
164                  *      their initial values
165                  * ---------------------
166                  */
167                 aggname = agg->aggname;
168                 aggTuple = SearchSysCacheTuple(AGGNAME,
169                                                                            PointerGetDatum(aggname),
170                                                                            ObjectIdGetDatum(agg->basetype),
171                                                                            0, 0);
172                 if (!HeapTupleIsValid(aggTuple))
173                         elog(ABORT, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
174                                  aggname,
175                                  typeidTypeName(agg->basetype));
176                 aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
177
178                 xfn1_oid = aggp->aggtransfn1;
179                 xfn2_oid = aggp->aggtransfn2;
180                 finalfn_oid = aggp->aggfinalfn;
181
182                 if (OidIsValid(finalfn_oid))
183                 {
184                         fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs);
185                         aggFuncInfo[i].finalfn_oid = finalfn_oid;
186                         aggFuncInfo[i].finalfn = finalfn_ptr;
187                         aggFuncInfo[i].finalfn_nargs = finalfn_nargs;
188                 }
189
190                 if (OidIsValid(xfn2_oid))
191                 {
192                         fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs);
193                         aggFuncInfo[i].xfn2_oid = xfn2_oid;
194                         aggFuncInfo[i].xfn2 = xfn2_ptr;
195                         aggFuncInfo[i].xfn2_nargs = xfn2_nargs;
196                         value2[i] = (Datum) AggNameGetInitVal((char *) aggname,
197                                                                                                   aggp->aggbasetype,
198                                                                                                   2,
199                                                                                                   &isNull2);
200                         /* ------------------------------------------
201                          * If there is a second transition function, its initial
202                          * value must exist -- as it does not depend on data values,
203                          * we have no other way of determining an initial value.
204                          * ------------------------------------------
205                          */
206                         if (isNull2)
207                                 elog(ABORT, "ExecAgg: agginitval2 is null");
208                 }
209
210                 if (OidIsValid(xfn1_oid))
211                 {
212                         fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs);
213                         aggFuncInfo[i].xfn1_oid = xfn1_oid;
214                         aggFuncInfo[i].xfn1 = xfn1_ptr;
215                         aggFuncInfo[i].xfn1_nargs = xfn1_nargs;
216                         value1[i] = (Datum) AggNameGetInitVal((char *) aggname,
217                                                                                                   aggp->aggbasetype,
218                                                                                                   1,
219                                                                                                   &isNull1);
220
221                         /* ------------------------------------------
222                          * If the initial value for the first transition function
223                          * doesn't exist in the pg_aggregate table then we let
224                          * the first value returned from the outer procNode become
225                          * the initial value. (This is useful for aggregates like
226                          * max{} and min{}.)
227                          * ------------------------------------------
228                          */
229                         if (isNull1)
230                         {
231                                 noInitValue[i] = 1;
232                                 nulls[i] = 1;
233                         }
234                 }
235         }
236
237         /* ----------------
238          *       for each tuple from the the outer plan, apply all the aggregates
239          * ----------------
240          */
241         for (;;)
242         {
243                 HeapTuple       outerTuple = NULL;
244                 TupleTableSlot *outerslot;
245
246                 isNull = isNull1 = isNull2 = 0;
247                 outerslot = ExecProcNode(outerPlan, (Plan *) node);
248                 if (outerslot)
249                         outerTuple = outerslot->val;
250                 if (!HeapTupleIsValid(outerTuple))
251                 {
252
253                         /*
254                          * when the outerplan doesn't return a single tuple, create a
255                          * dummy heaptuple anyway because we still need to return a
256                          * valid aggregate value. The value returned will be the
257                          * initial values of the transition functions
258                          */
259                         if (nTuplesAgged == 0)
260                         {
261                                 TupleDesc       tupType;
262                                 Datum      *tupValue;
263                                 char       *null_array;
264
265                                 tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
266                                 tupValue = projInfo->pi_tupValue;
267
268                                 /* initially, set all the values to NULL */
269                                 null_array = palloc(tupType->natts);
270                                 for (i = 0; i < tupType->natts; i++)
271                                         null_array[i] = 'n';
272                                 oneTuple = heap_formtuple(tupType, tupValue, null_array);
273                                 pfree(null_array);
274                         }
275                         break;
276                 }
277
278                 for (i = 0; i < nagg; i++)
279                 {
280                         AttrNumber      attnum;
281                         int2            attlen = 0;
282                         Datum           newVal = (Datum) NULL;
283                         AggFuncInfo *aggfns = &aggFuncInfo[i];
284                         Datum           args[2];
285                         Node       *tagnode = NULL;
286
287                         switch (nodeTag(aggregates[i]->target))
288                         {
289                                 case T_Var:
290                                         tagnode = NULL;
291                                         newVal = aggGetAttr(outerslot,
292                                                                                 aggregates[i],
293                                                                                 &isNull);
294                                         break;
295                                 case T_Expr:
296                                         tagnode = ((Expr *) aggregates[i]->target)->oper;
297                                         econtext->ecxt_scantuple = outerslot;
298                                         newVal = ExecEvalExpr(aggregates[i]->target, econtext,
299                                                                                   &isNull, &isDone);
300                                         break;
301                                 case T_Const:
302                                         tagnode = NULL;
303                                         econtext->ecxt_scantuple = outerslot;
304                                         newVal = ExecEvalExpr(aggregates[i]->target, econtext,
305                                                                                   &isNull, &isDone);
306                                         break;
307                                 default:
308                                         elog(ABORT, "ExecAgg: Bad Agg->Target for Agg %d", i);
309                         }
310
311                         if (isNull && !aggregates[i]->usenulls)
312                                 continue;               /* ignore this tuple for this agg */
313
314                         if (aggfns->xfn1)
315                         {
316                                 if (noInitValue[i])
317                                 {
318                                         int                     byVal = 0;
319
320                                         /*
321                                          * value1 and value2 has not been initialized. This is
322                                          * the first non-NULL value. We use it as the initial
323                                          * value.
324                                          */
325
326                                         /*
327                                          * but we can't just use it straight, we have to make
328                                          * a copy of it since the tuple from which it came
329                                          * will be freed on the next iteration of the scan
330                                          */
331                                         switch (nodeTag(aggregates[i]->target))
332                                         {
333                                                 case T_Var:
334                                                         attnum = ((Var *) aggregates[i]->target)->varattno;
335                                                         attlen = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attlen;
336                                                         byVal = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attbyval;
337
338                                                         break;
339                                                 case T_Expr:
340                                                 {
341                                                         FunctionCachePtr fcache_ptr;
342                 
343                                                         if (nodeTag(tagnode) == T_Func)
344                                                                 fcache_ptr = ((Func *) tagnode)->func_fcache;
345                                                         else
346                                                                 fcache_ptr = ((Oper *) tagnode)->op_fcache;
347                                                         attlen = fcache_ptr->typlen;
348                                                         byVal = fcache_ptr->typbyval;
349
350                                                         break;
351                                                 }
352                                                 case T_Const:
353                                                         attlen = ((Const *) aggregates[i]->target)->constlen;
354                                                         byVal = ((Const *) aggregates[i]->target)->constbyval;
355
356                                                         break;
357                                                 default:
358                                                         elog(ABORT, "ExecAgg: Bad Agg->Target for Agg %d", i);
359                                         }
360                                         if (attlen == -1)
361                                         {
362                                                 /* variable length */
363                                                 attlen = VARSIZE((struct varlena *) newVal);
364                                         }
365                                         value1[i] = (Datum) palloc(attlen);
366                                         if (byVal)
367                                                 value1[i] = newVal;
368                                         else
369                                                 memmove((char *) (value1[i]), (char *) newVal, attlen);
370                                         noInitValue[i] = 0;
371                                         nulls[i] = 0;
372                                 }
373                                 else
374                                 {
375
376                                         /*
377                                          * apply the transition functions.
378                                          */
379                                         args[0] = value1[i];
380                                         args[1] = newVal;
381                                         value1[i] =
382                                                 (Datum) fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
383                                                                  aggfns->xfn1_nargs, (FmgrValues *) args,
384                                                                            &isNull1);
385                                         Assert(!isNull1);
386                                 }
387                         }
388
389                         if (aggfns->xfn2)
390                         {
391                                 Datum           xfn2_val = value2[i];
392
393                                 value2[i] =
394                                         (Datum) fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
395                                                                    aggfns->xfn2_nargs,
396                                                                    (FmgrValues *) &xfn2_val, &isNull2);
397                                 Assert(!isNull2);
398                         }
399                 }
400
401                 /*
402                  * keep this for the projection (we only need one of these - all
403                  * the tuples we aggregate over share the same group column)
404                  */
405                 if (!oneTuple)
406                 {
407                         oneTuple = heap_copytuple(outerslot->val);
408                 }
409
410                 nTuplesAgged++;
411         }
412
413         /* --------------
414          * finalize the aggregate (if necessary), and get the resultant value
415          * --------------
416          */
417         for (i = 0; i < nagg; i++)
418         {
419                 char       *args[2];
420                 AggFuncInfo *aggfns = &aggFuncInfo[i];
421
422                 if (noInitValue[i])
423                 {
424
425                         /*
426                          * No values found for this agg; return current state. This
427                          * seems to fix behavior for avg() aggregate. -tgl 12/96
428                          */
429                 }
430                 else if (aggfns->finalfn && nTuplesAgged > 0)
431                 {
432                         if (aggfns->finalfn_nargs > 1)
433                         {
434                                 args[0] = (char *) value1[i];
435                                 args[1] = (char *) value2[i];
436                         }
437                         else if (aggfns->xfn1)
438                         {
439                                 args[0] = (char *) value1[i];
440                         }
441                         else if (aggfns->xfn2)
442                         {
443                                 args[0] = (char *) value2[i];
444                         }
445                         else
446                                 elog(ABORT, "ExecAgg: no valid transition functions??");
447                         value1[i] =
448                                 (Datum) fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
449                                                            aggfns->finalfn_nargs, (FmgrValues *) args,
450                                                            &(nulls[i]));
451                 }
452                 else if (aggfns->xfn1)
453                 {
454
455                         /*
456                          * value in the right place, ignore. (If you remove this case,
457                          * fix the else part. -ay 2/95)
458                          */
459                 }
460                 else if (aggfns->xfn2)
461                 {
462                         value1[i] = value2[i];
463                 }
464                 else
465                         elog(ABORT, "ExecAgg: no valid transition functions??");
466         }
467
468         /*
469          * whether the aggregation is done depends on whether we are doing
470          * aggregation over groups or the entire table
471          */
472         if (nodeTag(outerPlan) == T_Group)
473         {
474                 /* aggregation over groups */
475                 aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
476         }
477         else
478         {
479                 aggstate->agg_done = TRUE;
480         }
481
482         /* ----------------
483          *      form a projection tuple, store it in the result tuple
484          *      slot and return it.
485          * ----------------
486          */
487         ExecStoreTuple(oneTuple,
488                                    aggstate->csstate.css_ScanTupleSlot,
489                                    InvalidBuffer,
490                                    false);
491         econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
492         resultSlot = ExecProject(projInfo, &isDone);
493
494         if (oneTuple)
495                 pfree(oneTuple);
496
497         return resultSlot;
498 }
499
500 /* -----------------
501  * ExecInitAgg
502  *
503  *      Creates the run-time information for the agg node produced by the
504  *      planner and initializes its outer subtree
505  * -----------------
506  */
507 bool
508 ExecInitAgg(Agg *node, EState *estate, Plan *parent)
509 {
510         AggState   *aggstate;
511         Plan       *outerPlan;
512         ExprContext *econtext;
513
514         /*
515          * assign the node's execution state
516          */
517         node->plan.state = estate;
518
519         /*
520          * create state structure
521          */
522         aggstate = makeNode(AggState);
523         node->aggstate = aggstate;
524         aggstate->agg_done = FALSE;
525
526         /*
527          * assign node's base id and create expression context
528          */
529         ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
530                                                    (Plan *) parent);
531         ExecAssignExprContext(estate, &aggstate->csstate.cstate);
532
533 #define AGG_NSLOTS 2
534
535         /*
536          * tuple table initialization
537          */
538         ExecInitScanTupleSlot(estate, &aggstate->csstate);
539         ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
540
541         econtext = aggstate->csstate.cstate.cs_ExprContext;
542         econtext->ecxt_values =
543                 (Datum *) palloc(sizeof(Datum) * node->numAgg);
544         MemSet(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg);
545         econtext->ecxt_nulls = (char *) palloc(node->numAgg);
546         MemSet(econtext->ecxt_nulls, 0, node->numAgg);
547
548         /*
549          * initializes child nodes
550          */
551         outerPlan = outerPlan(node);
552         ExecInitNode(outerPlan, estate, (Plan *) node);
553
554         /*
555          *      Result runs in its own context, but make it use our aggregates
556          *      fix for 'select sum(2+2)'
557          */
558         if (nodeTag(outerPlan) == T_Result)
559         {
560                 ((Result *)outerPlan)->resstate->cstate.cs_ProjInfo->pi_exprContext->ecxt_values =
561                                                                                                 econtext->ecxt_values;
562                 ((Result *)outerPlan)->resstate->cstate.cs_ProjInfo->pi_exprContext->ecxt_nulls =
563                                                                                                 econtext->ecxt_nulls;
564         }
565
566
567         /* ----------------
568          *      initialize tuple type.
569          * ----------------
570          */
571         ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
572
573         /*
574          * Initialize tuple type for both result and scan. This node does no
575          * projection
576          */
577         ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate);
578         ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate);
579
580         return TRUE;
581 }
582
583 int
584 ExecCountSlotsAgg(Agg *node)
585 {
586         return ExecCountSlotsNode(outerPlan(node)) +
587         ExecCountSlotsNode(innerPlan(node)) +
588         AGG_NSLOTS;
589 }
590
591 /* ------------------------
592  *              ExecEndAgg(node)
593  *
594  * -----------------------
595  */
596 void
597 ExecEndAgg(Agg *node)
598 {
599         AggState   *aggstate;
600         Plan       *outerPlan;
601
602         aggstate = node->aggstate;
603
604         ExecFreeProjectionInfo(&aggstate->csstate.cstate);
605
606         outerPlan = outerPlan(node);
607         ExecEndNode(outerPlan, (Plan *) node);
608
609         /* clean up tuple table */
610         ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
611 }
612
613
614 /*****************************************************************************
615  *      Support Routines
616  *****************************************************************************/
617
618 /*
619  * aggGetAttr -
620  *        get the attribute (specified in the Var node in agg) to aggregate
621  *        over from the tuple
622  */
623 static Datum
624 aggGetAttr(TupleTableSlot *slot,
625                    Aggreg *agg,
626                    bool *isNull)
627 {
628         Datum           result;
629         AttrNumber      attnum;
630         HeapTuple       heapTuple;
631         TupleDesc       tuple_type;
632         Buffer          buffer;
633
634         /* ----------------
635          *       extract tuple information from the slot
636          * ----------------
637          */
638         heapTuple = slot->val;
639         tuple_type = slot->ttc_tupleDescriptor;
640         buffer = slot->ttc_buffer;
641
642         attnum = ((Var *) agg->target)->varattno;
643
644         /*
645          * If the attribute number is invalid, then we are supposed to return
646          * the entire tuple, we give back a whole slot so that callers know
647          * what the tuple looks like.
648          */
649         if (attnum == InvalidAttrNumber)
650         {
651                 TupleTableSlot *tempSlot;
652                 TupleDesc       td;
653                 HeapTuple       tup;
654
655                 tempSlot = makeNode(TupleTableSlot);
656                 tempSlot->ttc_shouldFree = false;
657                 tempSlot->ttc_descIsNew = true;
658                 tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL,
659                         tempSlot->ttc_buffer = InvalidBuffer;
660                 tempSlot->ttc_whichplan = -1;
661
662                 tup = heap_copytuple(slot->val);
663                 td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
664
665                 ExecSetSlotDescriptor(tempSlot, td);
666
667                 ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
668                 return (Datum) tempSlot;
669         }
670
671         result = 
672                 heap_getattr(heapTuple, /* tuple containing attribute */
673                                          buffer,        /* buffer associated with tuple */
674                                          attnum,        /* attribute number of desired attribute */
675                                          tuple_type,/* tuple descriptor of tuple */
676                                          isNull);       /* return: is attribute null? */
677
678         /* ----------------
679          *      return null if att is null
680          * ----------------
681          */
682         if (*isNull)
683                 return (Datum) NULL;
684
685         return result;
686 }