mirror of
https://github.com/postgres/postgres.git
synced 2025-08-21 10:42:50 +03:00
Aggregate functions now support multiple input arguments. I also took
the opportunity to treat COUNT(*) as a zero-argument aggregate instead of the old hack that equated it to COUNT(1); this is materially cleaner (no more weird ANYOID cases) and ought to be at least a tiny bit faster. Original patch by Sergey Koposov; review, documentation, simple regression tests, pg_dump and psql support by moi.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.81 2006/07/14 14:52:17 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.82 2006/07/27 19:52:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,7 +42,8 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
|
||||
void
|
||||
AggregateCreate(const char *aggName,
|
||||
Oid aggNamespace,
|
||||
Oid aggBaseType,
|
||||
Oid *aggArgTypes,
|
||||
int numArgs,
|
||||
List *aggtransfnName,
|
||||
List *aggfinalfnName,
|
||||
List *aggsortopName,
|
||||
@@ -57,9 +58,10 @@ AggregateCreate(const char *aggName,
|
||||
Oid transfn;
|
||||
Oid finalfn = InvalidOid; /* can be omitted */
|
||||
Oid sortop = InvalidOid; /* can be omitted */
|
||||
bool hasPolyArg;
|
||||
Oid rettype;
|
||||
Oid finaltype;
|
||||
Oid fnArgs[2]; /* we only deal with 1- and 2-arg fns */
|
||||
Oid *fnArgs;
|
||||
int nargs_transfn;
|
||||
Oid procOid;
|
||||
TupleDesc tupDesc;
|
||||
@@ -74,27 +76,34 @@ AggregateCreate(const char *aggName,
|
||||
if (!aggtransfnName)
|
||||
elog(ERROR, "aggregate must have a transition function");
|
||||
|
||||
/* check for polymorphic arguments */
|
||||
hasPolyArg = false;
|
||||
for (i = 0; i < numArgs; i++)
|
||||
{
|
||||
if (aggArgTypes[i] == ANYARRAYOID ||
|
||||
aggArgTypes[i] == ANYELEMENTOID)
|
||||
{
|
||||
hasPolyArg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If transtype is polymorphic, basetype must be polymorphic also; else we
|
||||
* will have no way to deduce the actual transtype.
|
||||
* If transtype is polymorphic, must have polymorphic argument also;
|
||||
* else we will have no way to deduce the actual transtype.
|
||||
*/
|
||||
if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) &&
|
||||
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
|
||||
if (!hasPolyArg &&
|
||||
(aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("cannot determine transition data type"),
|
||||
errdetail("An aggregate using \"anyarray\" or \"anyelement\" as "
|
||||
"transition type must have one of them as its base type.")));
|
||||
errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type.")));
|
||||
|
||||
/* handle transfn */
|
||||
/* find the transfn */
|
||||
nargs_transfn = numArgs + 1;
|
||||
fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
|
||||
fnArgs[0] = aggTransType;
|
||||
if (aggBaseType == ANYOID)
|
||||
nargs_transfn = 1;
|
||||
else
|
||||
{
|
||||
fnArgs[1] = aggBaseType;
|
||||
nargs_transfn = 2;
|
||||
}
|
||||
memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
|
||||
transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
|
||||
&rettype);
|
||||
|
||||
@@ -123,13 +132,14 @@ AggregateCreate(const char *aggName,
|
||||
proc = (Form_pg_proc) GETSTRUCT(tup);
|
||||
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure input type
|
||||
* and transtype are the same (or at least binary-compatible), so that
|
||||
* If the transfn is strict and the initval is NULL, make sure first input
|
||||
* type and transtype are the same (or at least binary-compatible), so that
|
||||
* it's OK to use the first input value as the initial transValue.
|
||||
*/
|
||||
if (proc->proisstrict && agginitval == NULL)
|
||||
{
|
||||
if (!IsBinaryCoercible(aggBaseType, aggTransType))
|
||||
if (numArgs < 1 ||
|
||||
!IsBinaryCoercible(aggArgTypes[0], aggTransType))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
|
||||
@@ -153,32 +163,37 @@ AggregateCreate(const char *aggName,
|
||||
Assert(OidIsValid(finaltype));
|
||||
|
||||
/*
|
||||
* If finaltype (i.e. aggregate return type) is polymorphic, basetype must
|
||||
* If finaltype (i.e. aggregate return type) is polymorphic, inputs must
|
||||
* be polymorphic also, else parser will fail to deduce result type.
|
||||
* (Note: given the previous test on transtype and basetype, this cannot
|
||||
* (Note: given the previous test on transtype and inputs, this cannot
|
||||
* happen, unless someone has snuck a finalfn definition into the catalogs
|
||||
* that itself violates the rule against polymorphic result with no
|
||||
* polymorphic input.)
|
||||
*/
|
||||
if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) &&
|
||||
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
|
||||
if (!hasPolyArg &&
|
||||
(finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("cannot determine result data type"),
|
||||
errdetail("An aggregate returning \"anyarray\" or \"anyelement\" "
|
||||
"must have one of them as its base type.")));
|
||||
"must have at least one argument of either type.")));
|
||||
|
||||
/* handle sortop, if supplied */
|
||||
if (aggsortopName)
|
||||
{
|
||||
if (numArgs != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("sort operator can only be specified for single-argument aggregates")));
|
||||
sortop = LookupOperName(NULL, aggsortopName,
|
||||
aggBaseType, aggBaseType,
|
||||
aggArgTypes[0], aggArgTypes[0],
|
||||
false, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything looks okay. Try to create the pg_proc entry for the
|
||||
* aggregate. (This could fail if there's already a conflicting entry.)
|
||||
*/
|
||||
fnArgs[0] = aggBaseType;
|
||||
|
||||
procOid = ProcedureCreate(aggName,
|
||||
aggNamespace,
|
||||
@@ -195,7 +210,8 @@ AggregateCreate(const char *aggName,
|
||||
false, /* isStrict (not needed for agg) */
|
||||
PROVOLATILE_IMMUTABLE, /* volatility (not
|
||||
* needed for agg) */
|
||||
buildoidvector(fnArgs, 1), /* paramTypes */
|
||||
buildoidvector(aggArgTypes,
|
||||
numArgs), /* paramTypes */
|
||||
PointerGetDatum(NULL), /* allParamTypes */
|
||||
PointerGetDatum(NULL), /* parameterModes */
|
||||
PointerGetDatum(NULL)); /* parameterNames */
|
||||
@@ -279,6 +295,8 @@ lookup_agg_function(List *fnName,
|
||||
Oid *true_oid_array;
|
||||
FuncDetailCode fdresult;
|
||||
AclResult aclresult;
|
||||
int i;
|
||||
bool allPolyArgs = true;
|
||||
|
||||
/*
|
||||
* func_get_detail looks up the function in the catalogs, does
|
||||
@@ -307,13 +325,17 @@ lookup_agg_function(List *fnName,
|
||||
* If the given type(s) are all polymorphic, there's nothing we can check.
|
||||
* Otherwise, enforce consistency, and possibly refine the result type.
|
||||
*/
|
||||
if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) &&
|
||||
(nargs == 1 ||
|
||||
(input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
/* nothing to check here */
|
||||
if (input_types[i] != ANYARRAYOID &&
|
||||
input_types[i] != ANYELEMENTOID)
|
||||
{
|
||||
allPolyArgs = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!allPolyArgs)
|
||||
{
|
||||
*rettype = enforce_generic_type_consistency(input_types,
|
||||
true_oid_array,
|
||||
@@ -325,22 +347,16 @@ lookup_agg_function(List *fnName,
|
||||
* func_get_detail will find functions requiring run-time argument type
|
||||
* coercion, but nodeAgg.c isn't prepared to deal with that
|
||||
*/
|
||||
if (true_oid_array[0] != ANYARRAYOID &&
|
||||
true_oid_array[0] != ANYELEMENTOID &&
|
||||
!IsBinaryCoercible(input_types[0], true_oid_array[0]))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("function %s requires run-time type coercion",
|
||||
func_signature_string(fnName, nargs, true_oid_array))));
|
||||
|
||||
if (nargs == 2 &&
|
||||
true_oid_array[1] != ANYARRAYOID &&
|
||||
true_oid_array[1] != ANYELEMENTOID &&
|
||||
!IsBinaryCoercible(input_types[1], true_oid_array[1]))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("function %s requires run-time type coercion",
|
||||
func_signature_string(fnName, nargs, true_oid_array))));
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
if (true_oid_array[i] != ANYARRAYOID &&
|
||||
true_oid_array[i] != ANYELEMENTOID &&
|
||||
!IsBinaryCoercible(input_types[i], true_oid_array[i]))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("function %s requires run-time type coercion",
|
||||
func_signature_string(fnName, nargs, true_oid_array))));
|
||||
}
|
||||
|
||||
/* Check aggregate creator has permission to call the function */
|
||||
aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.37 2006/07/14 14:52:18 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.38 2006/07/27 19:52:04 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -57,7 +57,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
|
||||
TypeName *baseType = NULL;
|
||||
TypeName *transType = NULL;
|
||||
char *initval = NULL;
|
||||
Oid baseTypeId;
|
||||
Oid *aggArgTypes;
|
||||
int numArgs;
|
||||
Oid transTypeId;
|
||||
ListCell *pl;
|
||||
|
||||
@@ -116,12 +117,13 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
|
||||
errmsg("aggregate sfunc must be specified")));
|
||||
|
||||
/*
|
||||
* look up the aggregate's input datatype.
|
||||
* look up the aggregate's input datatype(s).
|
||||
*/
|
||||
if (oldstyle)
|
||||
{
|
||||
/*
|
||||
* Old style: use basetype parameter. This supports only one input.
|
||||
* Old style: use basetype parameter. This supports aggregates
|
||||
* of zero or one input, with input type ANY meaning zero inputs.
|
||||
*
|
||||
* Historically we allowed the command to look like basetype = 'ANY'
|
||||
* so we must do a case-insensitive comparison for the name ANY. Ugh.
|
||||
@@ -132,37 +134,37 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
|
||||
errmsg("aggregate input type must be specified")));
|
||||
|
||||
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
|
||||
baseTypeId = ANYOID;
|
||||
{
|
||||
numArgs = 0;
|
||||
aggArgTypes = NULL;
|
||||
}
|
||||
else
|
||||
baseTypeId = typenameTypeId(NULL, baseType);
|
||||
{
|
||||
numArgs = 1;
|
||||
aggArgTypes = (Oid *) palloc(sizeof(Oid));
|
||||
aggArgTypes[0] = typenameTypeId(NULL, baseType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* New style: args is a list of TypeNames. For the moment, though,
|
||||
* we allow at most one.
|
||||
* New style: args is a list of TypeNames (possibly zero of 'em).
|
||||
*/
|
||||
ListCell *lc;
|
||||
int i = 0;
|
||||
|
||||
if (baseType != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("basetype is redundant with aggregate input type specification")));
|
||||
|
||||
if (args == NIL)
|
||||
numArgs = list_length(args);
|
||||
aggArgTypes = (Oid *) palloc(sizeof(Oid) * numArgs);
|
||||
foreach(lc, args)
|
||||
{
|
||||
/* special case for agg(*) */
|
||||
baseTypeId = ANYOID;
|
||||
}
|
||||
else if (list_length(args) != 1)
|
||||
{
|
||||
/* temporarily reject > 1 arg */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("aggregates can have only one input")));
|
||||
baseTypeId = InvalidOid; /* keep compiler quiet */
|
||||
}
|
||||
else
|
||||
{
|
||||
baseTypeId = typenameTypeId(NULL, (TypeName *) linitial(args));
|
||||
TypeName *curTypeName = (TypeName *) lfirst(lc);
|
||||
|
||||
aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +189,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
|
||||
*/
|
||||
AggregateCreate(aggName, /* aggregate name */
|
||||
aggNamespace, /* namespace */
|
||||
baseTypeId, /* type of data being aggregated */
|
||||
aggArgTypes, /* input data type(s) */
|
||||
numArgs,
|
||||
transfuncName, /* step function name */
|
||||
finalfuncName, /* final function name */
|
||||
sortoperatorName, /* sort operator name */
|
||||
@@ -211,7 +214,7 @@ RemoveAggregate(RemoveFuncStmt *stmt)
|
||||
|
||||
/* Look up function and make sure it's an aggregate */
|
||||
procOid = LookupAggNameTypeNames(aggName, aggArgs, stmt->missing_ok);
|
||||
|
||||
|
||||
if (!OidIsValid(procOid))
|
||||
{
|
||||
/* we only get here if stmt->missing_ok is true */
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.192 2006/07/14 14:52:19 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.193 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -3174,10 +3174,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
||||
aggstate->aggs = lcons(astate, aggstate->aggs);
|
||||
naggs = ++aggstate->numaggs;
|
||||
|
||||
astate->target = ExecInitExpr(aggref->target, parent);
|
||||
astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
|
||||
parent);
|
||||
|
||||
/*
|
||||
* Complain if the aggregate's argument contains any
|
||||
* Complain if the aggregate's arguments contain any
|
||||
* aggregates; nested agg functions are semantically
|
||||
* nonsensical. (This should have been caught earlier,
|
||||
* but we defend against it here anyway.)
|
||||
|
@@ -6,8 +6,8 @@
|
||||
* ExecAgg evaluates each aggregate in the following steps:
|
||||
*
|
||||
* transvalue = initcond
|
||||
* foreach input_value do
|
||||
* transvalue = transfunc(transvalue, input_value)
|
||||
* foreach input_tuple do
|
||||
* transvalue = transfunc(transvalue, input_value(s))
|
||||
* result = finalfunc(transvalue)
|
||||
*
|
||||
* If a finalfunc is not supplied then the result is just the ending
|
||||
@@ -16,12 +16,12 @@
|
||||
* If transfunc is marked "strict" in pg_proc and initcond is NULL,
|
||||
* then the first non-NULL input_value is assigned directly to transvalue,
|
||||
* and transfunc isn't applied until the second non-NULL input_value.
|
||||
* The agg's input type and transtype must be the same in this case!
|
||||
* The agg's first input type and transtype must be the same in this case!
|
||||
*
|
||||
* If transfunc is marked "strict" then NULL input_values are skipped,
|
||||
* keeping the previous transvalue. If transfunc is not strict then it
|
||||
* is called for every input tuple and must deal with NULL initcond
|
||||
* or NULL input_value for itself.
|
||||
* or NULL input_values for itself.
|
||||
*
|
||||
* If finalfunc is marked "strict" then it is not called when the
|
||||
* ending transvalue is NULL, instead a NULL result is created
|
||||
@@ -61,7 +61,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.144 2006/07/14 14:52:19 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.145 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -103,6 +103,9 @@ typedef struct AggStatePerAggData
|
||||
AggrefExprState *aggrefstate;
|
||||
Aggref *aggref;
|
||||
|
||||
/* number of input arguments for aggregate */
|
||||
int numArguments;
|
||||
|
||||
/* Oids of transfer functions */
|
||||
Oid transfn_oid;
|
||||
Oid finalfn_oid; /* may be InvalidOid */
|
||||
@@ -214,7 +217,7 @@ static void initialize_aggregates(AggState *aggstate,
|
||||
static void advance_transition_function(AggState *aggstate,
|
||||
AggStatePerAgg peraggstate,
|
||||
AggStatePerGroup pergroupstate,
|
||||
Datum newVal, bool isNull);
|
||||
FunctionCallInfoData *fcinfo);
|
||||
static void advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup);
|
||||
static void process_sorted_aggregate(AggState *aggstate,
|
||||
AggStatePerAgg peraggstate,
|
||||
@@ -314,7 +317,11 @@ initialize_aggregates(AggState *aggstate,
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a new input value, advance the transition function of an aggregate.
|
||||
* Given new input value(s), advance the transition function of an aggregate.
|
||||
*
|
||||
* The new values (and null flags) have been preloaded into argument positions
|
||||
* 1 and up in fcinfo, so that we needn't copy them again to pass to the
|
||||
* transition function. No other fields of fcinfo are assumed valid.
|
||||
*
|
||||
* It doesn't matter which memory context this is called in.
|
||||
*/
|
||||
@@ -322,19 +329,24 @@ static void
|
||||
advance_transition_function(AggState *aggstate,
|
||||
AggStatePerAgg peraggstate,
|
||||
AggStatePerGroup pergroupstate,
|
||||
Datum newVal, bool isNull)
|
||||
FunctionCallInfoData *fcinfo)
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
int numArguments = peraggstate->numArguments;
|
||||
MemoryContext oldContext;
|
||||
Datum newVal;
|
||||
int i;
|
||||
|
||||
if (peraggstate->transfn.fn_strict)
|
||||
{
|
||||
/*
|
||||
* For a strict transfn, nothing happens at a NULL input tuple; we
|
||||
* just keep the prior transValue.
|
||||
* For a strict transfn, nothing happens when there's a NULL input;
|
||||
* we just keep the prior transValue.
|
||||
*/
|
||||
if (isNull)
|
||||
return;
|
||||
for (i = 1; i <= numArguments; i++)
|
||||
{
|
||||
if (fcinfo->argnull[i])
|
||||
return;
|
||||
}
|
||||
if (pergroupstate->noTransValue)
|
||||
{
|
||||
/*
|
||||
@@ -347,7 +359,7 @@ advance_transition_function(AggState *aggstate,
|
||||
* do not need to pfree the old transValue, since it's NULL.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(aggstate->aggcontext);
|
||||
pergroupstate->transValue = datumCopy(newVal,
|
||||
pergroupstate->transValue = datumCopy(fcinfo->arg[1],
|
||||
peraggstate->transtypeByVal,
|
||||
peraggstate->transtypeLen);
|
||||
pergroupstate->transValueIsNull = false;
|
||||
@@ -373,14 +385,13 @@ advance_transition_function(AggState *aggstate,
|
||||
/*
|
||||
* OK to call the transition function
|
||||
*/
|
||||
InitFunctionCallInfoData(fcinfo, &(peraggstate->transfn), 2,
|
||||
InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
|
||||
numArguments + 1,
|
||||
(void *) aggstate, NULL);
|
||||
fcinfo.arg[0] = pergroupstate->transValue;
|
||||
fcinfo.argnull[0] = pergroupstate->transValueIsNull;
|
||||
fcinfo.arg[1] = newVal;
|
||||
fcinfo.argnull[1] = isNull;
|
||||
fcinfo->arg[0] = pergroupstate->transValue;
|
||||
fcinfo->argnull[0] = pergroupstate->transValueIsNull;
|
||||
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
newVal = FunctionCallInvoke(fcinfo);
|
||||
|
||||
/*
|
||||
* If pass-by-ref datatype, must copy the new value into aggcontext and
|
||||
@@ -390,7 +401,7 @@ advance_transition_function(AggState *aggstate,
|
||||
if (!peraggstate->transtypeByVal &&
|
||||
DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue))
|
||||
{
|
||||
if (!fcinfo.isnull)
|
||||
if (!fcinfo->isnull)
|
||||
{
|
||||
MemoryContextSwitchTo(aggstate->aggcontext);
|
||||
newVal = datumCopy(newVal,
|
||||
@@ -402,7 +413,7 @@ advance_transition_function(AggState *aggstate,
|
||||
}
|
||||
|
||||
pergroupstate->transValue = newVal;
|
||||
pergroupstate->transValueIsNull = fcinfo.isnull;
|
||||
pergroupstate->transValueIsNull = fcinfo->isnull;
|
||||
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
}
|
||||
@@ -423,27 +434,46 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
|
||||
|
||||
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
|
||||
{
|
||||
AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
|
||||
AggStatePerGroup pergroupstate = &pergroup[aggno];
|
||||
AggrefExprState *aggrefstate = peraggstate->aggrefstate;
|
||||
Aggref *aggref = peraggstate->aggref;
|
||||
Datum newVal;
|
||||
bool isNull;
|
||||
AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
|
||||
AggStatePerGroup pergroupstate = &pergroup[aggno];
|
||||
AggrefExprState *aggrefstate = peraggstate->aggrefstate;
|
||||
Aggref *aggref = peraggstate->aggref;
|
||||
FunctionCallInfoData fcinfo;
|
||||
int i;
|
||||
ListCell *arg;
|
||||
MemoryContext oldContext;
|
||||
|
||||
newVal = ExecEvalExprSwitchContext(aggrefstate->target, econtext,
|
||||
&isNull, NULL);
|
||||
/* Switch memory context just once for all args */
|
||||
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||
|
||||
/* Evaluate inputs and save in fcinfo */
|
||||
/* We start from 1, since the 0th arg will be the transition value */
|
||||
i = 1;
|
||||
foreach(arg, aggrefstate->args)
|
||||
{
|
||||
ExprState *argstate = (ExprState *) lfirst(arg);
|
||||
|
||||
fcinfo.arg[i] = ExecEvalExpr(argstate, econtext,
|
||||
fcinfo.argnull + i, NULL);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Switch back */
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
if (aggref->aggdistinct)
|
||||
{
|
||||
/* in DISTINCT mode, we may ignore nulls */
|
||||
if (isNull)
|
||||
/* XXX we assume there is only one input column */
|
||||
if (fcinfo.argnull[1])
|
||||
continue;
|
||||
tuplesort_putdatum(peraggstate->sortstate, newVal, isNull);
|
||||
tuplesort_putdatum(peraggstate->sortstate, fcinfo.arg[1],
|
||||
fcinfo.argnull[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
advance_transition_function(aggstate, peraggstate, pergroupstate,
|
||||
newVal, isNull);
|
||||
&fcinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,11 +495,15 @@ process_sorted_aggregate(AggState *aggstate,
|
||||
bool haveOldVal = false;
|
||||
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
|
||||
MemoryContext oldContext;
|
||||
Datum newVal;
|
||||
bool isNull;
|
||||
Datum *newVal;
|
||||
bool *isNull;
|
||||
FunctionCallInfoData fcinfo;
|
||||
|
||||
tuplesort_performsort(peraggstate->sortstate);
|
||||
|
||||
newVal = fcinfo.arg + 1;
|
||||
isNull = fcinfo.argnull + 1;
|
||||
|
||||
/*
|
||||
* Note: if input type is pass-by-ref, the datums returned by the sort are
|
||||
* freshly palloc'd in the per-query context, so we must be careful to
|
||||
@@ -477,13 +511,13 @@ process_sorted_aggregate(AggState *aggstate,
|
||||
*/
|
||||
|
||||
while (tuplesort_getdatum(peraggstate->sortstate, true,
|
||||
&newVal, &isNull))
|
||||
newVal, isNull))
|
||||
{
|
||||
/*
|
||||
* DISTINCT always suppresses nulls, per SQL spec, regardless of the
|
||||
* transition function's strictness.
|
||||
*/
|
||||
if (isNull)
|
||||
if (*isNull)
|
||||
continue;
|
||||
|
||||
/*
|
||||
@@ -495,21 +529,21 @@ process_sorted_aggregate(AggState *aggstate,
|
||||
|
||||
if (haveOldVal &&
|
||||
DatumGetBool(FunctionCall2(&peraggstate->equalfn,
|
||||
oldVal, newVal)))
|
||||
oldVal, *newVal)))
|
||||
{
|
||||
/* equal to prior, so forget this one */
|
||||
if (!peraggstate->inputtypeByVal)
|
||||
pfree(DatumGetPointer(newVal));
|
||||
pfree(DatumGetPointer(*newVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
advance_transition_function(aggstate, peraggstate, pergroupstate,
|
||||
newVal, false);
|
||||
&fcinfo);
|
||||
/* forget the old value, if any */
|
||||
if (haveOldVal && !peraggstate->inputtypeByVal)
|
||||
pfree(DatumGetPointer(oldVal));
|
||||
/* and remember the new one for subsequent equality checks */
|
||||
oldVal = newVal;
|
||||
oldVal = *newVal;
|
||||
haveOldVal = true;
|
||||
}
|
||||
|
||||
@@ -1286,7 +1320,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
|
||||
Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
|
||||
AggStatePerAgg peraggstate;
|
||||
Oid inputType;
|
||||
Oid inputTypes[FUNC_MAX_ARGS];
|
||||
int numArguments;
|
||||
HeapTuple aggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Oid aggtranstype;
|
||||
@@ -1297,6 +1332,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||
*finalfnexpr;
|
||||
Datum textInitVal;
|
||||
int i;
|
||||
ListCell *lc;
|
||||
|
||||
/* Planner should have assigned aggregate to correct level */
|
||||
Assert(aggref->agglevelsup == 0);
|
||||
@@ -1324,13 +1360,19 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||
/* Fill in the peraggstate data */
|
||||
peraggstate->aggrefstate = aggrefstate;
|
||||
peraggstate->aggref = aggref;
|
||||
numArguments = list_length(aggref->args);
|
||||
peraggstate->numArguments = numArguments;
|
||||
|
||||
/*
|
||||
* Get actual datatype of the input. We need this because it may be
|
||||
* different from the agg's declared input type, when the agg accepts
|
||||
* ANY (eg, COUNT(*)) or ANYARRAY or ANYELEMENT.
|
||||
* Get actual datatypes of the inputs. These could be different
|
||||
* from the agg's declared input types, when the agg accepts ANY,
|
||||
* ANYARRAY or ANYELEMENT.
|
||||
*/
|
||||
inputType = exprType((Node *) aggref->target);
|
||||
i = 0;
|
||||
foreach(lc, aggref->args)
|
||||
{
|
||||
inputTypes[i++] = exprType((Node *) lfirst(lc));
|
||||
}
|
||||
|
||||
aggTuple = SearchSysCache(AGGFNOID,
|
||||
ObjectIdGetDatum(aggref->aggfnoid),
|
||||
@@ -1383,21 +1425,23 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||
aggtranstype = aggform->aggtranstype;
|
||||
if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
|
||||
{
|
||||
/* have to fetch the agg's declared input type... */
|
||||
Oid *agg_arg_types;
|
||||
/* have to fetch the agg's declared input types... */
|
||||
Oid *declaredArgTypes;
|
||||
int agg_nargs;
|
||||
|
||||
(void) get_func_signature(aggref->aggfnoid,
|
||||
&agg_arg_types, &agg_nargs);
|
||||
Assert(agg_nargs == 1);
|
||||
aggtranstype = resolve_generic_type(aggtranstype,
|
||||
inputType,
|
||||
agg_arg_types[0]);
|
||||
pfree(agg_arg_types);
|
||||
&declaredArgTypes, &agg_nargs);
|
||||
Assert(agg_nargs == numArguments);
|
||||
aggtranstype = enforce_generic_type_consistency(inputTypes,
|
||||
declaredArgTypes,
|
||||
agg_nargs,
|
||||
aggtranstype);
|
||||
pfree(declaredArgTypes);
|
||||
}
|
||||
|
||||
/* build expression trees using actual argument & result types */
|
||||
build_aggregate_fnexprs(inputType,
|
||||
build_aggregate_fnexprs(inputTypes,
|
||||
numArguments,
|
||||
aggtranstype,
|
||||
aggref->aggtype,
|
||||
transfn_oid,
|
||||
@@ -1437,14 +1481,15 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure input
|
||||
* type and transtype are the same (or at least binary- compatible),
|
||||
* type and transtype are the same (or at least binary-compatible),
|
||||
* so that it's OK to use the first input value as the initial
|
||||
* transValue. This should have been checked at agg definition time,
|
||||
* but just in case...
|
||||
*/
|
||||
if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
|
||||
{
|
||||
if (!IsBinaryCoercible(inputType, aggtranstype))
|
||||
if (numArguments < 1 ||
|
||||
!IsBinaryCoercible(inputTypes[0], aggtranstype))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("aggregate %u needs to have compatible input type and transition type",
|
||||
@@ -1458,14 +1503,25 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||
/* We don't implement DISTINCT aggs in the HASHED case */
|
||||
Assert(node->aggstrategy != AGG_HASHED);
|
||||
|
||||
peraggstate->inputType = inputType;
|
||||
get_typlenbyval(inputType,
|
||||
/*
|
||||
* We don't currently implement DISTINCT aggs for aggs having
|
||||
* more than one argument. This isn't required for anything
|
||||
* in the SQL spec, but really it ought to be implemented for
|
||||
* feature-completeness. FIXME someday.
|
||||
*/
|
||||
if (numArguments != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("DISTINCT is supported only for single-argument aggregates")));
|
||||
|
||||
peraggstate->inputType = inputTypes[0];
|
||||
get_typlenbyval(inputTypes[0],
|
||||
&peraggstate->inputtypeLen,
|
||||
&peraggstate->inputtypeByVal);
|
||||
|
||||
eq_function = equality_oper_funcid(inputType);
|
||||
eq_function = equality_oper_funcid(inputTypes[0]);
|
||||
fmgr_info(eq_function, &(peraggstate->equalfn));
|
||||
peraggstate->sortOperator = ordering_oper_opid(inputType);
|
||||
peraggstate->sortOperator = ordering_oper_opid(inputTypes[0]);
|
||||
peraggstate->sortstate = NULL;
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.343 2006/07/14 14:52:19 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.344 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -743,7 +743,7 @@ _copyAggref(Aggref *from)
|
||||
|
||||
COPY_SCALAR_FIELD(aggfnoid);
|
||||
COPY_SCALAR_FIELD(aggtype);
|
||||
COPY_NODE_FIELD(target);
|
||||
COPY_NODE_FIELD(args);
|
||||
COPY_SCALAR_FIELD(agglevelsup);
|
||||
COPY_SCALAR_FIELD(aggstar);
|
||||
COPY_SCALAR_FIELD(aggdistinct);
|
||||
|
@@ -18,7 +18,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.277 2006/07/14 14:52:20 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.278 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -156,7 +156,7 @@ _equalAggref(Aggref *a, Aggref *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(aggfnoid);
|
||||
COMPARE_SCALAR_FIELD(aggtype);
|
||||
COMPARE_NODE_FIELD(target);
|
||||
COMPARE_NODE_FIELD(args);
|
||||
COMPARE_SCALAR_FIELD(agglevelsup);
|
||||
COMPARE_SCALAR_FIELD(aggstar);
|
||||
COMPARE_SCALAR_FIELD(aggdistinct);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.278 2006/07/14 14:52:20 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.279 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every node type that can appear in stored rules' parsetrees *must*
|
||||
@@ -635,7 +635,7 @@ _outAggref(StringInfo str, Aggref *node)
|
||||
|
||||
WRITE_OID_FIELD(aggfnoid);
|
||||
WRITE_OID_FIELD(aggtype);
|
||||
WRITE_NODE_FIELD(target);
|
||||
WRITE_NODE_FIELD(args);
|
||||
WRITE_UINT_FIELD(agglevelsup);
|
||||
WRITE_BOOL_FIELD(aggstar);
|
||||
WRITE_BOOL_FIELD(aggdistinct);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.191 2006/07/03 22:45:39 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.192 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Path and Plan nodes do not have any readfuncs support, because we
|
||||
@@ -348,7 +348,7 @@ _readAggref(void)
|
||||
|
||||
READ_OID_FIELD(aggfnoid);
|
||||
READ_OID_FIELD(aggtype);
|
||||
READ_NODE_FIELD(target);
|
||||
READ_NODE_FIELD(args);
|
||||
READ_UINT_FIELD(agglevelsup);
|
||||
READ_BOOL_FIELD(aggstar);
|
||||
READ_BOOL_FIELD(aggdistinct);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.19 2006/07/26 19:31:50 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.20 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -217,12 +217,13 @@ find_minmax_aggs_walker(Node *node, List **context)
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
Oid aggsortop;
|
||||
Expr *curTarget;
|
||||
MinMaxAggInfo *info;
|
||||
ListCell *l;
|
||||
|
||||
Assert(aggref->agglevelsup == 0);
|
||||
if (aggref->aggstar)
|
||||
return true; /* foo(*) is surely not optimizable */
|
||||
if (list_length(aggref->args) != 1)
|
||||
return true; /* it couldn't be MIN/MAX */
|
||||
/* note: we do not care if DISTINCT is mentioned ... */
|
||||
|
||||
aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
|
||||
@@ -232,18 +233,19 @@ find_minmax_aggs_walker(Node *node, List **context)
|
||||
/*
|
||||
* Check whether it's already in the list, and add it if not.
|
||||
*/
|
||||
curTarget = linitial(aggref->args);
|
||||
foreach(l, *context)
|
||||
{
|
||||
info = (MinMaxAggInfo *) lfirst(l);
|
||||
if (info->aggfnoid == aggref->aggfnoid &&
|
||||
equal(info->target, aggref->target))
|
||||
equal(info->target, curTarget))
|
||||
return false;
|
||||
}
|
||||
|
||||
info = (MinMaxAggInfo *) palloc0(sizeof(MinMaxAggInfo));
|
||||
info->aggfnoid = aggref->aggfnoid;
|
||||
info->aggsortop = aggsortop;
|
||||
info->target = aggref->target;
|
||||
info->target = curTarget;
|
||||
|
||||
*context = lappend(*context, info);
|
||||
|
||||
@@ -520,13 +522,14 @@ replace_aggs_with_params_mutator(Node *node, List **context)
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
ListCell *l;
|
||||
Expr *curTarget = linitial(aggref->args);
|
||||
|
||||
foreach(l, *context)
|
||||
{
|
||||
MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l);
|
||||
|
||||
if (info->aggfnoid == aggref->aggfnoid &&
|
||||
equal(info->target, aggref->target))
|
||||
equal(info->target, curTarget))
|
||||
return (Node *) info->param;
|
||||
}
|
||||
elog(ERROR, "failed to re-find aggregate info record");
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.214 2006/07/14 14:52:21 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.215 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -397,17 +397,27 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
if (IsA(node, Aggref))
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
Oid inputType;
|
||||
Oid *inputTypes;
|
||||
int numArguments;
|
||||
HeapTuple aggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Oid aggtranstype;
|
||||
int i;
|
||||
ListCell *l;
|
||||
|
||||
Assert(aggref->agglevelsup == 0);
|
||||
counts->numAggs++;
|
||||
if (aggref->aggdistinct)
|
||||
counts->numDistinctAggs++;
|
||||
|
||||
inputType = exprType((Node *) aggref->target);
|
||||
/* extract argument types */
|
||||
numArguments = list_length(aggref->args);
|
||||
inputTypes = (Oid *) palloc(sizeof(Oid) * numArguments);
|
||||
i = 0;
|
||||
foreach(l, aggref->args)
|
||||
{
|
||||
inputTypes[i++] = exprType((Node *) lfirst(l));
|
||||
}
|
||||
|
||||
/* fetch aggregate transition datatype from pg_aggregate */
|
||||
aggTuple = SearchSysCache(AGGFNOID,
|
||||
@@ -423,17 +433,18 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
/* resolve actual type of transition state, if polymorphic */
|
||||
if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
|
||||
{
|
||||
/* have to fetch the agg's declared input type... */
|
||||
Oid *agg_arg_types;
|
||||
/* have to fetch the agg's declared input types... */
|
||||
Oid *declaredArgTypes;
|
||||
int agg_nargs;
|
||||
|
||||
(void) get_func_signature(aggref->aggfnoid,
|
||||
&agg_arg_types, &agg_nargs);
|
||||
Assert(agg_nargs == 1);
|
||||
aggtranstype = resolve_generic_type(aggtranstype,
|
||||
inputType,
|
||||
agg_arg_types[0]);
|
||||
pfree(agg_arg_types);
|
||||
&declaredArgTypes, &agg_nargs);
|
||||
Assert(agg_nargs == numArguments);
|
||||
aggtranstype = enforce_generic_type_consistency(inputTypes,
|
||||
declaredArgTypes,
|
||||
agg_nargs,
|
||||
aggtranstype);
|
||||
pfree(declaredArgTypes);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -448,12 +459,12 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
int32 avgwidth;
|
||||
|
||||
/*
|
||||
* If transition state is of same type as input, assume it's the
|
||||
* same typmod (same width) as well. This works for cases like
|
||||
* MAX/MIN and is probably somewhat reasonable otherwise.
|
||||
* If transition state is of same type as first input, assume it's
|
||||
* the same typmod (same width) as well. This works for cases
|
||||
* like MAX/MIN and is probably somewhat reasonable otherwise.
|
||||
*/
|
||||
if (aggtranstype == inputType)
|
||||
aggtranstypmod = exprTypmod((Node *) aggref->target);
|
||||
if (numArguments > 0 && aggtranstype == inputTypes[0])
|
||||
aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
|
||||
else
|
||||
aggtranstypmod = -1;
|
||||
|
||||
@@ -464,10 +475,10 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
}
|
||||
|
||||
/*
|
||||
* Complain if the aggregate's argument contains any aggregates;
|
||||
* Complain if the aggregate's arguments contain any aggregates;
|
||||
* nested agg functions are semantically nonsensical.
|
||||
*/
|
||||
if (contain_agg_clause((Node *) aggref->target))
|
||||
if (contain_agg_clause((Node *) aggref->args))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("aggregate function calls may not be nested")));
|
||||
@@ -3026,7 +3037,14 @@ expression_tree_walker(Node *node,
|
||||
/* primitive node types with no expression subnodes */
|
||||
break;
|
||||
case T_Aggref:
|
||||
return walker(((Aggref *) node)->target, context);
|
||||
{
|
||||
Aggref *expr = (Aggref *) node;
|
||||
|
||||
if (expression_tree_walker((Node *) expr->args,
|
||||
walker, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_ArrayRef:
|
||||
{
|
||||
ArrayRef *aref = (ArrayRef *) node;
|
||||
@@ -3448,7 +3466,7 @@ expression_tree_mutator(Node *node,
|
||||
Aggref *newnode;
|
||||
|
||||
FLATCOPY(newnode, aggref, Aggref);
|
||||
MUTATE(newnode->target, aggref->target, Expr *);
|
||||
MUTATE(newnode->args, aggref->args, List *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.551 2006/07/03 22:45:39 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.552 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -7346,10 +7346,8 @@ func_expr: func_name '(' ')'
|
||||
| func_name '(' '*' ')'
|
||||
{
|
||||
/*
|
||||
* For now, we transform AGGREGATE(*) into AGGREGATE(1).
|
||||
*
|
||||
* This does the right thing for COUNT(*) (in fact,
|
||||
* any certainly-non-null expression would do for COUNT),
|
||||
* We consider AGGREGATE(*) to invoke a parameterless
|
||||
* aggregate. This does the right thing for COUNT(*),
|
||||
* and there are no other aggregates in SQL92 that accept
|
||||
* '*' as parameter.
|
||||
*
|
||||
@@ -7358,12 +7356,8 @@ func_expr: func_name '(' ')'
|
||||
* really was.
|
||||
*/
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
A_Const *star = makeNode(A_Const);
|
||||
|
||||
star->val.type = T_Integer;
|
||||
star->val.val.ival = 1;
|
||||
n->funcname = $1;
|
||||
n->args = list_make1(star);
|
||||
n->args = NIL;
|
||||
n->agg_star = TRUE;
|
||||
n->agg_distinct = FALSE;
|
||||
n->location = @1;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.72 2006/07/14 14:52:21 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.73 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -55,10 +55,10 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
|
||||
|
||||
/*
|
||||
* The aggregate's level is the same as the level of the lowest-level
|
||||
* variable or aggregate in its argument; or if it contains no variables
|
||||
* variable or aggregate in its arguments; or if it contains no variables
|
||||
* at all, we presume it to be local.
|
||||
*/
|
||||
min_varlevel = find_minimum_var_level((Node *) agg->target);
|
||||
min_varlevel = find_minimum_var_level((Node *) agg->args);
|
||||
|
||||
/*
|
||||
* An aggregate can't directly contain another aggregate call of the same
|
||||
@@ -67,7 +67,7 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
|
||||
*/
|
||||
if (min_varlevel == 0)
|
||||
{
|
||||
if (checkExprHasAggs((Node *) agg->target))
|
||||
if (checkExprHasAggs((Node *) agg->args))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("aggregate function calls may not be nested")));
|
||||
@@ -360,7 +360,7 @@ check_ungrouped_columns_walker(Node *node,
|
||||
* (The trees will never actually be executed, however, so we can skimp
|
||||
* a bit on correctness.)
|
||||
*
|
||||
* agg_input_type, agg_state_type, agg_result_type identify the input,
|
||||
* agg_input_types, agg_state_type, agg_result_type identify the input,
|
||||
* transition, and result types of the aggregate. These should all be
|
||||
* resolved to actual types (ie, none should ever be ANYARRAY or ANYELEMENT).
|
||||
*
|
||||
@@ -371,7 +371,8 @@ check_ungrouped_columns_walker(Node *node,
|
||||
* *finalfnexpr. The latter is set to NULL if there's no finalfn.
|
||||
*/
|
||||
void
|
||||
build_aggregate_fnexprs(Oid agg_input_type,
|
||||
build_aggregate_fnexprs(Oid *agg_input_types,
|
||||
int agg_num_inputs,
|
||||
Oid agg_state_type,
|
||||
Oid agg_result_type,
|
||||
Oid transfn_oid,
|
||||
@@ -379,13 +380,9 @@ build_aggregate_fnexprs(Oid agg_input_type,
|
||||
Expr **transfnexpr,
|
||||
Expr **finalfnexpr)
|
||||
{
|
||||
int transfn_nargs;
|
||||
Param *arg0;
|
||||
Param *arg1;
|
||||
Param *argp;
|
||||
List *args;
|
||||
|
||||
/* get the transition function arg count */
|
||||
transfn_nargs = get_func_nargs(transfn_oid);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Build arg list to use in the transfn FuncExpr node. We really only care
|
||||
@@ -393,22 +390,21 @@ build_aggregate_fnexprs(Oid agg_input_type,
|
||||
* get_fn_expr_argtype(), so it's okay to use Param nodes that don't
|
||||
* correspond to any real Param.
|
||||
*/
|
||||
arg0 = makeNode(Param);
|
||||
arg0->paramkind = PARAM_EXEC;
|
||||
arg0->paramid = -1;
|
||||
arg0->paramtype = agg_state_type;
|
||||
argp = makeNode(Param);
|
||||
argp->paramkind = PARAM_EXEC;
|
||||
argp->paramid = -1;
|
||||
argp->paramtype = agg_state_type;
|
||||
|
||||
if (transfn_nargs == 2)
|
||||
args = list_make1(argp);
|
||||
|
||||
for (i = 0; i < agg_num_inputs; i++)
|
||||
{
|
||||
arg1 = makeNode(Param);
|
||||
arg1->paramkind = PARAM_EXEC;
|
||||
arg1->paramid = -1;
|
||||
arg1->paramtype = agg_input_type;
|
||||
|
||||
args = list_make2(arg0, arg1);
|
||||
argp = makeNode(Param);
|
||||
argp->paramkind = PARAM_EXEC;
|
||||
argp->paramid = -1;
|
||||
argp->paramtype = agg_input_types[i];
|
||||
args = lappend(args, argp);
|
||||
}
|
||||
else
|
||||
args = list_make1(arg0);
|
||||
|
||||
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
|
||||
agg_state_type,
|
||||
@@ -425,11 +421,11 @@ build_aggregate_fnexprs(Oid agg_input_type,
|
||||
/*
|
||||
* Build expr tree for final function
|
||||
*/
|
||||
arg0 = makeNode(Param);
|
||||
arg0->paramkind = PARAM_EXEC;
|
||||
arg0->paramid = -1;
|
||||
arg0->paramtype = agg_state_type;
|
||||
args = list_make1(arg0);
|
||||
argp = makeNode(Param);
|
||||
argp->paramkind = PARAM_EXEC;
|
||||
argp->paramid = -1;
|
||||
argp->paramtype = agg_state_type;
|
||||
args = list_make1(argp);
|
||||
|
||||
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
|
||||
agg_result_type,
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.188 2006/07/14 14:52:22 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.189 2006/07/27 19:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -259,10 +259,21 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
|
||||
aggref->aggfnoid = funcid;
|
||||
aggref->aggtype = rettype;
|
||||
aggref->target = linitial(fargs);
|
||||
aggref->args = fargs;
|
||||
aggref->aggstar = agg_star;
|
||||
aggref->aggdistinct = agg_distinct;
|
||||
|
||||
/*
|
||||
* Reject attempt to call a parameterless aggregate without (*)
|
||||
* syntax. This is mere pedantry but some folks insisted ...
|
||||
*/
|
||||
if (fargs == NIL && !agg_star)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("%s(*) must be used to call a parameterless aggregate function",
|
||||
NameListToString(funcname)),
|
||||
parser_errposition(pstate, location)));
|
||||
|
||||
/* parse_agg.c does additional aggregate-specific processing */
|
||||
transformAggregateCall(pstate, aggref);
|
||||
|
||||
@@ -1194,9 +1205,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
|
||||
*
|
||||
* This is almost like LookupFuncNameTypeNames, but the error messages refer
|
||||
* to aggregates rather than plain functions, and we verify that the found
|
||||
* function really is an aggregate, and we recognize the convention used by
|
||||
* the grammar that agg(*) translates to a NIL list, which we have to treat
|
||||
* as one ANY argument. (XXX this ought to be changed)
|
||||
* function really is an aggregate.
|
||||
*/
|
||||
Oid
|
||||
LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
|
||||
@@ -1204,7 +1213,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
|
||||
Oid argoids[FUNC_MAX_ARGS];
|
||||
int argcount;
|
||||
int i;
|
||||
ListCell *args_item;
|
||||
ListCell *lc;
|
||||
Oid oid;
|
||||
HeapTuple ftup;
|
||||
Form_pg_proc pform;
|
||||
@@ -1216,29 +1225,18 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
|
||||
errmsg("functions cannot have more than %d arguments",
|
||||
FUNC_MAX_ARGS)));
|
||||
|
||||
if (argcount == 0)
|
||||
i = 0;
|
||||
foreach(lc, argtypes)
|
||||
{
|
||||
/* special case for agg(*) */
|
||||
argoids[0] = ANYOID;
|
||||
argcount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
args_item = list_head(argtypes);
|
||||
for (i = 0; i < argcount; i++)
|
||||
{
|
||||
TypeName *t = (TypeName *) lfirst(args_item);
|
||||
TypeName *t = (TypeName *) lfirst(lc);
|
||||
|
||||
argoids[i] = LookupTypeName(NULL, t);
|
||||
|
||||
if (!OidIsValid(argoids[i]))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("type \"%s\" does not exist",
|
||||
TypeNameToString(t))));
|
||||
|
||||
args_item = lnext(args_item);
|
||||
}
|
||||
argoids[i] = LookupTypeName(NULL, t);
|
||||
if (!OidIsValid(argoids[i]))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("type \"%s\" does not exist",
|
||||
TypeNameToString(t))));
|
||||
i++;
|
||||
}
|
||||
|
||||
oid = LookupFuncName(aggname, argcount, argoids, true);
|
||||
@@ -1247,7 +1245,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
|
||||
{
|
||||
if (noError)
|
||||
return InvalidOid;
|
||||
if (argcount == 1 && argoids[0] == ANYOID)
|
||||
if (argcount == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||
errmsg("aggregate %s(*) does not exist",
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* ruleutils.c - Functions to convert stored expressions/querytrees
|
||||
* back to source text
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.228 2006/07/14 14:52:24 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.229 2006/07/27 19:52:06 tgl Exp $
|
||||
**********************************************************************/
|
||||
|
||||
#include "postgres.h"
|
||||
@@ -3880,15 +3880,29 @@ static void
|
||||
get_agg_expr(Aggref *aggref, deparse_context *context)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
Oid argtype = exprType((Node *) aggref->target);
|
||||
Oid argtypes[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
ListCell *l;
|
||||
|
||||
nargs = 0;
|
||||
foreach(l, aggref->args)
|
||||
{
|
||||
if (nargs >= FUNC_MAX_ARGS)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
|
||||
errmsg("too many arguments")));
|
||||
argtypes[nargs] = exprType((Node *) lfirst(l));
|
||||
nargs++;
|
||||
}
|
||||
|
||||
appendStringInfo(buf, "%s(%s",
|
||||
generate_function_name(aggref->aggfnoid, 1, &argtype),
|
||||
generate_function_name(aggref->aggfnoid, nargs, argtypes),
|
||||
aggref->aggdistinct ? "DISTINCT " : "");
|
||||
/* aggstar can be set only in zero-argument aggregates */
|
||||
if (aggref->aggstar)
|
||||
appendStringInfo(buf, "*");
|
||||
appendStringInfoChar(buf, '*');
|
||||
else
|
||||
get_rule_expr((Node *) aggref->target, context, true);
|
||||
get_rule_expr((Node *) aggref->args, context, true);
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user