1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* nodeAgg.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to handle aggregate nodes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* NOTE
|
1997-09-07 07:04:48 +02:00
|
|
|
* The implementation of Agg node has been reworked to handle legal
|
|
|
|
* SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1997-09-07 07:04:48 +02:00
|
|
|
* /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-06 07:52:23 +01:00
|
|
|
|
1996-10-31 11:12:26 +01:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/pg_aggregate.h"
|
|
|
|
#include "executor/executor.h"
|
|
|
|
#include "executor/nodeAgg.h"
|
1998-03-30 18:36:43 +02:00
|
|
|
#include "optimizer/clauses.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "parser/parse_type.h"
|
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* AggFuncInfo -
|
1997-09-07 07:04:48 +02:00
|
|
|
* keeps the transition functions information around
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
typedef struct AggFuncInfo
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid xfn1_oid;
|
|
|
|
Oid xfn2_oid;
|
|
|
|
Oid finalfn_oid;
|
1998-01-15 20:46:37 +01:00
|
|
|
FmgrInfo xfn1;
|
|
|
|
FmgrInfo xfn2;
|
|
|
|
FmgrInfo finalfn;
|
1997-09-08 22:59:27 +02:00
|
|
|
} AggFuncInfo;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-05-26 00:43:53 +02:00
|
|
|
static Datum aggGetAttr(TupleTableSlot *tuple, Aggref *aggref, bool *isNull);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------
|
|
|
|
*
|
|
|
|
* ExecAgg -
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.)
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* value1[i] = initcond1
|
|
|
|
* value2[i] = initcond2
|
|
|
|
* forall tuples do
|
1999-03-21 20:59:13 +01:00
|
|
|
* value1[i] = sfunc1(value1[i], aggregated_value)
|
1997-09-07 07:04:48 +02:00
|
|
|
* value2[i] = sfunc2(value2[i])
|
|
|
|
* value1[i] = finalfunc(value1[i], value2[i])
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1999-03-21 20:59:13 +01:00
|
|
|
* If initcond1 is NULL then the first non-NULL aggregated_value is
|
|
|
|
* assigned directly to value1[i]. sfunc1 isn't applied until value1[i]
|
|
|
|
* is non-NULL.
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If the outer subplan is a Group node, ExecAgg returns as many tuples
|
|
|
|
* as there are groups.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* XXX handling of NULL doesn't work
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* OLD COMMENTS
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* ------------------------------------------
|
|
|
|
*/
|
|
|
|
TupleTableSlot *
|
1997-09-08 22:59:27 +02:00
|
|
|
ExecAgg(Agg *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
AggState *aggstate;
|
|
|
|
EState *estate;
|
|
|
|
Plan *outerPlan;
|
1999-01-25 19:02:28 +01:00
|
|
|
int aggno,
|
1999-08-21 05:49:17 +02:00
|
|
|
numaggs;
|
1997-09-08 04:41:22 +02:00
|
|
|
Datum *value1,
|
|
|
|
*value2;
|
|
|
|
int *noInitValue;
|
|
|
|
AggFuncInfo *aggFuncInfo;
|
|
|
|
long nTuplesAgged = 0;
|
|
|
|
ExprContext *econtext;
|
1997-09-07 07:04:48 +02:00
|
|
|
ProjectionInfo *projInfo;
|
|
|
|
TupleTableSlot *resultSlot;
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple oneTuple;
|
1998-01-15 20:00:16 +01:00
|
|
|
List *alist;
|
1997-09-08 04:41:22 +02:00
|
|
|
char *nulls;
|
|
|
|
bool isDone;
|
|
|
|
bool isNull = FALSE,
|
|
|
|
isNull1 = FALSE,
|
|
|
|
isNull2 = FALSE;
|
1998-09-01 06:40:42 +02:00
|
|
|
bool qual_result;
|
1999-06-12 16:07:33 +02:00
|
|
|
|
1998-03-30 18:36:43 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ---------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* get state info from node
|
1996-07-09 08:22:35 +02:00
|
|
|
* ---------------------
|
|
|
|
*/
|
1998-03-30 18:36:43 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* We loop retrieving groups until we find one matching
|
|
|
|
* node->plan.qual
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
do
|
|
|
|
{
|
|
|
|
aggstate = node->aggstate;
|
|
|
|
if (aggstate->agg_done)
|
|
|
|
return NULL;
|
1998-07-19 07:49:26 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
estate = node->plan.state;
|
|
|
|
econtext = aggstate->csstate.cstate.cs_ExprContext;
|
1998-01-15 20:00:16 +01:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
numaggs = length(aggstate->aggs);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
|
|
|
|
nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
value2 = (Datum *) palloc(sizeof(Datum) * numaggs);
|
|
|
|
MemSet(value2, 0, sizeof(Datum) * numaggs);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * numaggs);
|
|
|
|
MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * numaggs);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
noInitValue = (int *) palloc(sizeof(int) * numaggs);
|
|
|
|
MemSet(noInitValue, 0, sizeof(int) * numaggs);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
outerPlan = outerPlan(node);
|
|
|
|
oneTuple = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
projInfo = aggstate->csstate.cstate.cs_ProjInfo;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-01-27 00:32:04 +01:00
|
|
|
aggno = -1;
|
1999-08-21 05:49:17 +02:00
|
|
|
foreach(alist, aggstate->aggs)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
Aggref *aggref = lfirst(alist);
|
1998-09-01 06:40:42 +02:00
|
|
|
char *aggname;
|
|
|
|
HeapTuple aggTuple;
|
|
|
|
Form_pg_aggregate aggp;
|
|
|
|
Oid xfn1_oid,
|
|
|
|
xfn2_oid,
|
|
|
|
finalfn_oid;
|
|
|
|
|
1999-01-27 17:15:01 +01:00
|
|
|
aggref->aggno = ++aggno;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/* ---------------------
|
|
|
|
* find transfer functions of all the aggregates and initialize
|
|
|
|
* their initial values
|
|
|
|
* ---------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-01-25 19:02:28 +01:00
|
|
|
aggname = aggref->aggname;
|
1998-09-01 06:40:42 +02:00
|
|
|
aggTuple = SearchSysCacheTuple(AGGNAME,
|
|
|
|
PointerGetDatum(aggname),
|
1999-05-25 18:15:34 +02:00
|
|
|
ObjectIdGetDatum(aggref->basetype),
|
1998-09-01 06:40:42 +02:00
|
|
|
0, 0);
|
|
|
|
if (!HeapTupleIsValid(aggTuple))
|
|
|
|
elog(ERROR, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
|
|
|
|
aggname,
|
1999-01-25 19:02:28 +01:00
|
|
|
typeidTypeName(aggref->basetype));
|
1998-09-01 06:40:42 +02:00
|
|
|
aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
|
|
|
|
|
|
|
|
xfn1_oid = aggp->aggtransfn1;
|
|
|
|
xfn2_oid = aggp->aggtransfn2;
|
|
|
|
finalfn_oid = aggp->aggfinalfn;
|
|
|
|
|
|
|
|
if (OidIsValid(finalfn_oid))
|
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
fmgr_info(finalfn_oid, &aggFuncInfo[aggno].finalfn);
|
|
|
|
aggFuncInfo[aggno].finalfn_oid = finalfn_oid;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (OidIsValid(xfn2_oid))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
fmgr_info(xfn2_oid, &aggFuncInfo[aggno].xfn2);
|
|
|
|
aggFuncInfo[aggno].xfn2_oid = xfn2_oid;
|
|
|
|
value2[aggno] = (Datum) AggNameGetInitVal((char *) aggname,
|
1999-05-25 18:15:34 +02:00
|
|
|
aggp->aggbasetype,
|
|
|
|
2,
|
|
|
|
&isNull2);
|
1998-09-01 06:40:42 +02:00
|
|
|
/* ------------------------------------------
|
|
|
|
* 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(ERROR, "ExecAgg: agginitval2 is null");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (OidIsValid(xfn1_oid))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
fmgr_info(xfn1_oid, &aggFuncInfo[aggno].xfn1);
|
|
|
|
aggFuncInfo[aggno].xfn1_oid = xfn1_oid;
|
|
|
|
value1[aggno] = (Datum) AggNameGetInitVal((char *) aggname,
|
1999-05-25 18:15:34 +02:00
|
|
|
aggp->aggbasetype,
|
|
|
|
1,
|
|
|
|
&isNull1);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
/* ------------------------------------------
|
|
|
|
* 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)
|
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
noInitValue[aggno] = 1;
|
|
|
|
nulls[aggno] = 1;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/* ----------------
|
|
|
|
* for each tuple from the the outer plan, apply all the aggregates
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
for (;;)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
TupleTableSlot *outerslot;
|
|
|
|
|
|
|
|
isNull = isNull1 = isNull2 = 0;
|
|
|
|
outerslot = ExecProcNode(outerPlan, (Plan *) node);
|
1998-11-27 20:52:36 +01:00
|
|
|
if (TupIsNull(outerslot))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
1999-01-27 17:15:01 +01:00
|
|
|
AttrNumber attnum;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
|
|
|
|
tupValue = projInfo->pi_tupValue;
|
|
|
|
|
|
|
|
/* initially, set all the values to NULL */
|
1999-01-27 00:32:04 +01:00
|
|
|
null_array = palloc(sizeof(char) * tupType->natts);
|
1999-01-27 17:15:01 +01:00
|
|
|
for (attnum = 0; attnum < tupType->natts; attnum++)
|
|
|
|
null_array[attnum] = 'n';
|
1998-09-01 06:40:42 +02:00
|
|
|
oneTuple = heap_formtuple(tupType, tupValue, null_array);
|
|
|
|
pfree(null_array);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-01-27 00:32:04 +01:00
|
|
|
aggno = -1;
|
1999-08-21 05:49:17 +02:00
|
|
|
foreach(alist, aggstate->aggs)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
Aggref *aggref = lfirst(alist);
|
1999-01-27 00:32:04 +01:00
|
|
|
AggFuncInfo *aggfns = &aggFuncInfo[++aggno];
|
1999-04-29 03:13:13 +02:00
|
|
|
Datum newVal;
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum args[2];
|
|
|
|
|
1999-04-29 03:13:13 +02:00
|
|
|
/* Do we really need the special case for Var here? */
|
|
|
|
if (IsA(aggref->target, Var))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1999-04-29 03:13:13 +02:00
|
|
|
newVal = aggGetAttr(outerslot, aggref,
|
|
|
|
&isNull);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
econtext->ecxt_scantuple = outerslot;
|
|
|
|
newVal = ExecEvalExpr(aggref->target, econtext,
|
|
|
|
&isNull, &isDone);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
|
1999-01-25 19:02:28 +01:00
|
|
|
if (isNull && !aggref->usenulls)
|
1998-09-01 06:40:42 +02:00
|
|
|
continue; /* ignore this tuple for this agg */
|
|
|
|
|
|
|
|
if (aggfns->xfn1.fn_addr != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
if (noInitValue[aggno])
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* value1 has not been initialized. This is the
|
|
|
|
* first non-NULL input value. We use it as the
|
|
|
|
* initial value for value1.
|
1999-04-29 03:13:13 +02:00
|
|
|
*
|
1999-05-25 18:15:34 +02:00
|
|
|
* 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
|
1999-04-29 03:13:13 +02:00
|
|
|
* scan. This requires finding out how to copy
|
|
|
|
* the Datum. We assume the datum is of the agg's
|
1999-05-25 18:15:34 +02:00
|
|
|
* basetype, or at least binary compatible with
|
|
|
|
* it.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
1999-05-25 18:15:34 +02:00
|
|
|
Type aggBaseType = typeidType(aggref->basetype);
|
|
|
|
int attlen = typeLen(aggBaseType);
|
|
|
|
bool byVal = typeByVal(aggBaseType);
|
1999-04-29 03:13:13 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (byVal)
|
1999-01-25 19:02:28 +01:00
|
|
|
value1[aggno] = newVal;
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
1999-03-21 20:59:13 +01:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
if (attlen == -1) /* variable length */
|
1999-03-21 20:59:13 +01:00
|
|
|
attlen = VARSIZE((struct varlena *) newVal);
|
|
|
|
value1[aggno] = (Datum) palloc(attlen);
|
|
|
|
memcpy((char *) (value1[aggno]), (char *) newVal,
|
|
|
|
attlen);
|
|
|
|
}
|
1999-01-25 19:02:28 +01:00
|
|
|
noInitValue[aggno] = 0;
|
|
|
|
nulls[aggno] = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
|
|
|
* apply the transition functions.
|
|
|
|
*/
|
1999-01-25 19:02:28 +01:00
|
|
|
args[0] = value1[aggno];
|
1998-09-01 06:40:42 +02:00
|
|
|
args[1] = newVal;
|
1999-05-25 18:15:34 +02:00
|
|
|
value1[aggno] = (Datum) fmgr_c(&aggfns->xfn1,
|
|
|
|
(FmgrValues *) args, &isNull1);
|
1998-09-01 06:40:42 +02:00
|
|
|
Assert(!isNull1);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
if (aggfns->xfn2.fn_addr != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-03-21 20:59:13 +01:00
|
|
|
args[0] = value2[aggno];
|
1999-05-25 18:15:34 +02:00
|
|
|
value2[aggno] = (Datum) fmgr_c(&aggfns->xfn2,
|
|
|
|
(FmgrValues *) args, &isNull2);
|
1998-09-01 06:40:42 +02:00
|
|
|
Assert(!isNull2);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
nTuplesAgged++;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/* --------------
|
|
|
|
* finalize the aggregate (if necessary), and get the resultant value
|
|
|
|
* --------------
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-01-25 19:02:28 +01:00
|
|
|
|
1999-01-27 00:32:04 +01:00
|
|
|
aggno = -1;
|
1999-08-21 05:49:17 +02:00
|
|
|
foreach(alist, aggstate->aggs)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
char *args[2];
|
1999-01-27 00:32:04 +01:00
|
|
|
AggFuncInfo *aggfns = &aggFuncInfo[++aggno];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-01-25 19:02:28 +01:00
|
|
|
if (noInitValue[aggno])
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* No values found for this agg; return current state.
|
|
|
|
* This seems to fix behavior for avg() aggregate. -tgl
|
|
|
|
* 12/96
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
else if (aggfns->finalfn.fn_addr != NULL && nTuplesAgged > 0)
|
|
|
|
{
|
|
|
|
if (aggfns->finalfn.fn_nargs > 1)
|
|
|
|
{
|
1999-01-25 19:02:28 +01:00
|
|
|
args[0] = (char *) value1[aggno];
|
|
|
|
args[1] = (char *) value2[aggno];
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else if (aggfns->xfn1.fn_addr != NULL)
|
1999-01-25 19:02:28 +01:00
|
|
|
args[0] = (char *) value1[aggno];
|
1998-09-01 06:40:42 +02:00
|
|
|
else if (aggfns->xfn2.fn_addr != NULL)
|
1999-01-25 19:02:28 +01:00
|
|
|
args[0] = (char *) value2[aggno];
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
elog(NOTICE, "ExecAgg: no valid transition functions??");
|
1999-01-25 19:02:28 +01:00
|
|
|
value1[aggno] = (Datum) fmgr_c(&aggfns->finalfn,
|
1999-05-25 18:15:34 +02:00
|
|
|
(FmgrValues *) args, &(nulls[aggno]));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-01-15 20:46:37 +01:00
|
|
|
else if (aggfns->xfn1.fn_addr != NULL)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* value in the right place, ignore. (If you remove this
|
|
|
|
* case, fix the else part. -ay 2/95)
|
|
|
|
*/
|
|
|
|
}
|
1998-01-15 20:46:37 +01:00
|
|
|
else if (aggfns->xfn2.fn_addr != NULL)
|
1999-01-25 19:02:28 +01:00
|
|
|
value1[aggno] = value2[aggno];
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-09-01 06:40:42 +02:00
|
|
|
elog(ERROR, "ExecAgg: no valid transition functions??");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
1998-09-01 06:40:42 +02:00
|
|
|
aggstate->agg_done = TRUE;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/* ----------------
|
|
|
|
* form a projection tuple, store it in the result tuple
|
|
|
|
* slot and return it.
|
|
|
|
* ----------------
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
ExecStoreTuple(oneTuple,
|
|
|
|
aggstate->csstate.css_ScanTupleSlot,
|
|
|
|
InvalidBuffer,
|
|
|
|
false);
|
|
|
|
econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
|
|
|
|
|
|
|
|
resultSlot = ExecProject(projInfo, &isDone);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As long as the retrieved group does not match the
|
|
|
|
* qualifications it is ignored and the next group is fetched
|
|
|
|
*/
|
1999-05-25 18:15:34 +02:00
|
|
|
if (node->plan.qual != NULL)
|
1999-08-22 22:15:04 +02:00
|
|
|
qual_result = ExecQual(node->plan.qual, econtext);
|
1999-05-25 18:15:34 +02:00
|
|
|
else
|
|
|
|
qual_result = false;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (oneTuple)
|
|
|
|
pfree(oneTuple);
|
|
|
|
}
|
1999-01-25 19:02:28 +01:00
|
|
|
while (node->plan.qual != NULL && qual_result != true);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return resultSlot;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -----------------
|
|
|
|
* ExecInitAgg
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates the run-time information for the agg node produced by the
|
|
|
|
* planner and initializes its outer subtree
|
1996-07-09 08:22:35 +02:00
|
|
|
* -----------------
|
|
|
|
*/
|
|
|
|
bool
|
1997-09-08 23:56:23 +02:00
|
|
|
ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
AggState *aggstate;
|
|
|
|
Plan *outerPlan;
|
|
|
|
ExprContext *econtext;
|
1999-08-21 05:49:17 +02:00
|
|
|
int numaggs;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* assign the node's execution state
|
|
|
|
*/
|
|
|
|
node->plan.state = estate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create state structure
|
|
|
|
*/
|
|
|
|
aggstate = makeNode(AggState);
|
|
|
|
node->aggstate = aggstate;
|
1999-08-21 05:49:17 +02:00
|
|
|
aggstate->agg_done = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find aggregates in targetlist and quals
|
|
|
|
*/
|
|
|
|
aggstate->aggs = nconc(pull_agg_clause((Node *) node->plan.targetlist),
|
|
|
|
pull_agg_clause((Node *) node->plan.qual));
|
|
|
|
numaggs = length(aggstate->aggs);
|
|
|
|
if (numaggs <= 0)
|
|
|
|
elog(ERROR, "ExecInitAgg: could not find any aggregate functions");
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* assign node's base id and create expression context
|
|
|
|
*/
|
1999-01-27 17:15:01 +01:00
|
|
|
ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, (Plan *) parent);
|
1997-09-07 07:04:48 +02:00
|
|
|
ExecAssignExprContext(estate, &aggstate->csstate.cstate);
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#define AGG_NSLOTS 2
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* tuple table initialization
|
|
|
|
*/
|
|
|
|
ExecInitScanTupleSlot(estate, &aggstate->csstate);
|
|
|
|
ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
|
|
|
|
|
|
|
|
econtext = aggstate->csstate.cstate.cs_ExprContext;
|
1999-08-21 05:49:17 +02:00
|
|
|
econtext->ecxt_values = (Datum *) palloc(sizeof(Datum) * numaggs);
|
|
|
|
MemSet(econtext->ecxt_values, 0, sizeof(Datum) * numaggs);
|
|
|
|
econtext->ecxt_nulls = (char *) palloc(sizeof(char) * numaggs);
|
|
|
|
MemSet(econtext->ecxt_nulls, 0, sizeof(char) * numaggs);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* initializes child nodes
|
|
|
|
*/
|
|
|
|
outerPlan = outerPlan(node);
|
|
|
|
ExecInitNode(outerPlan, estate, (Plan *) node);
|
|
|
|
|
1997-12-22 06:42:25 +01:00
|
|
|
/*
|
1998-02-26 05:46:47 +01:00
|
|
|
* Result runs in its own context, but make it use our aggregates fix
|
|
|
|
* for 'select sum(2+2)'
|
1997-12-22 06:42:25 +01:00
|
|
|
*/
|
1999-08-21 05:49:17 +02:00
|
|
|
if (IsA(outerPlan, Result))
|
1997-12-22 06:42:25 +01:00
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
((Result *) outerPlan)->resstate->cstate.cs_ProjInfo->pi_exprContext->ecxt_values =
|
|
|
|
econtext->ecxt_values;
|
|
|
|
((Result *) outerPlan)->resstate->cstate.cs_ProjInfo->pi_exprContext->ecxt_nulls =
|
|
|
|
econtext->ecxt_nulls;
|
1997-12-22 06:42:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ----------------
|
|
|
|
* 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;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
1997-09-08 22:59:27 +02:00
|
|
|
ExecCountSlotsAgg(Agg *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
return ExecCountSlotsNode(outerPlan(node)) +
|
1999-05-25 18:15:34 +02:00
|
|
|
ExecCountSlotsNode(innerPlan(node)) +
|
|
|
|
AGG_NSLOTS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecEndAgg(node)
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* -----------------------
|
|
|
|
*/
|
|
|
|
void
|
1997-09-08 22:59:27 +02:00
|
|
|
ExecEndAgg(Agg *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
AggState *aggstate;
|
|
|
|
Plan *outerPlan;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
aggstate = node->aggstate;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
ExecFreeProjectionInfo(&aggstate->csstate.cstate);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
outerPlan = outerPlan(node);
|
|
|
|
ExecEndNode(outerPlan, (Plan *) node);
|
|
|
|
|
|
|
|
/* clean up tuple table */
|
|
|
|
ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
* Support Routines
|
1996-07-09 08:22:35 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aggGetAttr -
|
1997-09-07 07:04:48 +02:00
|
|
|
* get the attribute (specified in the Var node in agg) to aggregate
|
|
|
|
* over from the tuple
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static Datum
|
1997-09-08 23:56:23 +02:00
|
|
|
aggGetAttr(TupleTableSlot *slot,
|
1999-05-26 00:43:53 +02:00
|
|
|
Aggref *aggref,
|
1997-09-08 23:56:23 +02:00
|
|
|
bool *isNull)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Datum result;
|
|
|
|
AttrNumber attnum;
|
|
|
|
HeapTuple heapTuple;
|
|
|
|
TupleDesc tuple_type;
|
|
|
|
Buffer buffer;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* extract tuple information from the slot
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
heapTuple = slot->val;
|
|
|
|
tuple_type = slot->ttc_tupleDescriptor;
|
|
|
|
buffer = slot->ttc_buffer;
|
|
|
|
|
1999-01-25 19:02:28 +01:00
|
|
|
attnum = ((Var *) aggref->target)->varattno;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc td;
|
|
|
|
HeapTuple tup;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
tempSlot = makeNode(TupleTableSlot);
|
|
|
|
tempSlot->ttc_shouldFree = false;
|
|
|
|
tempSlot->ttc_descIsNew = true;
|
1999-01-27 00:32:04 +01:00
|
|
|
tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
|
|
|
|
tempSlot->ttc_buffer = InvalidBuffer;
|
1997-09-07 07:04:48 +02:00
|
|
|
tempSlot->ttc_whichplan = -1;
|
|
|
|
|
1998-11-27 20:52:36 +01:00
|
|
|
tup = heap_copytuple(heapTuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
|
|
|
|
|
|
|
|
ExecSetSlotDescriptor(tempSlot, td);
|
|
|
|
|
|
|
|
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
|
|
|
|
return (Datum) tempSlot;
|
|
|
|
}
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
result = heap_getattr(heapTuple, /* tuple containing attribute */
|
|
|
|
attnum, /* attribute number of desired
|
|
|
|
* attribute */
|
|
|
|
tuple_type, /* tuple descriptor of tuple */
|
|
|
|
isNull); /* return: is attribute null? */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* return null if att is null
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
if (*isNull)
|
|
|
|
return (Datum) NULL;
|
|
|
|
|
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-02-13 04:26:53 +01:00
|
|
|
|
|
|
|
void
|
|
|
|
ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
|
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
AggState *aggstate = node->aggstate;
|
|
|
|
ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext;
|
1998-02-13 04:26:53 +01:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
aggstate->agg_done = false;
|
|
|
|
MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(aggstate->aggs));
|
|
|
|
MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(aggstate->aggs));
|
1998-02-26 05:46:47 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if chgParam of subnode is not null then plan will be re-scanned by
|
|
|
|
* first ExecProcNode.
|
1998-02-13 04:26:53 +01:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
if (((Plan *) node)->lefttree->chgParam == NULL)
|
|
|
|
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
|
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|