1 /*-------------------------------------------------------------------------
4 * Routines to handle aggregate nodes.
6 * Copyright (c) 1994, Regents of the University of California
10 * The implementation of Agg node has been reworked to handle legal
11 * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95
14 * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
16 *-------------------------------------------------------------------------
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"
35 * keeps the transition functions information around
37 typedef struct AggFuncInfo
50 static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull);
53 /* ---------------------------------------
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.
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.)
69 * value1[i] = initcond1
70 * value2[i] = initcond2
72 * value1[i] = sfunc1(aggregate_attribute, value1[i])
73 * value2[i] = sfunc2(value2[i])
74 * value1[i] = finalfunc(value1[i], value2[i])
76 * If the outer subplan is a Group node, ExecAgg returns as many tuples
77 * as there are groups.
79 * XXX handling of NULL doesn't work
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
88 * ------------------------------------------
102 AggFuncInfo *aggFuncInfo;
103 long nTuplesAgged = 0;
104 ExprContext *econtext;
105 ProjectionInfo *projInfo;
106 TupleTableSlot *resultSlot;
114 /* ---------------------
115 * get state info from node
116 * ---------------------
118 aggstate = node->aggstate;
119 if (aggstate->agg_done)
122 estate = node->plan.state;
123 econtext = aggstate->csstate.cstate.cs_ExprContext;
124 aggregates = node->aggs;
127 value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
128 nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
130 value2 = (Datum *) palloc(sizeof(Datum) * nagg);
131 MemSet(value2, 0, sizeof(Datum) * nagg);
133 aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg);
134 MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
136 noInitValue = (int *) palloc(sizeof(int) * nagg);
137 MemSet(noInitValue, 0, sizeof(noInitValue) * nagg);
139 outerPlan = outerPlan(node);
142 projInfo = aggstate->csstate.cstate.cs_ProjInfo;
144 for (i = 0; i < nagg; i++)
149 Form_pg_aggregate aggp;
162 /* ---------------------
163 * find transfer functions of all the aggregates and initialize
164 * their initial values
165 * ---------------------
167 aggname = agg->aggname;
168 aggTuple = SearchSysCacheTuple(AGGNAME,
169 PointerGetDatum(aggname),
170 ObjectIdGetDatum(agg->basetype),
172 if (!HeapTupleIsValid(aggTuple))
173 elog(ERROR, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
175 typeidTypeName(agg->basetype));
176 aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
178 xfn1_oid = aggp->aggtransfn1;
179 xfn2_oid = aggp->aggtransfn2;
180 finalfn_oid = aggp->aggfinalfn;
182 if (OidIsValid(finalfn_oid))
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;
190 if (OidIsValid(xfn2_oid))
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,
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 * ------------------------------------------
207 elog(ERROR, "ExecAgg: agginitval2 is null");
210 if (OidIsValid(xfn1_oid))
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,
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
227 * ------------------------------------------
238 * for each tuple from the the outer plan, apply all the aggregates
243 HeapTuple outerTuple = NULL;
244 TupleTableSlot *outerslot;
246 isNull = isNull1 = isNull2 = 0;
247 outerslot = ExecProcNode(outerPlan, (Plan *) node);
249 outerTuple = outerslot->val;
250 if (!HeapTupleIsValid(outerTuple))
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
259 if (nTuplesAgged == 0)
265 tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
266 tupValue = projInfo->pi_tupValue;
268 /* initially, set all the values to NULL */
269 null_array = palloc(tupType->natts);
270 for (i = 0; i < tupType->natts; i++)
272 oneTuple = heap_formtuple(tupType, tupValue, null_array);
278 for (i = 0; i < nagg; i++)
282 Datum newVal = (Datum) NULL;
283 AggFuncInfo *aggfns = &aggFuncInfo[i];
285 Node *tagnode = NULL;
287 switch (nodeTag(aggregates[i]->target))
291 newVal = aggGetAttr(outerslot,
296 tagnode = ((Expr *) aggregates[i]->target)->oper;
297 econtext->ecxt_scantuple = outerslot;
298 newVal = ExecEvalExpr(aggregates[i]->target, econtext,
303 econtext->ecxt_scantuple = outerslot;
304 newVal = ExecEvalExpr(aggregates[i]->target, econtext,
308 elog(ERROR, "ExecAgg: Bad Agg->Target for Agg %d", i);
311 if (isNull && !aggregates[i]->usenulls)
312 continue; /* ignore this tuple for this agg */
321 * value1 and value2 has not been initialized. This is
322 * the first non-NULL value. We use it as the initial
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
331 switch (nodeTag(aggregates[i]->target))
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;
341 FunctionCachePtr fcache_ptr;
343 if (nodeTag(tagnode) == T_Func)
344 fcache_ptr = ((Func *) tagnode)->func_fcache;
346 fcache_ptr = ((Oper *) tagnode)->op_fcache;
347 attlen = fcache_ptr->typlen;
348 byVal = fcache_ptr->typbyval;
353 attlen = ((Const *) aggregates[i]->target)->constlen;
354 byVal = ((Const *) aggregates[i]->target)->constbyval;
358 elog(ERROR, "ExecAgg: Bad Agg->Target for Agg %d", i);
362 /* variable length */
363 attlen = VARSIZE((struct varlena *) newVal);
365 value1[i] = (Datum) palloc(attlen);
369 memmove((char *) (value1[i]), (char *) newVal, attlen);
377 * apply the transition functions.
382 (Datum) fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
383 aggfns->xfn1_nargs, (FmgrValues *) args,
391 Datum xfn2_val = value2[i];
394 (Datum) fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
396 (FmgrValues *) &xfn2_val, &isNull2);
402 * keep this for the projection (we only need one of these - all
403 * the tuples we aggregate over share the same group column)
407 oneTuple = heap_copytuple(outerslot->val);
414 * finalize the aggregate (if necessary), and get the resultant value
417 for (i = 0; i < nagg; i++)
420 AggFuncInfo *aggfns = &aggFuncInfo[i];
426 * No values found for this agg; return current state. This
427 * seems to fix behavior for avg() aggregate. -tgl 12/96
430 else if (aggfns->finalfn && nTuplesAgged > 0)
432 if (aggfns->finalfn_nargs > 1)
434 args[0] = (char *) value1[i];
435 args[1] = (char *) value2[i];
437 else if (aggfns->xfn1)
439 args[0] = (char *) value1[i];
441 else if (aggfns->xfn2)
443 args[0] = (char *) value2[i];
446 elog(ERROR, "ExecAgg: no valid transition functions??");
448 (Datum) fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
449 aggfns->finalfn_nargs, (FmgrValues *) args,
452 else if (aggfns->xfn1)
456 * value in the right place, ignore. (If you remove this case,
457 * fix the else part. -ay 2/95)
460 else if (aggfns->xfn2)
462 value1[i] = value2[i];
465 elog(ERROR, "ExecAgg: no valid transition functions??");
469 * whether the aggregation is done depends on whether we are doing
470 * aggregation over groups or the entire table
472 if (nodeTag(outerPlan) == T_Group)
474 /* aggregation over groups */
475 aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
479 aggstate->agg_done = TRUE;
483 * form a projection tuple, store it in the result tuple
484 * slot and return it.
487 ExecStoreTuple(oneTuple,
488 aggstate->csstate.css_ScanTupleSlot,
491 econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
492 resultSlot = ExecProject(projInfo, &isDone);
503 * Creates the run-time information for the agg node produced by the
504 * planner and initializes its outer subtree
508 ExecInitAgg(Agg *node, EState *estate, Plan *parent)
512 ExprContext *econtext;
515 * assign the node's execution state
517 node->plan.state = estate;
520 * create state structure
522 aggstate = makeNode(AggState);
523 node->aggstate = aggstate;
524 aggstate->agg_done = FALSE;
527 * assign node's base id and create expression context
529 ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
531 ExecAssignExprContext(estate, &aggstate->csstate.cstate);
536 * tuple table initialization
538 ExecInitScanTupleSlot(estate, &aggstate->csstate);
539 ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
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);
549 * initializes child nodes
551 outerPlan = outerPlan(node);
552 ExecInitNode(outerPlan, estate, (Plan *) node);
555 * Result runs in its own context, but make it use our aggregates
556 * fix for 'select sum(2+2)'
558 if (nodeTag(outerPlan) == T_Result)
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;
568 * initialize tuple type.
571 ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
574 * Initialize tuple type for both result and scan. This node does no
577 ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate);
578 ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate);
584 ExecCountSlotsAgg(Agg *node)
586 return ExecCountSlotsNode(outerPlan(node)) +
587 ExecCountSlotsNode(innerPlan(node)) +
591 /* ------------------------
594 * -----------------------
597 ExecEndAgg(Agg *node)
602 aggstate = node->aggstate;
604 ExecFreeProjectionInfo(&aggstate->csstate.cstate);
606 outerPlan = outerPlan(node);
607 ExecEndNode(outerPlan, (Plan *) node);
609 /* clean up tuple table */
610 ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
614 /*****************************************************************************
616 *****************************************************************************/
620 * get the attribute (specified in the Var node in agg) to aggregate
621 * over from the tuple
624 aggGetAttr(TupleTableSlot *slot,
631 TupleDesc tuple_type;
635 * extract tuple information from the slot
638 heapTuple = slot->val;
639 tuple_type = slot->ttc_tupleDescriptor;
640 buffer = slot->ttc_buffer;
642 attnum = ((Var *) agg->target)->varattno;
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.
649 if (attnum == InvalidAttrNumber)
651 TupleTableSlot *tempSlot;
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;
662 tup = heap_copytuple(slot->val);
663 td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
665 ExecSetSlotDescriptor(tempSlot, td);
667 ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
668 return (Datum) tempSlot;
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? */
679 * return null if att is null