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
|
1999-10-30 03:18:16 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.58 1999/10/30 01:18:16 tgl 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
|
|
|
|
|
|
|
/*
|
1999-09-26 23:21:15 +02:00
|
|
|
* AggStatePerAggData - per-aggregate working state for the Agg scan
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1999-09-26 23:21:15 +02:00
|
|
|
typedef struct AggStatePerAggData
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* These values are set up during ExecInitAgg() and do not change
|
|
|
|
* thereafter:
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Oids of transfer functions */
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid xfn1_oid;
|
|
|
|
Oid xfn2_oid;
|
|
|
|
Oid finalfn_oid;
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* fmgr lookup data for transfer functions --- only valid when
|
|
|
|
* corresponding oid is not InvalidOid
|
|
|
|
*/
|
1998-01-15 20:46:37 +01:00
|
|
|
FmgrInfo xfn1;
|
|
|
|
FmgrInfo xfn2;
|
|
|
|
FmgrInfo finalfn;
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* initial values from pg_aggregate entry
|
|
|
|
*/
|
|
|
|
Datum initValue1; /* for transtype1 */
|
|
|
|
Datum initValue2; /* for transtype2 */
|
|
|
|
bool initValue1IsNull,
|
|
|
|
initValue2IsNull;
|
|
|
|
/*
|
|
|
|
* We need the len and byval info for the agg's transition status types
|
|
|
|
* in order to know how to copy/delete values.
|
|
|
|
*/
|
|
|
|
int transtype1Len,
|
|
|
|
transtype2Len;
|
|
|
|
bool transtype1ByVal,
|
|
|
|
transtype2ByVal;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* These values are working state that is initialized at the start
|
|
|
|
* of an input tuple group and updated for each input tuple:
|
|
|
|
*/
|
|
|
|
|
|
|
|
Datum value1, /* current transfer values 1 and 2 */
|
|
|
|
value2;
|
|
|
|
bool value1IsNull,
|
|
|
|
value2IsNull;
|
|
|
|
bool noInitValue; /* true if value1 not set yet */
|
|
|
|
/*
|
|
|
|
* Note: right now, noInitValue always has the same value as value1IsNull.
|
|
|
|
* But we should keep them separate because once the fmgr interface is
|
|
|
|
* fixed, we'll need to distinguish a null returned by transfn1 from
|
|
|
|
* a null we haven't yet replaced with an input value.
|
|
|
|
*/
|
|
|
|
} AggStatePerAggData;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper routine to make a copy of a Datum.
|
|
|
|
*
|
|
|
|
* NB: input had better not be a NULL; might cause null-pointer dereference.
|
|
|
|
*/
|
|
|
|
static Datum
|
|
|
|
copyDatum(Datum val, int typLen, bool typByVal)
|
|
|
|
{
|
|
|
|
if (typByVal)
|
|
|
|
return val;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *newVal;
|
|
|
|
|
|
|
|
if (typLen == -1) /* variable length type? */
|
|
|
|
typLen = VARSIZE((struct varlena *) DatumGetPointer(val));
|
|
|
|
newVal = (char *) palloc(typLen);
|
|
|
|
memcpy(newVal, DatumGetPointer(val), typLen);
|
|
|
|
return PointerGetDatum(newVal);
|
|
|
|
}
|
|
|
|
}
|
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
|
1999-09-26 23:21:15 +02:00
|
|
|
* the appropriate attribute for each aggregate function use (Aggref
|
|
|
|
* node) appearing in the targetlist or qual of the node. The number
|
|
|
|
* of tuples to aggregate over depends on whether a GROUP BY clause is
|
|
|
|
* present. We can produce an aggregate result row per group, or just
|
|
|
|
* one for the whole query. The value of each aggregate is stored in
|
|
|
|
* the expression context to be used when ExecProject evaluates the
|
|
|
|
* result tuple.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
|
|
|
* 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
|
|
|
*
|
1999-09-26 23:21:15 +02:00
|
|
|
* value1 = initcond1
|
|
|
|
* value2 = initcond2
|
|
|
|
* foreach tuple do
|
|
|
|
* value1 = sfunc1(value1, aggregated_value)
|
|
|
|
* value2 = sfunc2(value2)
|
|
|
|
* value1 = finalfunc(value1, value2)
|
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
|
1999-09-26 23:21:15 +02:00
|
|
|
* assigned directly to value1. sfunc1 isn't applied until value1
|
1999-03-21 20:59:13 +01:00
|
|
|
* is non-NULL.
|
|
|
|
*
|
1999-09-26 23:21:15 +02:00
|
|
|
* sfunc1 is never applied when the current tuple's aggregated_value
|
|
|
|
* is NULL. sfunc2 is applied for each tuple if the aggref is marked
|
|
|
|
* 'usenulls', otherwise it is only applied when aggregated_value is
|
1999-10-08 05:49:55 +02:00
|
|
|
* not NULL. (usenulls was formerly used for COUNT(*), but is no longer
|
|
|
|
* needed for that purpose; as of 10/1999 the support for usenulls is
|
|
|
|
* dead code. I have not removed it because it seems like a potentially
|
|
|
|
* useful feature for user-defined aggregates. We'd just need to add a
|
|
|
|
* flag column to pg_aggregate and a parameter to CREATE AGGREGATE...)
|
1999-09-26 23:21:15 +02:00
|
|
|
*
|
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
|
|
|
*
|
|
|
|
* ------------------------------------------
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
ExprContext *econtext;
|
1997-09-07 07:04:48 +02:00
|
|
|
ProjectionInfo *projInfo;
|
1999-09-26 23:21:15 +02:00
|
|
|
Datum *aggvalues;
|
|
|
|
bool *aggnulls;
|
|
|
|
AggStatePerAgg peragg;
|
1997-09-07 07:04:48 +02:00
|
|
|
TupleTableSlot *resultSlot;
|
1999-09-26 23:21:15 +02:00
|
|
|
HeapTuple inputTuple;
|
|
|
|
int aggno;
|
1998-01-15 20:00:16 +01:00
|
|
|
List *alist;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool isDone;
|
1999-09-26 23:21:15 +02:00
|
|
|
bool isNull;
|
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
|
|
|
* ---------------------
|
|
|
|
*/
|
1999-09-26 23:21:15 +02:00
|
|
|
aggstate = node->aggstate;
|
|
|
|
estate = node->plan.state;
|
|
|
|
outerPlan = outerPlan(node);
|
|
|
|
econtext = aggstate->csstate.cstate.cs_ExprContext;
|
|
|
|
aggvalues = econtext->ecxt_aggvalues;
|
|
|
|
aggnulls = econtext->ecxt_aggnulls;
|
|
|
|
projInfo = aggstate->csstate.cstate.cs_ProjInfo;
|
|
|
|
peragg = aggstate->peragg;
|
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
|
|
|
|
{
|
|
|
|
if (aggstate->agg_done)
|
|
|
|
return NULL;
|
1998-07-19 07:49:26 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* Initialize working state for a new input tuple group
|
|
|
|
*/
|
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-09-26 23:21:15 +02:00
|
|
|
AggStatePerAgg peraggstate = &peragg[++aggno];
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* (Re)set value1 and value2 to their initial values.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-09-26 23:21:15 +02:00
|
|
|
if (OidIsValid(peraggstate->xfn1_oid) &&
|
|
|
|
! peraggstate->initValue1IsNull)
|
|
|
|
peraggstate->value1 = copyDatum(peraggstate->initValue1,
|
|
|
|
peraggstate->transtype1Len,
|
|
|
|
peraggstate->transtype1ByVal);
|
|
|
|
else
|
|
|
|
peraggstate->value1 = (Datum) NULL;
|
|
|
|
peraggstate->value1IsNull = peraggstate->initValue1IsNull;
|
|
|
|
|
|
|
|
if (OidIsValid(peraggstate->xfn2_oid) &&
|
|
|
|
! peraggstate->initValue2IsNull)
|
|
|
|
peraggstate->value2 = copyDatum(peraggstate->initValue2,
|
|
|
|
peraggstate->transtype2Len,
|
|
|
|
peraggstate->transtype2ByVal);
|
|
|
|
else
|
|
|
|
peraggstate->value2 = (Datum) NULL;
|
|
|
|
peraggstate->value2IsNull = peraggstate->initValue2IsNull;
|
|
|
|
|
|
|
|
/* ------------------------------------------
|
|
|
|
* If the initial value for the first transition function
|
|
|
|
* doesn't exist in the pg_aggregate table then we will let
|
|
|
|
* the first value returned from the outer procNode become
|
|
|
|
* the initial value. (This is useful for aggregates like
|
|
|
|
* max{} and min{}.) The noInitValue flag signals that we
|
|
|
|
* still need to do this.
|
|
|
|
* ------------------------------------------
|
|
|
|
*/
|
|
|
|
peraggstate->noInitValue = peraggstate->initValue1IsNull;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
inputTuple = NULL; /* no saved input tuple yet */
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/* ----------------
|
1999-09-26 23:21:15 +02:00
|
|
|
* for each tuple from the outer plan, update all the aggregates
|
1998-09-01 06:40:42 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
for (;;)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
TupleTableSlot *outerslot;
|
|
|
|
|
|
|
|
outerslot = ExecProcNode(outerPlan, (Plan *) node);
|
1998-11-27 20:52:36 +01:00
|
|
|
if (TupIsNull(outerslot))
|
1998-09-01 06:40:42 +02:00
|
|
|
break;
|
1999-09-26 23:21:15 +02:00
|
|
|
econtext->ecxt_scantuple = outerslot;
|
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-09-26 23:21:15 +02:00
|
|
|
Aggref *aggref = (Aggref *) lfirst(alist);
|
|
|
|
AggStatePerAgg peraggstate = &peragg[++aggno];
|
|
|
|
Datum newVal;
|
|
|
|
Datum args[2];
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
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 */
|
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
if (OidIsValid(peraggstate->xfn1_oid) && !isNull)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
if (peraggstate->noInitValue)
|
1997-09-07 07:04:48 +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
|
1999-09-26 23:21:15 +02:00
|
|
|
* initial value for value1. XXX We assume,
|
|
|
|
* without having checked, that the agg's input type
|
|
|
|
* is binary-compatible with its transtype1!
|
1999-04-29 03:13:13 +02:00
|
|
|
*
|
1999-09-26 23:21:15 +02:00
|
|
|
* We have to copy the datum since the tuple from
|
|
|
|
* which it came will be freed on the next iteration
|
|
|
|
* of the scan.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
1999-09-26 23:21:15 +02:00
|
|
|
peraggstate->value1 = copyDatum(newVal,
|
|
|
|
peraggstate->transtype1Len,
|
|
|
|
peraggstate->transtype1ByVal);
|
|
|
|
peraggstate->value1IsNull = false;
|
|
|
|
peraggstate->noInitValue = false;
|
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-09-26 23:21:15 +02:00
|
|
|
/* apply transition function 1 */
|
|
|
|
args[0] = peraggstate->value1;
|
1998-09-01 06:40:42 +02:00
|
|
|
args[1] = newVal;
|
1999-09-26 23:21:15 +02:00
|
|
|
newVal = (Datum) fmgr_c(&peraggstate->xfn1,
|
|
|
|
(FmgrValues *) args,
|
|
|
|
&isNull);
|
|
|
|
if (! peraggstate->transtype1ByVal)
|
|
|
|
pfree(peraggstate->value1);
|
|
|
|
peraggstate->value1 = newVal;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
if (OidIsValid(peraggstate->xfn2_oid))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
/* apply transition function 2 */
|
|
|
|
args[0] = peraggstate->value2;
|
|
|
|
isNull = false; /* value2 cannot be null, currently */
|
|
|
|
newVal = (Datum) fmgr_c(&peraggstate->xfn2,
|
|
|
|
(FmgrValues *) args,
|
|
|
|
&isNull);
|
|
|
|
if (! peraggstate->transtype2ByVal)
|
|
|
|
pfree(peraggstate->value2);
|
|
|
|
peraggstate->value2 = newVal;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
1999-09-26 23:21:15 +02:00
|
|
|
* Keep a copy of the first input tuple for the projection.
|
|
|
|
* (We only need one since only the GROUP BY columns in it
|
|
|
|
* can be referenced, and these will be the same for all
|
|
|
|
* tuples aggregated over.)
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
1999-09-26 23:21:15 +02:00
|
|
|
if (!inputTuple)
|
|
|
|
inputTuple = heap_copytuple(outerslot->val);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* Done scanning input tuple group.
|
|
|
|
* Finalize each aggregate calculation.
|
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-09-26 23:21:15 +02:00
|
|
|
AggStatePerAgg peraggstate = &peragg[++aggno];
|
|
|
|
char *args[2];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* XXX For now, only apply finalfn if we got at least one
|
|
|
|
* non-null input value. This prevents zero divide in AVG().
|
|
|
|
* If we had cleaner handling of null inputs/results in functions,
|
|
|
|
* we could probably take out this hack and define the result
|
|
|
|
* for no inputs as whatever finalfn returns for null input.
|
|
|
|
*/
|
|
|
|
if (OidIsValid(peraggstate->finalfn_oid) &&
|
|
|
|
! peraggstate->noInitValue)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
if (peraggstate->finalfn.fn_nargs > 1)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
args[0] = (char *) peraggstate->value1;
|
|
|
|
args[1] = (char *) peraggstate->value2;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1999-09-26 23:21:15 +02:00
|
|
|
else if (OidIsValid(peraggstate->xfn1_oid))
|
|
|
|
args[0] = (char *) peraggstate->value1;
|
|
|
|
else if (OidIsValid(peraggstate->xfn2_oid))
|
|
|
|
args[0] = (char *) peraggstate->value2;
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
1999-09-26 23:21:15 +02:00
|
|
|
elog(ERROR, "ExecAgg: no valid transition functions??");
|
|
|
|
aggnulls[aggno] = false;
|
|
|
|
aggvalues[aggno] = (Datum) fmgr_c(&peraggstate->finalfn,
|
|
|
|
(FmgrValues *) args,
|
|
|
|
&(aggnulls[aggno]));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-09-26 23:21:15 +02:00
|
|
|
else if (OidIsValid(peraggstate->xfn1_oid))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
/* Return value1 */
|
|
|
|
aggvalues[aggno] = peraggstate->value1;
|
|
|
|
aggnulls[aggno] = peraggstate->value1IsNull;
|
|
|
|
/* prevent pfree below */
|
|
|
|
peraggstate->value1IsNull = true;
|
|
|
|
}
|
|
|
|
else if (OidIsValid(peraggstate->xfn2_oid))
|
|
|
|
{
|
|
|
|
/* Return value2 */
|
|
|
|
aggvalues[aggno] = peraggstate->value2;
|
|
|
|
aggnulls[aggno] = peraggstate->value2IsNull;
|
|
|
|
/* prevent pfree below */
|
|
|
|
peraggstate->value2IsNull = true;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-09-01 06:40:42 +02:00
|
|
|
elog(ERROR, "ExecAgg: no valid transition functions??");
|
1999-09-26 23:21:15 +02:00
|
|
|
|
|
|
|
/*
|
1999-09-28 04:03:19 +02:00
|
|
|
* Release any per-group working storage, unless we're passing
|
|
|
|
* it back as the result of the aggregate.
|
1999-09-26 23:21:15 +02:00
|
|
|
*/
|
|
|
|
if (OidIsValid(peraggstate->xfn1_oid) &&
|
|
|
|
! peraggstate->value1IsNull &&
|
|
|
|
! peraggstate->transtype1ByVal)
|
|
|
|
pfree(peraggstate->value1);
|
|
|
|
|
|
|
|
if (OidIsValid(peraggstate->xfn2_oid) &&
|
|
|
|
! peraggstate->value2IsNull &&
|
|
|
|
! peraggstate->transtype2ByVal)
|
|
|
|
pfree(peraggstate->value2);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
1999-09-26 23:21:15 +02:00
|
|
|
* If the outerPlan is a Group node, we will reach here after each
|
|
|
|
* group. We are not done unless the Group node is done (a little
|
|
|
|
* ugliness here while we reach into the Group's state to find out).
|
|
|
|
* Furthermore, when grouping we return nothing at all unless we
|
|
|
|
* had some input tuple(s). By the nature of Group, there are
|
|
|
|
* no empty groups, so if we get here with no input the whole scan
|
|
|
|
* is empty.
|
|
|
|
*
|
|
|
|
* If the outerPlan isn't a Group, we are done when we get here,
|
|
|
|
* and we will emit a (single) tuple even if there were no input
|
|
|
|
* tuples.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
1999-09-26 23:21:15 +02:00
|
|
|
if (IsA(outerPlan, Group))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
|
|
|
/* aggregation over groups */
|
|
|
|
aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
|
1999-09-26 23:21:15 +02:00
|
|
|
/* check for no groups */
|
|
|
|
if (inputTuple == NULL)
|
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
1999-10-30 03:18:16 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
aggstate->agg_done = true;
|
1999-10-30 03:18:16 +02:00
|
|
|
/*
|
|
|
|
* If inputtuple==NULL (ie, the outerPlan didn't return anything),
|
|
|
|
* create a dummy all-nulls input tuple for use by execProject.
|
|
|
|
* 99.44% of the time this is a waste of cycles, because
|
|
|
|
* ordinarily the projected output tuple's targetlist cannot
|
|
|
|
* contain any direct (non-aggregated) references to input
|
|
|
|
* columns, so the dummy tuple will not be referenced. However
|
|
|
|
* there are special cases where this isn't so --- in particular
|
|
|
|
* an UPDATE involving an aggregate will have a targetlist
|
|
|
|
* reference to ctid. We need to return a null for ctid in that
|
|
|
|
* situation, not coredump.
|
|
|
|
*
|
|
|
|
* The values returned for the aggregates will be the initial
|
|
|
|
* values of the transition functions.
|
|
|
|
*/
|
|
|
|
if (inputTuple == NULL)
|
|
|
|
{
|
|
|
|
TupleDesc tupType;
|
|
|
|
Datum *tupValue;
|
|
|
|
char *null_array;
|
|
|
|
AttrNumber attnum;
|
|
|
|
|
|
|
|
tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
|
|
|
|
tupValue = projInfo->pi_tupValue;
|
|
|
|
null_array = (char *) palloc(sizeof(char) * tupType->natts);
|
|
|
|
for (attnum = 0; attnum < tupType->natts; attnum++)
|
|
|
|
null_array[attnum] = 'n';
|
|
|
|
inputTuple = heap_formtuple(tupType, tupValue, null_array);
|
|
|
|
pfree(null_array);
|
|
|
|
}
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
1999-10-30 03:18:16 +02:00
|
|
|
* Store the representative input tuple in the tuple table slot
|
|
|
|
* reserved for it.
|
1999-09-26 23:21:15 +02:00
|
|
|
*/
|
|
|
|
ExecStoreTuple(inputTuple,
|
1998-09-01 06:40:42 +02:00
|
|
|
aggstate->csstate.css_ScanTupleSlot,
|
|
|
|
InvalidBuffer,
|
1999-09-26 23:21:15 +02:00
|
|
|
true);
|
1998-09-01 06:40:42 +02:00
|
|
|
econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
|
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* Form a projection tuple using the aggregate results and the
|
|
|
|
* representative input tuple. Store it in the result tuple slot,
|
|
|
|
* and return it if it meets my qual condition.
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
resultSlot = ExecProject(projInfo, &isDone);
|
|
|
|
|
|
|
|
/*
|
1999-09-26 23:21:15 +02:00
|
|
|
* If the completed tuple does not match the qualifications,
|
|
|
|
* it is ignored and we loop back to try to process another group.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
}
|
1999-09-26 23:21:15 +02:00
|
|
|
while (! ExecQual(node->plan.qual, econtext));
|
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
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
AggState *aggstate;
|
|
|
|
AggStatePerAgg peragg;
|
|
|
|
Plan *outerPlan;
|
|
|
|
ExprContext *econtext;
|
|
|
|
int numaggs,
|
|
|
|
aggno;
|
|
|
|
List *alist;
|
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));
|
1999-09-26 23:21:15 +02:00
|
|
|
aggstate->numaggs = numaggs = length(aggstate->aggs);
|
1999-08-21 05:49:17 +02:00
|
|
|
if (numaggs <= 0)
|
1999-09-26 23:21:15 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This used to be treated as an error, but we can't do that anymore
|
|
|
|
* because constant-expression simplification could optimize away
|
|
|
|
* all of the Aggrefs in the targetlist and qual. So, just make a
|
|
|
|
* debug note, and force numaggs positive so that palloc()s below
|
|
|
|
* don't choke.
|
|
|
|
*/
|
|
|
|
elog(DEBUG, "ExecInitAgg: could not find any aggregate functions");
|
|
|
|
numaggs = 1;
|
|
|
|
}
|
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);
|
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* Set up aggregate-result storage in the expr context,
|
|
|
|
* and also allocate my private per-agg working storage
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
econtext = aggstate->csstate.cstate.cs_ExprContext;
|
1999-09-26 23:21:15 +02:00
|
|
|
econtext->ecxt_aggvalues = (Datum *) palloc(sizeof(Datum) * numaggs);
|
|
|
|
MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * numaggs);
|
|
|
|
econtext->ecxt_aggnulls = (bool *) palloc(sizeof(bool) * numaggs);
|
|
|
|
MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * numaggs);
|
|
|
|
|
|
|
|
peragg = (AggStatePerAgg) palloc(sizeof(AggStatePerAggData) * numaggs);
|
|
|
|
MemSet(peragg, 0, sizeof(AggStatePerAggData) * numaggs);
|
|
|
|
aggstate->peragg = peragg;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-09-26 23:21:15 +02:00
|
|
|
* initialize child nodes
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
outerPlan = outerPlan(node);
|
|
|
|
ExecInitNode(outerPlan, estate, (Plan *) node);
|
|
|
|
|
|
|
|
/* ----------------
|
1999-10-08 05:49:55 +02:00
|
|
|
* initialize source tuple type.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
|
|
|
|
|
|
|
|
/*
|
1999-10-08 05:49:55 +02:00
|
|
|
* Initialize result tuple type and projection info.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate);
|
|
|
|
ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate);
|
|
|
|
|
1999-09-26 23:21:15 +02:00
|
|
|
/*
|
|
|
|
* Perform lookups of aggregate function info, and initialize the
|
|
|
|
* unchanging fields of the per-agg data
|
|
|
|
*/
|
|
|
|
aggno = -1;
|
|
|
|
foreach(alist, aggstate->aggs)
|
|
|
|
{
|
|
|
|
Aggref *aggref = (Aggref *) lfirst(alist);
|
|
|
|
AggStatePerAgg peraggstate = &peragg[++aggno];
|
|
|
|
char *aggname = aggref->aggname;
|
|
|
|
HeapTuple aggTuple;
|
|
|
|
Form_pg_aggregate aggform;
|
|
|
|
Type typeInfo;
|
|
|
|
Oid xfn1_oid,
|
|
|
|
xfn2_oid,
|
|
|
|
finalfn_oid;
|
|
|
|
|
|
|
|
/* Mark Aggref node with its associated index in the result array */
|
|
|
|
aggref->aggno = aggno;
|
|
|
|
|
|
|
|
aggTuple = SearchSysCacheTuple(AGGNAME,
|
|
|
|
PointerGetDatum(aggname),
|
|
|
|
ObjectIdGetDatum(aggref->basetype),
|
|
|
|
0, 0);
|
|
|
|
if (!HeapTupleIsValid(aggTuple))
|
|
|
|
elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
|
|
|
|
aggname,
|
|
|
|
typeidTypeName(aggref->basetype));
|
|
|
|
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
|
|
|
|
|
|
|
|
peraggstate->initValue1 = (Datum)
|
|
|
|
AggNameGetInitVal(aggname,
|
|
|
|
aggform->aggbasetype,
|
|
|
|
1,
|
|
|
|
&peraggstate->initValue1IsNull);
|
|
|
|
|
|
|
|
peraggstate->initValue2 = (Datum)
|
|
|
|
AggNameGetInitVal(aggname,
|
|
|
|
aggform->aggbasetype,
|
|
|
|
2,
|
|
|
|
&peraggstate->initValue2IsNull);
|
|
|
|
|
|
|
|
peraggstate->xfn1_oid = xfn1_oid = aggform->aggtransfn1;
|
|
|
|
peraggstate->xfn2_oid = xfn2_oid = aggform->aggtransfn2;
|
|
|
|
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
|
|
|
|
|
|
|
|
if (OidIsValid(xfn1_oid))
|
|
|
|
{
|
|
|
|
fmgr_info(xfn1_oid, &peraggstate->xfn1);
|
|
|
|
/* If a transfn1 is specified, transtype1 had better be, too */
|
|
|
|
typeInfo = typeidType(aggform->aggtranstype1);
|
|
|
|
peraggstate->transtype1Len = typeLen(typeInfo);
|
|
|
|
peraggstate->transtype1ByVal = typeByVal(typeInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OidIsValid(xfn2_oid))
|
|
|
|
{
|
|
|
|
fmgr_info(xfn2_oid, &peraggstate->xfn2);
|
|
|
|
/* If a transfn2 is specified, transtype2 had better be, too */
|
|
|
|
typeInfo = typeidType(aggform->aggtranstype2);
|
|
|
|
peraggstate->transtype2Len = typeLen(typeInfo);
|
|
|
|
peraggstate->transtype2ByVal = typeByVal(typeInfo);
|
|
|
|
/* ------------------------------------------
|
|
|
|
* 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 (peraggstate->initValue2IsNull)
|
|
|
|
elog(ERROR, "ExecInitAgg: agginitval2 is null");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OidIsValid(finalfn_oid))
|
|
|
|
{
|
|
|
|
fmgr_info(finalfn_oid, &peraggstate->finalfn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1997-09-08 22:59:27 +02:00
|
|
|
ExecEndAgg(Agg *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-09-26 23:21:15 +02:00
|
|
|
AggState *aggstate = node->aggstate;
|
1997-09-08 04:41:22 +02:00
|
|
|
Plan *outerPlan;
|
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
|
|
|
}
|
|
|
|
|
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;
|
1999-09-26 23:21:15 +02:00
|
|
|
MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * aggstate->numaggs);
|
|
|
|
MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * aggstate->numaggs);
|
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
|
|
|
}
|