aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeAgg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeAgg.c')
-rw-r--r--src/backend/executor/nodeAgg.c1055
1 files changed, 555 insertions, 500 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index c6e5b269cde..ee03f6854d9 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* nodeAgg.c--
- * Routines to handle aggregate nodes.
+ * Routines to handle aggregate nodes.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* NOTE
- * The implementation of Agg node has been reworked to handle legal
- * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95
+ * The implementation of Agg node has been reworked to handle legal
+ * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95
*
* IDENTIFICATION
- * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
+ * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
*
*-------------------------------------------------------------------------
*/
@@ -32,570 +32,625 @@
/*
* AggFuncInfo -
- * keeps the transition functions information around
+ * keeps the transition functions information around
*/
-typedef struct AggFuncInfo {
- Oid xfn1_oid;
- Oid xfn2_oid;
- Oid finalfn_oid;
- func_ptr xfn1;
- func_ptr xfn2;
- func_ptr finalfn;
- int xfn1_nargs;
- int xfn2_nargs;
- int finalfn_nargs;
-} AggFuncInfo;
+typedef struct AggFuncInfo
+{
+ Oid xfn1_oid;
+ Oid xfn2_oid;
+ Oid finalfn_oid;
+ func_ptr xfn1;
+ func_ptr xfn2;
+ func_ptr finalfn;
+ int xfn1_nargs;
+ int xfn2_nargs;
+ int finalfn_nargs;
+} AggFuncInfo;
-static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull);
+static Datum aggGetAttr(TupleTableSlot * tuple, Aggreg * agg, bool * isNull);
/* ---------------------------------------
*
* ExecAgg -
*
- * ExecAgg receives tuples from its outer subplan and aggregates over
- * the appropriate attribute for each (unique) aggregate in the target
- * list. (The number of tuples to aggregate over depends on whether a
- * GROUP BY clause is present. It might be the number of tuples in a
- * group or all the tuples that satisfy the qualifications.) The value of
- * each aggregate is stored in the expression context for ExecProject to
- * evaluate the result tuple.
- *
- * ExecAgg evaluates each aggregate in the following steps: (initcond1,
- * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
- * the transition functions.)
+ * ExecAgg receives tuples from its outer subplan and aggregates over
+ * the appropriate attribute for each (unique) aggregate in the target
+ * list. (The number of tuples to aggregate over depends on whether a
+ * GROUP BY clause is present. It might be the number of tuples in a
+ * group or all the tuples that satisfy the qualifications.) The value of
+ * each aggregate is stored in the expression context for ExecProject to
+ * evaluate the result tuple.
+ *
+ * ExecAgg evaluates each aggregate in the following steps: (initcond1,
+ * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
+ * the transition functions.)
*
- * value1[i] = initcond1
- * value2[i] = initcond2
- * forall tuples do
- * value1[i] = sfunc1(aggregate_attribute, value1[i])
- * value2[i] = sfunc2(value2[i])
- * value1[i] = finalfunc(value1[i], value2[i])
+ * value1[i] = initcond1
+ * value2[i] = initcond2
+ * forall tuples do
+ * value1[i] = sfunc1(aggregate_attribute, value1[i])
+ * value2[i] = sfunc2(value2[i])
+ * value1[i] = finalfunc(value1[i], value2[i])
*
- * If the outer subplan is a Group node, ExecAgg returns as many tuples
- * as there are groups.
+ * If the outer subplan is a Group node, ExecAgg returns as many tuples
+ * as there are groups.
*
- * XXX handling of NULL doesn't work
+ * XXX handling of NULL doesn't work
*
- * OLD COMMENTS
+ * OLD COMMENTS
*
- * XXX Aggregates should probably have another option: what to do
- * with transfn2 if we hit a null value. "count" (transfn1 = null,
- * transfn2 = increment) will want to have transfn2 called; "avg"
- * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93
+ * XXX Aggregates should probably have another option: what to do
+ * with transfn2 if we hit a null value. "count" (transfn1 = null,
+ * transfn2 = increment) will want to have transfn2 called; "avg"
+ * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93
*
* ------------------------------------------
*/
TupleTableSlot *
-ExecAgg(Agg *node)
+ExecAgg(Agg * node)
{
- AggState *aggstate;
- EState *estate;
- Aggreg **aggregates;
- Plan *outerPlan;
- int i, nagg;
- Datum *value1, *value2;
- int *noInitValue;
- AggFuncInfo *aggFuncInfo;
- long nTuplesAgged = 0;
- ExprContext *econtext;
- ProjectionInfo *projInfo;
- TupleTableSlot *resultSlot;
- HeapTuple oneTuple;
- char* nulls;
- bool isDone;
- bool isNull = FALSE, isNull1 = FALSE, isNull2 = FALSE;
-
- /* ---------------------
- * get state info from node
- * ---------------------
- */
- aggstate = node->aggstate;
- if (aggstate->agg_done)
- return NULL;
-
- estate = node->plan.state;
- econtext = aggstate->csstate.cstate.cs_ExprContext;
- aggregates = node->aggs;
- nagg = node->numAgg;
-
- value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
- nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
-
- value2 = (Datum *)palloc(sizeof(Datum) * nagg);
- memset(value2, 0, sizeof(Datum) * nagg);
-
- aggFuncInfo = (AggFuncInfo *)palloc(sizeof(AggFuncInfo) * nagg);
- memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
-
- noInitValue = (int *)palloc(sizeof(int) * nagg);
- memset(noInitValue, 0, sizeof(noInitValue) * nagg);
-
- outerPlan = outerPlan(node);
- oneTuple = NULL;
-
- projInfo = aggstate->csstate.cstate.cs_ProjInfo;
-
- for(i = 0; i < nagg; i++) {
- Aggreg *agg;
- char *aggname;
- HeapTuple aggTuple;
- Form_pg_aggregate aggp;
- Oid xfn1_oid, xfn2_oid, finalfn_oid;
- func_ptr xfn1_ptr, xfn2_ptr, finalfn_ptr;
- int xfn1_nargs, xfn2_nargs, finalfn_nargs;
-
- agg = aggregates[i];
+ AggState *aggstate;
+ EState *estate;
+ Aggreg **aggregates;
+ Plan *outerPlan;
+ int i,
+ nagg;
+ Datum *value1,
+ *value2;
+ int *noInitValue;
+ AggFuncInfo *aggFuncInfo;
+ long nTuplesAgged = 0;
+ ExprContext *econtext;
+ ProjectionInfo *projInfo;
+ TupleTableSlot *resultSlot;
+ HeapTuple oneTuple;
+ char *nulls;
+ bool isDone;
+ bool isNull = FALSE,
+ isNull1 = FALSE,
+ isNull2 = FALSE;
/* ---------------------
- * find transfer functions of all the aggregates and initialize
- * their initial values
+ * get state info from node
* ---------------------
*/
- aggname = agg->aggname;
- aggTuple = SearchSysCacheTuple(AGGNAME,
- PointerGetDatum(aggname),
- ObjectIdGetDatum(agg->basetype),
- 0,0);
- if (!HeapTupleIsValid(aggTuple))
- elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
- aggname,
- tname(get_id_type(agg->basetype)));
- aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
-
- xfn1_oid = aggp->aggtransfn1;
- xfn2_oid = aggp->aggtransfn2;
- finalfn_oid = aggp->aggfinalfn;
-
- if (OidIsValid(finalfn_oid)) {
- fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs);
- aggFuncInfo[i].finalfn_oid = finalfn_oid;
- aggFuncInfo[i].finalfn = finalfn_ptr;
- aggFuncInfo[i].finalfn_nargs = finalfn_nargs;
- }
+ aggstate = node->aggstate;
+ if (aggstate->agg_done)
+ return NULL;
+
+ estate = node->plan.state;
+ econtext = aggstate->csstate.cstate.cs_ExprContext;
+ aggregates = node->aggs;
+ nagg = node->numAgg;
+
+ value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
+ nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
+
+ value2 = (Datum *) palloc(sizeof(Datum) * nagg);
+ memset(value2, 0, sizeof(Datum) * nagg);
+
+ aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg);
+ memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
+
+ noInitValue = (int *) palloc(sizeof(int) * nagg);
+ memset(noInitValue, 0, sizeof(noInitValue) * nagg);
+
+ outerPlan = outerPlan(node);
+ oneTuple = NULL;
+
+ projInfo = aggstate->csstate.cstate.cs_ProjInfo;
+
+ for (i = 0; i < nagg; i++)
+ {
+ Aggreg *agg;
+ char *aggname;
+ HeapTuple aggTuple;
+ Form_pg_aggregate aggp;
+ Oid xfn1_oid,
+ xfn2_oid,
+ finalfn_oid;
+ func_ptr xfn1_ptr,
+ xfn2_ptr,
+ finalfn_ptr;
+ int xfn1_nargs,
+ xfn2_nargs,
+ finalfn_nargs;
+
+ agg = aggregates[i];
+
+ /* ---------------------
+ * find transfer functions of all the aggregates and initialize
+ * their initial values
+ * ---------------------
+ */
+ aggname = agg->aggname;
+ aggTuple = SearchSysCacheTuple(AGGNAME,
+ PointerGetDatum(aggname),
+ ObjectIdGetDatum(agg->basetype),
+ 0, 0);
+ if (!HeapTupleIsValid(aggTuple))
+ elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
+ aggname,
+ tname(get_id_type(agg->basetype)));
+ aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+
+ xfn1_oid = aggp->aggtransfn1;
+ xfn2_oid = aggp->aggtransfn2;
+ finalfn_oid = aggp->aggfinalfn;
+
+ if (OidIsValid(finalfn_oid))
+ {
+ fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs);
+ aggFuncInfo[i].finalfn_oid = finalfn_oid;
+ aggFuncInfo[i].finalfn = finalfn_ptr;
+ aggFuncInfo[i].finalfn_nargs = finalfn_nargs;
+ }
- if (OidIsValid(xfn2_oid)) {
- fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs);
- aggFuncInfo[i].xfn2_oid = xfn2_oid;
- aggFuncInfo[i].xfn2 = xfn2_ptr;
- aggFuncInfo[i].xfn2_nargs = xfn2_nargs;
- value2[i] = (Datum)AggNameGetInitVal((char*)aggname,
- aggp->aggbasetype,
- 2,
- &isNull2);
- /* ------------------------------------------
- * If there is a second transition function, its initial
- * value must exist -- as it does not depend on data values,
- * we have no other way of determining an initial value.
- * ------------------------------------------
- */
- if (isNull2)
- elog(WARN, "ExecAgg: agginitval2 is null");
- }
+ if (OidIsValid(xfn2_oid))
+ {
+ fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs);
+ aggFuncInfo[i].xfn2_oid = xfn2_oid;
+ aggFuncInfo[i].xfn2 = xfn2_ptr;
+ aggFuncInfo[i].xfn2_nargs = xfn2_nargs;
+ value2[i] = (Datum) AggNameGetInitVal((char *) aggname,
+ aggp->aggbasetype,
+ 2,
+ &isNull2);
+ /* ------------------------------------------
+ * If there is a second transition function, its initial
+ * value must exist -- as it does not depend on data values,
+ * we have no other way of determining an initial value.
+ * ------------------------------------------
+ */
+ if (isNull2)
+ elog(WARN, "ExecAgg: agginitval2 is null");
+ }
- if (OidIsValid(xfn1_oid)) {
- fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs);
- aggFuncInfo[i].xfn1_oid = xfn1_oid;
- aggFuncInfo[i].xfn1 = xfn1_ptr;
- aggFuncInfo[i].xfn1_nargs = xfn1_nargs;
- value1[i] = (Datum)AggNameGetInitVal((char*)aggname,
- aggp->aggbasetype,
- 1,
- &isNull1);
-
- /* ------------------------------------------
- * If the initial value for the first transition function
- * doesn't exist in the pg_aggregate table then we let
- * the first value returned from the outer procNode become
- * the initial value. (This is useful for aggregates like
- * max{} and min{}.)
- * ------------------------------------------
- */
- if (isNull1) {
- noInitValue[i] = 1;
- nulls[i] = 1;
- }
- }
- }
-
- /* ----------------
- * for each tuple from the the outer plan, apply all the aggregates
- * ----------------
- */
- for (;;) {
- HeapTuple outerTuple = NULL;
- TupleTableSlot *outerslot;
-
- isNull = isNull1 = isNull2 = 0;
- outerslot = ExecProcNode(outerPlan, (Plan*)node);
- if (outerslot) outerTuple = outerslot->val;
- if (!HeapTupleIsValid(outerTuple)) {
- /* when the outerplan doesn't return a single tuple,
- create a dummy heaptuple anyway
- because we still need to return a valid aggregate value.
- The value returned will be the initial values of the
- transition functions */
- if (nTuplesAgged == 0) {
- TupleDesc tupType;
- Datum *tupValue;
- char* null_array;
-
- tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
- tupValue = projInfo->pi_tupValue;
-
- /* initially, set all the values to NULL */
- null_array = malloc(tupType->natts);
- for (i=0;i<tupType->natts;i++)
- null_array[i] = 'n';
- oneTuple = heap_formtuple(tupType, tupValue, null_array);
- free(null_array);
- }
- break;
+ if (OidIsValid(xfn1_oid))
+ {
+ fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs);
+ aggFuncInfo[i].xfn1_oid = xfn1_oid;
+ aggFuncInfo[i].xfn1 = xfn1_ptr;
+ aggFuncInfo[i].xfn1_nargs = xfn1_nargs;
+ value1[i] = (Datum) AggNameGetInitVal((char *) aggname,
+ aggp->aggbasetype,
+ 1,
+ &isNull1);
+
+ /* ------------------------------------------
+ * If the initial value for the first transition function
+ * doesn't exist in the pg_aggregate table then we let
+ * the first value returned from the outer procNode become
+ * the initial value. (This is useful for aggregates like
+ * max{} and min{}.)
+ * ------------------------------------------
+ */
+ if (isNull1)
+ {
+ noInitValue[i] = 1;
+ nulls[i] = 1;
+ }
+ }
}
- for(i = 0; i < nagg; i++) {
- AttrNumber attnum;
- int2 attlen;
- Datum newVal = (Datum) NULL;
- AggFuncInfo *aggfns = &aggFuncInfo[i];
- Datum args[2];
- Node *tagnode = NULL;
-
- switch(nodeTag(aggregates[i]->target))
- {
- case T_Var:
- tagnode = NULL;
- newVal = aggGetAttr(outerslot,
- aggregates[i],
- &isNull);
- break;
- case T_Expr:
- tagnode = ((Expr*)aggregates[i]->target)->oper;
- econtext->ecxt_scantuple = outerslot;
- newVal = ExecEvalExpr (aggregates[i]->target, econtext,
- &isNull, NULL);
+ /* ----------------
+ * for each tuple from the the outer plan, apply all the aggregates
+ * ----------------
+ */
+ for (;;)
+ {
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot;
+
+ isNull = isNull1 = isNull2 = 0;
+ outerslot = ExecProcNode(outerPlan, (Plan *) node);
+ if (outerslot)
+ outerTuple = outerslot->val;
+ if (!HeapTupleIsValid(outerTuple))
+ {
+
+ /*
+ * when the outerplan doesn't return a single tuple, create a
+ * dummy heaptuple anyway because we still need to return a
+ * valid aggregate value. The value returned will be the
+ * initial values of the transition functions
+ */
+ if (nTuplesAgged == 0)
+ {
+ TupleDesc tupType;
+ Datum *tupValue;
+ char *null_array;
+
+ tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
+ tupValue = projInfo->pi_tupValue;
+
+ /* initially, set all the values to NULL */
+ null_array = malloc(tupType->natts);
+ for (i = 0; i < tupType->natts; i++)
+ null_array[i] = 'n';
+ oneTuple = heap_formtuple(tupType, tupValue, null_array);
+ free(null_array);
+ }
break;
- default:
- elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i);
- }
-
- if (isNull)
- continue; /* ignore this tuple for this agg */
-
- if (aggfns->xfn1) {
- if (noInitValue[i]) {
- int byVal;
-
- /*
- * value1 and value2 has not been initialized. This
- * is the first non-NULL value. We use it as the
- * initial value.
- */
- /* but we can't just use it straight, we have
- to make a copy of it since the tuple from which
- it came will be freed on the next iteration
- of the scan */
- if ( tagnode != NULL )
- {
- FunctionCachePtr fcache_ptr;
-
- if ( nodeTag(tagnode) == T_Func )
- fcache_ptr = ((Func*)tagnode)->func_fcache;
- else
- fcache_ptr = ((Oper*)tagnode)->op_fcache;
- attlen = fcache_ptr->typlen;
- byVal = fcache_ptr->typbyval;
- }
- else
- {
- attnum = ((Var*)aggregates[i]->target)->varattno;
- attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen;
- byVal = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval;
- }
- if (attlen == -1) {
- /* variable length */
- attlen = VARSIZE((struct varlena*) newVal);
- }
- value1[i] = (Datum)palloc(attlen);
- if ( byVal )
- value1[i] = newVal;
- else
- memmove((char*)(value1[i]), (char*)newVal, attlen);
- /* value1[i] = newVal; */
- noInitValue[i] = 0;
- nulls[i] = 0;
- } else {
- /*
- * apply the transition functions.
- */
- args[0] = value1[i];
- args[1] = newVal;
- value1[i] =
- (Datum)fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
- aggfns->xfn1_nargs, (FmgrValues *)args,
- &isNull1);
- Assert(!isNull1);
}
- }
-
- if (aggfns->xfn2) {
- Datum xfn2_val = value2[i];
-
- value2[i] =
- (Datum)fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
- aggfns->xfn2_nargs,
- (FmgrValues *)&xfn2_val, &isNull2);
- Assert(!isNull2);
- }
+
+ for (i = 0; i < nagg; i++)
+ {
+ AttrNumber attnum;
+ int2 attlen;
+ Datum newVal = (Datum) NULL;
+ AggFuncInfo *aggfns = &aggFuncInfo[i];
+ Datum args[2];
+ Node *tagnode = NULL;
+
+ switch (nodeTag(aggregates[i]->target))
+ {
+ case T_Var:
+ tagnode = NULL;
+ newVal = aggGetAttr(outerslot,
+ aggregates[i],
+ &isNull);
+ break;
+ case T_Expr:
+ tagnode = ((Expr *) aggregates[i]->target)->oper;
+ econtext->ecxt_scantuple = outerslot;
+ newVal = ExecEvalExpr(aggregates[i]->target, econtext,
+ &isNull, NULL);
+ break;
+ default:
+ elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i);
+ }
+
+ if (isNull)
+ continue; /* ignore this tuple for this agg */
+
+ if (aggfns->xfn1)
+ {
+ if (noInitValue[i])
+ {
+ int byVal;
+
+ /*
+ * value1 and value2 has not been initialized. This is
+ * the first non-NULL value. We use it as the initial
+ * value.
+ */
+
+ /*
+ * but we can't just use it straight, we have to make
+ * a copy of it since the tuple from which it came
+ * will be freed on the next iteration of the scan
+ */
+ if (tagnode != NULL)
+ {
+ FunctionCachePtr fcache_ptr;
+
+ if (nodeTag(tagnode) == T_Func)
+ fcache_ptr = ((Func *) tagnode)->func_fcache;
+ else
+ fcache_ptr = ((Oper *) tagnode)->op_fcache;
+ attlen = fcache_ptr->typlen;
+ byVal = fcache_ptr->typbyval;
+ }
+ else
+ {
+ attnum = ((Var *) aggregates[i]->target)->varattno;
+ attlen = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attlen;
+ byVal = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attbyval;
+ }
+ if (attlen == -1)
+ {
+ /* variable length */
+ attlen = VARSIZE((struct varlena *) newVal);
+ }
+ value1[i] = (Datum) palloc(attlen);
+ if (byVal)
+ value1[i] = newVal;
+ else
+ memmove((char *) (value1[i]), (char *) newVal, attlen);
+ /* value1[i] = newVal; */
+ noInitValue[i] = 0;
+ nulls[i] = 0;
+ }
+ else
+ {
+
+ /*
+ * apply the transition functions.
+ */
+ args[0] = value1[i];
+ args[1] = newVal;
+ value1[i] =
+ (Datum) fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
+ aggfns->xfn1_nargs, (FmgrValues *) args,
+ &isNull1);
+ Assert(!isNull1);
+ }
+ }
+
+ if (aggfns->xfn2)
+ {
+ Datum xfn2_val = value2[i];
+
+ value2[i] =
+ (Datum) fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
+ aggfns->xfn2_nargs,
+ (FmgrValues *) & xfn2_val, &isNull2);
+ Assert(!isNull2);
+ }
+ }
+
+ /*
+ * keep this for the projection (we only need one of these - all
+ * the tuples we aggregate over share the same group column)
+ */
+ if (!oneTuple)
+ {
+ oneTuple = heap_copytuple(outerslot->val);
+ }
+
+ nTuplesAgged++;
+ }
+
+ /* --------------
+ * finalize the aggregate (if necessary), and get the resultant value
+ * --------------
+ */
+ for (i = 0; i < nagg; i++)
+ {
+ char *args[2];
+ AggFuncInfo *aggfns = &aggFuncInfo[i];
+
+ if (noInitValue[i])
+ {
+
+ /*
+ * No values found for this agg; return current state. This
+ * seems to fix behavior for avg() aggregate. -tgl 12/96
+ */
+ }
+ else if (aggfns->finalfn && nTuplesAgged > 0)
+ {
+ if (aggfns->finalfn_nargs > 1)
+ {
+ args[0] = (char *) value1[i];
+ args[1] = (char *) value2[i];
+ }
+ else if (aggfns->xfn1)
+ {
+ args[0] = (char *) value1[i];
+ }
+ else if (aggfns->xfn2)
+ {
+ args[0] = (char *) value2[i];
+ }
+ else
+ elog(WARN, "ExecAgg: no valid transition functions??");
+ value1[i] =
+ (Datum) fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
+ aggfns->finalfn_nargs, (FmgrValues *) args,
+ &(nulls[i]));
+ }
+ else if (aggfns->xfn1)
+ {
+
+ /*
+ * value in the right place, ignore. (If you remove this case,
+ * fix the else part. -ay 2/95)
+ */
+ }
+ else if (aggfns->xfn2)
+ {
+ value1[i] = value2[i];
+ }
+ else
+ elog(WARN, "ExecAgg: no valid transition functions??");
}
/*
- * keep this for the projection (we only need one of these -
- * all the tuples we aggregate over share the same group column)
+ * whether the aggregation is done depends on whether we are doing
+ * aggregation over groups or the entire table
*/
- if (!oneTuple) {
- oneTuple = heap_copytuple(outerslot->val);
+ if (nodeTag(outerPlan) == T_Group)
+ {
+ /* aggregation over groups */
+ aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
+ }
+ else
+ {
+ aggstate->agg_done = TRUE;
}
- nTuplesAgged++;
- }
-
- /* --------------
- * finalize the aggregate (if necessary), and get the resultant value
- * --------------
- */
- for(i = 0; i < nagg; i++) {
- char *args[2];
- AggFuncInfo *aggfns = &aggFuncInfo[i];
-
- if (noInitValue[i]) {
- /*
- * No values found for this agg; return current state.
- * This seems to fix behavior for avg() aggregate. -tgl 12/96
- */
- } else if (aggfns->finalfn && nTuplesAgged > 0) {
- if (aggfns->finalfn_nargs > 1) {
- args[0] = (char*)value1[i];
- args[1] = (char*)value2[i];
- } else if (aggfns->xfn1) {
- args[0] = (char*)value1[i];
- } else if (aggfns->xfn2) {
- args[0] = (char*)value2[i];
- } else
- elog(WARN, "ExecAgg: no valid transition functions??");
- value1[i] =
- (Datum)fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
- aggfns->finalfn_nargs, (FmgrValues *) args,
- &(nulls[i]));
- } else if (aggfns->xfn1) {
- /*
- * value in the right place, ignore. (If you remove this
- * case, fix the else part. -ay 2/95)
- */
- } else if (aggfns->xfn2) {
- value1[i] = value2[i];
- } else
- elog(WARN, "ExecAgg: no valid transition functions??");
- }
-
- /*
- * whether the aggregation is done depends on whether we are doing
- * aggregation over groups or the entire table
- */
- if (nodeTag(outerPlan)==T_Group) {
- /* aggregation over groups */
- aggstate->agg_done = ((Group*)outerPlan)->grpstate->grp_done;
- } else {
- aggstate->agg_done = TRUE;
- }
-
- /* ----------------
- * form a projection tuple, store it in the result tuple
- * slot and return it.
- * ----------------
- */
- ExecStoreTuple(oneTuple,
- aggstate->csstate.css_ScanTupleSlot,
- InvalidBuffer,
- false);
- econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
- resultSlot = ExecProject(projInfo, &isDone);
-
- if (oneTuple)
- pfree(oneTuple);
-
- return resultSlot;
+ /* ----------------
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
+ * ----------------
+ */
+ ExecStoreTuple(oneTuple,
+ aggstate->csstate.css_ScanTupleSlot,
+ InvalidBuffer,
+ false);
+ econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
+ resultSlot = ExecProject(projInfo, &isDone);
+
+ if (oneTuple)
+ pfree(oneTuple);
+
+ return resultSlot;
}
/* -----------------
* ExecInitAgg
*
- * Creates the run-time information for the agg node produced by the
- * planner and initializes its outer subtree
+ * Creates the run-time information for the agg node produced by the
+ * planner and initializes its outer subtree
* -----------------
*/
bool
-ExecInitAgg(Agg *node, EState *estate, Plan *parent)
+ExecInitAgg(Agg * node, EState * estate, Plan * parent)
{
- AggState *aggstate;
- Plan *outerPlan;
- ExprContext *econtext;
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create state structure
- */
- aggstate = makeNode(AggState);
- node->aggstate = aggstate;
- aggstate->agg_done = FALSE;
-
- /*
- * assign node's base id and create expression context
- */
- ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
- (Plan*) parent);
- ExecAssignExprContext(estate, &aggstate->csstate.cstate);
-
+ AggState *aggstate;
+ Plan *outerPlan;
+ ExprContext *econtext;
+
+ /*
+ * assign the node's execution state
+ */
+ node->plan.state = estate;
+
+ /*
+ * create state structure
+ */
+ aggstate = makeNode(AggState);
+ node->aggstate = aggstate;
+ aggstate->agg_done = FALSE;
+
+ /*
+ * assign node's base id and create expression context
+ */
+ ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
+ (Plan *) parent);
+ ExecAssignExprContext(estate, &aggstate->csstate.cstate);
+
#define AGG_NSLOTS 2
- /*
- * tuple table initialization
- */
- ExecInitScanTupleSlot(estate, &aggstate->csstate);
- ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
-
- econtext = aggstate->csstate.cstate.cs_ExprContext;
- econtext->ecxt_values =
- (Datum *)palloc(sizeof(Datum) * node->numAgg);
- memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg);
- econtext->ecxt_nulls = (char *)palloc(node->numAgg);
- memset(econtext->ecxt_nulls, 0, node->numAgg);
-
- /*
- * initializes child nodes
- */
- outerPlan = outerPlan(node);
- ExecInitNode(outerPlan, estate, (Plan *)node);
-
- /* ----------------
- * initialize tuple type.
- * ----------------
- */
- ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
-
- /*
- * Initialize tuple type for both result and scan.
- * This node does no projection
- */
- ExecAssignResultTypeFromTL((Plan*) node, &aggstate->csstate.cstate);
- ExecAssignProjectionInfo((Plan*)node, &aggstate->csstate.cstate);
-
- return TRUE;
+
+ /*
+ * tuple table initialization
+ */
+ ExecInitScanTupleSlot(estate, &aggstate->csstate);
+ ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
+
+ econtext = aggstate->csstate.cstate.cs_ExprContext;
+ econtext->ecxt_values =
+ (Datum *) palloc(sizeof(Datum) * node->numAgg);
+ memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg);
+ econtext->ecxt_nulls = (char *) palloc(node->numAgg);
+ memset(econtext->ecxt_nulls, 0, node->numAgg);
+
+ /*
+ * initializes child nodes
+ */
+ outerPlan = outerPlan(node);
+ ExecInitNode(outerPlan, estate, (Plan *) node);
+
+ /* ----------------
+ * initialize tuple type.
+ * ----------------
+ */
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
+
+ /*
+ * Initialize tuple type for both result and scan. This node does no
+ * projection
+ */
+ ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate);
+ ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate);
+
+ return TRUE;
}
int
-ExecCountSlotsAgg(Agg *node)
+ExecCountSlotsAgg(Agg * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
+ return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
- AGG_NSLOTS;
+ AGG_NSLOTS;
}
/* ------------------------
- * ExecEndAgg(node)
+ * ExecEndAgg(node)
*
* -----------------------
*/
void
-ExecEndAgg(Agg *node)
+ExecEndAgg(Agg * node)
{
- AggState *aggstate;
- Plan *outerPlan;
+ AggState *aggstate;
+ Plan *outerPlan;
- aggstate = node->aggstate;
+ aggstate = node->aggstate;
- ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+ ExecFreeProjectionInfo(&aggstate->csstate.cstate);
- outerPlan = outerPlan(node);
- ExecEndNode(outerPlan, (Plan*)node);
-
- /* clean up tuple table */
- ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan *) node);
+
+ /* clean up tuple table */
+ ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
}
/*****************************************************************************
- * Support Routines
+ * Support Routines
*****************************************************************************/
/*
* aggGetAttr -
- * get the attribute (specified in the Var node in agg) to aggregate
- * over from the tuple
+ * get the attribute (specified in the Var node in agg) to aggregate
+ * over from the tuple
*/
-static Datum
-aggGetAttr(TupleTableSlot *slot,
- Aggreg *agg,
- bool *isNull)
+static Datum
+aggGetAttr(TupleTableSlot * slot,
+ Aggreg * agg,
+ bool * isNull)
{
- Datum result;
- AttrNumber attnum;
- HeapTuple heapTuple;
- TupleDesc tuple_type;
- Buffer buffer;
-
- /* ----------------
- * extract tuple information from the slot
- * ----------------
- */
- heapTuple = slot->val;
- tuple_type = slot->ttc_tupleDescriptor;
- buffer = slot->ttc_buffer;
-
- attnum = ((Var*)agg->target)->varattno;
-
- /*
- * If the attribute number is invalid, then we are supposed to
- * return the entire tuple, we give back a whole slot so that
- * callers know what the tuple looks like.
- */
- if (attnum == InvalidAttrNumber) {
- TupleTableSlot *tempSlot;
- TupleDesc td;
- HeapTuple tup;
-
- tempSlot = makeNode(TupleTableSlot);
- tempSlot->ttc_shouldFree = false;
- tempSlot->ttc_descIsNew = true;
- tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
- tempSlot->ttc_buffer = InvalidBuffer;
- tempSlot->ttc_whichplan = -1;
-
- tup = heap_copytuple(slot->val);
- td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
-
- ExecSetSlotDescriptor(tempSlot, td);
-
- ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
- return (Datum) tempSlot;
- }
-
- result = (Datum)
- heap_getattr(heapTuple, /* tuple containing attribute */
- buffer, /* buffer associated with tuple */
- attnum, /* attribute number of desired attribute */
- tuple_type, /* tuple descriptor of tuple */
- isNull); /* return: is attribute null? */
-
- /* ----------------
- * return null if att is null
- * ----------------
- */
- if (*isNull)
- return (Datum) NULL;
-
- return result;
+ Datum result;
+ AttrNumber attnum;
+ HeapTuple heapTuple;
+ TupleDesc tuple_type;
+ Buffer buffer;
+
+ /* ----------------
+ * extract tuple information from the slot
+ * ----------------
+ */
+ heapTuple = slot->val;
+ tuple_type = slot->ttc_tupleDescriptor;
+ buffer = slot->ttc_buffer;
+
+ attnum = ((Var *) agg->target)->varattno;
+
+ /*
+ * If the attribute number is invalid, then we are supposed to return
+ * the entire tuple, we give back a whole slot so that callers know
+ * what the tuple looks like.
+ */
+ if (attnum == InvalidAttrNumber)
+ {
+ TupleTableSlot *tempSlot;
+ TupleDesc td;
+ HeapTuple tup;
+
+ tempSlot = makeNode(TupleTableSlot);
+ tempSlot->ttc_shouldFree = false;
+ tempSlot->ttc_descIsNew = true;
+ tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL,
+ tempSlot->ttc_buffer = InvalidBuffer;
+ tempSlot->ttc_whichplan = -1;
+
+ tup = heap_copytuple(slot->val);
+ td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+
+ ExecSetSlotDescriptor(tempSlot, td);
+
+ ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
+ return (Datum) tempSlot;
+ }
+
+ result = (Datum)
+ heap_getattr(heapTuple, /* tuple containing attribute */
+ buffer, /* buffer associated with tuple */
+ attnum, /* attribute number of desired attribute */
+ tuple_type,/* tuple descriptor of tuple */
+ isNull); /* return: is attribute null? */
+
+ /* ----------------
+ * return null if att is null
+ * ----------------
+ */
+ if (*isNull)
+ return (Datum) NULL;
+
+ return result;
}