<refsynopsisdiv>
<synopsis>
-ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>argtype</replaceable> [ , ... ] ) RENAME TO <replaceable>new_name</replaceable>
-ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>argtype</replaceable> [ , ... ] ) OWNER TO <replaceable>new_owner</replaceable>
-ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>argtype</replaceable> [ , ... ] ) SET SCHEMA <replaceable>new_schema</replaceable>
+ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ RENAME TO <replaceable>new_name</replaceable>
+ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ OWNER TO <replaceable>new_owner</replaceable>
+ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ SET SCHEMA <replaceable>new_schema</replaceable>
</synopsis>
</refsynopsisdiv>
</varlistentry>
<varlistentry>
- <term><replaceable class="parameter">argtype</replaceable></term>
+ <term><replaceable class="parameter">argmode</replaceable></term>
+
+ <listitem>
+ <para>
+ The mode of an argument: <literal>IN</> or <literal>VARIADIC</>.
+ If omitted, the default is <literal>IN</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">arg_name</replaceable></term>
+
+ <listitem>
+ <para>
+ The name of an argument.
+ Note that <command>ALTER AGGREGATE</command> does not actually pay
+ any attention to argument names, since only the argument data
+ types are needed to determine the aggregate function's identity.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">arg_data_type</replaceable></term>
<listitem>
<para>
An input data type on which the aggregate function operates.
To reference a zero-argument aggregate function, write <literal>*</>
- in place of the list of input data types.
+ in place of the list of argument specifications.
</para>
</listitem>
</varlistentry>
<phrase>where <replaceable class="PARAMETER">member_object</replaceable> is:</phrase>
- AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
+ AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
<para>
An input data type on which the aggregate function operates.
To reference a zero-argument aggregate function, write <literal>*</>
- in place of the list of input data types.
+ in place of the list of argument specifications.
</para>
</listitem>
</varlistentry>
<synopsis>
COMMENT ON
{
- AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
+ AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">relation_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
<para>
An input data type on which the aggregate function operates.
To reference a zero-argument aggregate function, write <literal>*</>
- in place of the list of input data types.
+ in place of the list of argument specifications.
</para>
</listitem>
</varlistentry>
The mode of a function argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>.
- Note that <command>COMMENT ON FUNCTION</command> does not actually pay
+ Note that <command>COMMENT</command> does not actually pay
any attention to <literal>OUT</> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
<listitem>
<para>
The name of a function argument.
- Note that <command>COMMENT ON FUNCTION</command> does not actually pay
+ Note that <command>COMMENT</command> does not actually pay
any attention to argument names, since only the argument data
types are needed to determine the function's identity.
</para>
<refsynopsisdiv>
<synopsis>
-CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">input_data_type</replaceable> [ , ... ] ) (
+CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
[ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
Note that this behavior is only available when
<replaceable class="PARAMETER">state_data_type</replaceable>
is the same as the first
- <replaceable class="PARAMETER">input_data_type</replaceable>.
+ <replaceable class="PARAMETER">arg_data_type</replaceable>.
When these types are different, you must supply a nonnull initial
condition or use a nonstrict transition function.
</para>
</varlistentry>
<varlistentry>
- <term><replaceable class="PARAMETER">input_data_type</replaceable></term>
+ <term><replaceable class="parameter">argmode</replaceable></term>
+
+ <listitem>
+ <para>
+ The mode of an argument: <literal>IN</> or <literal>VARIADIC</>.
+ (Aggregate functions do not support <literal>OUT</> arguments.)
+ If omitted, the default is <literal>IN</>. Only the last argument
+ can be marked <literal>VARIADIC</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">arg_name</replaceable></term>
+
+ <listitem>
+ <para>
+ The name of an argument. This is currently only useful for
+ documentation purposes. If omitted, the argument has no name.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">arg_data_type</replaceable></term>
<listitem>
<para>
An input data type on which this aggregate function operates.
To create a zero-argument aggregate function, write <literal>*</>
- in place of the list of input data types. (An example of such an
+ in place of the list of argument specifications. (An example of such an
aggregate is <function>count(*)</function>.)
</para>
</listitem>
In the old syntax for <command>CREATE AGGREGATE</>, the input data type
is specified by a <literal>basetype</> parameter rather than being
written next to the aggregate name. Note that this syntax allows
- only one input parameter. To define a zero-argument aggregate function,
- specify the <literal>basetype</> as
+ only one input parameter. To define a zero-argument aggregate function
+ with this syntax, specify the <literal>basetype</> as
<literal>"ANY"</> (not <literal>*</>).
</para>
</listitem>
<refsynopsisdiv>
<synopsis>
-DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">argtype</replaceable> [ , ... ] ) [ CASCADE | RESTRICT ]
+DROP AGGREGATE [ IF EXISTS ]
+ <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] )
+ [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
</varlistentry>
<varlistentry>
- <term><replaceable class="parameter">argtype</replaceable></term>
+ <term><replaceable class="parameter">argmode</replaceable></term>
+
+ <listitem>
+ <para>
+ The mode of an argument: <literal>IN</> or <literal>VARIADIC</>.
+ If omitted, the default is <literal>IN</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">arg_name</replaceable></term>
+
+ <listitem>
+ <para>
+ The name of an argument.
+ Note that <command>DROP AGGREGATE</command> does not actually pay
+ any attention to argument names, since only the argument data
+ types are needed to determine the aggregate function's identity.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">arg_data_type</replaceable></term>
<listitem>
<para>
An input data type on which the aggregate function operates.
To reference a zero-argument aggregate function, write <literal>*</>
- in place of the list of input data types.
+ in place of the list of argument specifications.
</para>
</listitem>
</varlistentry>
{
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
- AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
+ AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
</varlistentry>
<varlistentry>
- <term><replaceable class="parameter">arg_type</replaceable></term>
+ <term><replaceable class="parameter">agg_type</replaceable></term>
<listitem>
<para>
An input data type on which the aggregate function operates.
To reference a zero-argument aggregate function, write <literal>*</>
- in place of the list of input data types.
+ in place of the list of argument specifications.
</para>
</listitem>
</varlistentry>
The mode of a function argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>.
- Note that <command>SECURITY LABEL ON FUNCTION</command> does not actually
+ Note that <command>SECURITY LABEL</command> does not actually
pay any attention to <literal>OUT</> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
<listitem>
<para>
The name of a function argument.
- Note that <command>SECURITY LABEL ON FUNCTION</command> does not actually
+ Note that <command>SECURITY LABEL</command> does not actually
pay any attention to argument names, since only the argument data
types are needed to determine the function's identity.
</para>
having numerous parameters that have default values, named or mixed
notation can save a great deal of writing and reduce chances for error.
</para>
+
+ <note>
+ <para>
+ Named and mixed call notations can currently be used only with regular
+ functions, not with aggregate functions or window functions.
+ </para>
+ </note>
</sect2>
</sect1>
</programlisting>
</para>
+ <para>
+ An aggregate function can be made to accept a varying number of arguments
+ by declaring its last argument as a <literal>VARIADIC</> array, in much
+ the same fashion as for regular functions; see
+ <xref linkend="xfunc-sql-variadic-functions">. The aggregate's transition
+ function must have the same array type as its last argument. The
+ transition function typically would also be marked <literal>VARIADIC</>,
+ but this is not strictly required.
+ </para>
+
+ <note>
+ <para>
+ Variadic aggregates are easily misused in connection with
+ the <literal>ORDER BY</> option (see <xref linkend="syntax-aggregates">),
+ since the parser cannot tell whether the wrong number of actual arguments
+ have been given in such a combination. Keep in mind that everything to
+ the right of <literal>ORDER BY</> is a sort key, not an argument to the
+ aggregate. For example, in
+<programlisting>
+SELECT myaggregate(a ORDER BY a, b, c) FROM ...
+</programlisting>
+ the parser will see this as a single aggregate function argument and
+ three sort keys. However, the user might have intended
+<programlisting>
+SELECT myaggregate(a, b, c ORDER BY a) FROM ...
+</programlisting>
+ If <literal>myaggregate</> is variadic, both these calls could be
+ perfectly valid.
+ </para>
+
+ <para>
+ For the same reason, it's wise to think twice before creating aggregate
+ functions with the same names and different numbers of regular arguments.
+ </para>
+ </note>
+
<para>
A function written in C can detect that it is being called as an
aggregate transition or final function by calling
Oid
AggregateCreate(const char *aggName,
Oid aggNamespace,
- Oid *aggArgTypes,
int numArgs,
+ oidvector *parameterTypes,
+ Datum allParameterTypes,
+ Datum parameterModes,
+ Datum parameterNames,
+ List *parameterDefaults,
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
+ Oid *aggArgTypes = parameterTypes->values;
bool hasPolyArg;
bool hasInternalArg;
Oid rettype;
false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
- buildoidvector(aggArgTypes,
- numArgs), /* paramTypes */
- PointerGetDatum(NULL), /* allParamTypes */
- PointerGetDatum(NULL), /* parameterModes */
- PointerGetDatum(NULL), /* parameterNames */
- NIL, /* parameterDefaults */
+ parameterTypes, /* paramTypes */
+ allParameterTypes, /* allParamTypes */
+ parameterModes, /* parameterModes */
+ parameterNames, /* parameterNames */
+ parameterDefaults, /* parameterDefaults */
PointerGetDatum(NULL), /* proconfig */
1, /* procost */
0); /* prorows */
*
* "oldstyle" signals the old (pre-8.2) style where the aggregate input type
* is specified by a BASETYPE element in the parameters. Otherwise,
- * "args" defines the input type(s).
+ * "args" is a list of FunctionParameter structs defining the agg's arguments.
+ * "parameters" is a list of DefElem representing the agg's definition clauses.
*/
Oid
-DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
+DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
+ const char *queryString)
{
char *aggName;
Oid aggNamespace;
TypeName *baseType = NULL;
TypeName *transType = NULL;
char *initval = NULL;
- Oid *aggArgTypes;
int numArgs;
+ oidvector *parameterTypes;
+ ArrayType *allParameterTypes;
+ ArrayType *parameterModes;
+ ArrayType *parameterNames;
+ List *parameterDefaults;
Oid transTypeId;
char transTypeType;
ListCell *pl;
* Historically we allowed the command to look like basetype = 'ANY'
* so we must do a case-insensitive comparison for the name ANY. Ugh.
*/
+ Oid aggArgTypes[1];
+
if (baseType == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
{
numArgs = 0;
- aggArgTypes = NULL;
+ aggArgTypes[0] = InvalidOid;
}
else
{
numArgs = 1;
- aggArgTypes = (Oid *) palloc(sizeof(Oid));
aggArgTypes[0] = typenameTypeId(NULL, baseType);
}
+ parameterTypes = buildoidvector(aggArgTypes, numArgs);
+ allParameterTypes = NULL;
+ parameterModes = NULL;
+ parameterNames = NULL;
+ parameterDefaults = NIL;
}
else
{
/*
- * New style: args is a list of TypeNames (possibly zero of 'em).
+ * New style: args is a list of FunctionParameters (possibly zero of
+ * 'em). We share functioncmds.c's code for processing them.
*/
- ListCell *lc;
- int i = 0;
+ Oid requiredResultType;
if (baseType != NULL)
ereport(ERROR,
errmsg("basetype is redundant with aggregate input type specification")));
numArgs = list_length(args);
- aggArgTypes = (Oid *) palloc(sizeof(Oid) * numArgs);
- foreach(lc, args)
- {
- TypeName *curTypeName = (TypeName *) lfirst(lc);
-
- aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
- }
+ interpret_function_parameter_list(args,
+ InvalidOid,
+ true, /* is an aggregate */
+ queryString,
+ ¶meterTypes,
+ &allParameterTypes,
+ ¶meterModes,
+ ¶meterNames,
+ ¶meterDefaults,
+ &requiredResultType);
+ /* Parameter defaults are not currently allowed by the grammar */
+ Assert(parameterDefaults == NIL);
+ /* There shouldn't have been any OUT parameters, either */
+ Assert(requiredResultType == InvalidOid);
}
/*
*/
return AggregateCreate(aggName, /* aggregate name */
aggNamespace, /* namespace */
- aggArgTypes, /* input data type(s) */
numArgs,
+ parameterTypes,
+ PointerGetDatum(allParameterTypes),
+ PointerGetDatum(parameterModes),
+ PointerGetDatum(parameterNames),
+ parameterDefaults,
transfuncName, /* step function name */
finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */
typtup = LookupTypeName(NULL, returnType, NULL);
-
if (typtup)
{
if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
}
/*
- * Interpret the parameter list of the CREATE FUNCTION statement.
+ * Interpret the function parameter list of a CREATE FUNCTION or
+ * CREATE AGGREGATE statement.
+ *
+ * Input parameters:
+ * parameters: list of FunctionParameter structs
+ * languageOid: OID of function language (InvalidOid if it's CREATE AGGREGATE)
+ * is_aggregate: needed only to determine error handling
+ * queryString: likewise, needed only for error handling
*
* Results are stored into output parameters. parameterTypes must always
* be created, but the other arrays are set to NULL if not needed.
* requiredResultType is set to InvalidOid if there are no OUT parameters,
* else it is set to the OID of the implied result type.
*/
-static void
-examine_parameter_list(List *parameters, Oid languageOid,
- const char *queryString,
- oidvector **parameterTypes,
- ArrayType **allParameterTypes,
- ArrayType **parameterModes,
- ArrayType **parameterNames,
- List **parameterDefaults,
- Oid *requiredResultType)
+void
+interpret_function_parameter_list(List *parameters,
+ Oid languageOid,
+ bool is_aggregate,
+ const char *queryString,
+ oidvector **parameterTypes,
+ ArrayType **allParameterTypes,
+ ArrayType **parameterModes,
+ ArrayType **parameterNames,
+ List **parameterDefaults,
+ Oid *requiredResultType)
{
int parameterCount = list_length(parameters);
Oid *inTypes;
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("SQL function cannot accept shell type %s",
TypeNameToString(t))));
+ /* We don't allow creating aggregates on shell types either */
+ else if (is_aggregate)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("aggregate cannot accept shell type %s",
+ TypeNameToString(t))));
else
ereport(NOTICE,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
aclcheck_error_type(aclresult, toid);
if (t->setof)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("functions cannot accept set arguments")));
+ {
+ if (is_aggregate)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("aggregates cannot accept set arguments")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("functions cannot accept set arguments")));
+ }
/* handle input parameters */
if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
* Convert remaining parameters of CREATE to form wanted by
* ProcedureCreate.
*/
- examine_parameter_list(stmt->parameters, languageOid, queryString,
- ¶meterTypes,
- &allParameterTypes,
- ¶meterModes,
- ¶meterNames,
- ¶meterDefaults,
- &requiredResultType);
+ interpret_function_parameter_list(stmt->parameters,
+ languageOid,
+ false, /* not an aggregate */
+ queryString,
+ ¶meterTypes,
+ &allParameterTypes,
+ ¶meterModes,
+ ¶meterNames,
+ ¶meterDefaults,
+ &requiredResultType);
if (stmt->returnType)
{
/* build expression trees using actual argument & result types */
build_aggregate_fnexprs(inputTypes,
numArguments,
+ aggref->aggvariadic,
aggtranstype,
aggref->aggtype,
aggref->inputcollid,
/* build expression trees using actual argument & result types */
build_aggregate_fnexprs(inputTypes,
numArguments,
+ false, /* no variadic window functions yet */
aggtranstype,
wfunc->wintype,
wfunc->inputcollid,
COPY_NODE_FIELD(aggdistinct);
COPY_NODE_FIELD(aggfilter);
COPY_SCALAR_FIELD(aggstar);
+ COPY_SCALAR_FIELD(aggvariadic);
COPY_SCALAR_FIELD(agglevelsup);
COPY_LOCATION_FIELD(location);
COMPARE_NODE_FIELD(aggdistinct);
COMPARE_NODE_FIELD(aggfilter);
COMPARE_SCALAR_FIELD(aggstar);
+ COMPARE_SCALAR_FIELD(aggvariadic);
COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_LOCATION_FIELD(location);
WRITE_NODE_FIELD(aggdistinct);
WRITE_NODE_FIELD(aggfilter);
WRITE_BOOL_FIELD(aggstar);
+ WRITE_BOOL_FIELD(aggvariadic);
WRITE_UINT_FIELD(agglevelsup);
WRITE_LOCATION_FIELD(location);
}
READ_NODE_FIELD(aggdistinct);
READ_NODE_FIELD(aggfilter);
READ_BOOL_FIELD(aggstar);
+ READ_BOOL_FIELD(aggvariadic);
READ_UINT_FIELD(agglevelsup);
READ_LOCATION_FIELD(location);
reloptions opt_reloptions
OptWith opt_distinct opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
+ aggr_args aggr_args_list
func_as createfunc_opt_list alterfunc_opt_list
- aggr_args old_aggr_definition old_aggr_list
+ old_aggr_definition old_aggr_list
oper_argtypes RuleActionList RuleActionMulti
opt_column_list columnList opt_name_list
sort_clause opt_sort_clause sortby_list index_params
%type <into> into_clause create_as_target create_mv_target
%type <defelt> createfunc_opt_item common_func_opt_item dostmt_opt_item
-%type <fun_param> func_arg func_arg_with_default table_func_column
+%type <fun_param> func_arg func_arg_with_default table_func_column aggr_arg
%type <fun_param_mode> arg_class
%type <typnam> func_return func_type
n->action = $4;
n->objtype = OBJECT_AGGREGATE;
n->objname = $6;
- n->objargs = $7;
+ n->objargs = extractArgTypes($7);
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')'
| Sconst { $$ = (Node *)makeString($1); }
;
-aggr_args: '(' type_list ')' { $$ = $2; }
- | '(' '*' ')' { $$ = NIL; }
- ;
-
old_aggr_definition: '(' old_aggr_list ')' { $$ = $2; }
;
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_AGGREGATE;
n->objname = $4;
- n->objargs = $5;
+ n->objargs = extractArgTypes($5);
n->comment = $7;
$$ = (Node *) n;
}
n->provider = $3;
n->objtype = OBJECT_AGGREGATE;
n->objname = $6;
- n->objargs = $7;
+ n->objargs = extractArgTypes($7);
n->label = $9;
$$ = (Node *) n;
}
}
;
+/* Aggregate args can be most things that function args can be */
+aggr_arg: func_arg
+ {
+ if (!($1->mode == FUNC_PARAM_IN ||
+ $1->mode == FUNC_PARAM_VARIADIC))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("aggregates cannot have output arguments"),
+ parser_errposition(@1)));
+ $$ = $1;
+ }
+ ;
+
+/* Zero-argument aggregates are named with * for consistency with COUNT(*) */
+aggr_args: '(' aggr_args_list ')' { $$ = $2; }
+ | '(' '*' ')' { $$ = NIL; }
+ ;
+
+aggr_args_list:
+ aggr_arg { $$ = list_make1($1); }
+ | aggr_args_list ',' aggr_arg { $$ = lappend($1, $3); }
+ ;
createfunc_opt_list:
/* Must be at least one to prevent conflict */
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_AGGREGATE;
n->objects = list_make1($3);
- n->arguments = list_make1($4);
+ n->arguments = list_make1(extractArgTypes($4));
n->behavior = $5;
n->missing_ok = false;
n->concurrent = false;
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_AGGREGATE;
n->objects = list_make1($5);
- n->arguments = list_make1($6);
+ n->arguments = list_make1(extractArgTypes($6));
n->behavior = $7;
n->missing_ok = true;
n->concurrent = false;
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_AGGREGATE;
n->object = $3;
- n->objarg = $4;
+ n->objarg = extractArgTypes($4);
n->newname = $7;
n->missing_ok = false;
$$ = (Node *)n;
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_AGGREGATE;
n->object = $3;
- n->objarg = $4;
+ n->objarg = extractArgTypes($4);
n->newschema = $7;
n->missing_ok = false;
$$ = (Node *)n;
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_AGGREGATE;
n->object = $3;
- n->objarg = $4;
+ n->objarg = extractArgTypes($4);
n->newowner = $7;
$$ = (Node *)n;
}
void
build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
+ bool agg_variadic,
Oid agg_state_type,
Oid agg_result_type,
Oid agg_input_collation,
{
Param *argp;
List *args;
+ FuncExpr *fexpr;
int i;
/*
args = lappend(args, argp);
}
- *transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
- agg_state_type,
- args,
- InvalidOid,
- agg_input_collation,
- COERCE_EXPLICIT_CALL);
+ fexpr = makeFuncExpr(transfn_oid,
+ agg_state_type,
+ args,
+ InvalidOid,
+ agg_input_collation,
+ COERCE_EXPLICIT_CALL);
+ fexpr->funcvariadic = agg_variadic;
+ *transfnexpr = (Expr *) fexpr;
/* see if we have a final function */
if (!OidIsValid(finalfn_oid))
}
/*
- * When function is called an explicit VARIADIC labeled parameter,
+ * When function is called with an explicit VARIADIC labeled parameter,
* and the declared_arg_type is "any", then sanity check the actual
* parameter type now - it must be an array.
*/
aggref->aggtype = rettype;
/* aggcollid and inputcollid will be set by parse_collate.c */
/* args, aggorder, aggdistinct will be set by transformAggregateCall */
- aggref->aggstar = agg_star;
aggref->aggfilter = agg_filter;
+ aggref->aggstar = agg_star;
+ aggref->aggvariadic = func_variadic;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
parser_errposition(pstate, location)));
/*
- * Currently it's not possible to define an aggregate with named
- * arguments, so this case should be impossible. Check anyway because
- * the planner and executor wouldn't cope with NamedArgExprs in an
- * Aggref node.
+ * We might want to support named arguments later, but disallow it for
+ * now. We'd need to figure out the parsed representation (should the
+ * NamedArgExprs go above or below the TargetEntry nodes?) and then
+ * teach the planner to reorder the list properly. Or maybe we could
+ * make transformAggregateCall do that? However, if you'd also like
+ * to allow default arguments for aggregates, we'd need to do it in
+ * planning to avoid semantic problems.
*/
if (argnames != NIL)
ereport(ERROR,
{
case OBJECT_AGGREGATE:
DefineAggregate(stmt->defnames, stmt->args,
- stmt->oldstyle, stmt->definition);
+ stmt->oldstyle, stmt->definition,
+ queryString);
break;
case OBJECT_OPERATOR:
Assert(stmt->args == NIL);
Oid argtypes[FUNC_MAX_ARGS];
List *arglist;
int nargs;
+ bool use_variadic;
ListCell *l;
/* Extract the regular arguments, ignoring resjunk stuff for the moment */
appendStringInfo(buf, "%s(%s",
generate_function_name(aggref->aggfnoid, nargs,
NIL, argtypes,
- false, NULL),
+ aggref->aggvariadic,
+ &use_variadic),
(aggref->aggdistinct != NIL) ? "DISTINCT " : "");
+
/* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar)
appendStringInfoChar(buf, '*');
else
- get_rule_expr((Node *) arglist, context, true);
+ {
+ nargs = 0;
+ foreach(l, arglist)
+ {
+ if (nargs++ > 0)
+ appendStringInfoString(buf, ", ");
+ if (use_variadic && lnext(l) == NULL)
+ appendStringInfoString(buf, "VARIADIC ");
+ get_rule_expr((Node *) lfirst(l), context, true);
+ }
+ }
+
if (aggref->aggorder != NIL)
{
appendStringInfoString(buf, " ORDER BY ");
* types. (Those matter because of ambiguous-function resolution rules.)
*
* If we're dealing with a potentially variadic function (in practice, this
- * means a FuncExpr and not some other way of calling the function), then
+ * means a FuncExpr or Aggref, not some other way of calling a function), then
* was_variadic must specify whether VARIADIC appeared in the original call,
* and *use_variadic_p will be set to indicate whether to print VARIADIC in
* the output. For non-FuncExpr cases, was_variadic should be FALSE and
static void makeTableDataInfo(TableInfo *tbinfo, bool oids);
static void buildMatViewRefreshDependencies(Archive *fout);
static void getTableDataFKConstraints(void);
-static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
+static char *format_function_arguments(FuncInfo *finfo, char *funcargs,
+ bool is_agg);
static char *format_function_arguments_old(Archive *fout,
FuncInfo *finfo, int nallargs,
char **allargtypes,
* format_function_arguments: generate function name and argument list
*
* This is used when we can rely on pg_get_function_arguments to format
- * the argument list.
+ * the argument list. Note, however, that pg_get_function_arguments
+ * does not special-case zero-argument aggregates.
*/
static char *
-format_function_arguments(FuncInfo *finfo, char *funcargs)
+format_function_arguments(FuncInfo *finfo, char *funcargs, bool is_agg)
{
PQExpBufferData fn;
initPQExpBuffer(&fn);
- appendPQExpBuffer(&fn, "%s(%s)", fmtId(finfo->dobj.name), funcargs);
+ appendPQExpBuffer(&fn, "%s", fmtId(finfo->dobj.name));
+ if (is_agg && finfo->nargs == 0)
+ appendPQExpBuffer(&fn, "(*)");
+ else
+ appendPQExpBuffer(&fn, "(%s)", funcargs);
return fn.data;
}
if (funcargs)
{
/* 8.4 or later; we rely on server-side code for most of the work */
- funcfullsig = format_function_arguments(finfo, funcargs);
- funcsig = format_function_arguments(finfo, funciargs);
+ funcfullsig = format_function_arguments(finfo, funcargs, false);
+ funcsig = format_function_arguments(finfo, funciargs, false);
}
else
{
PQExpBuffer delq;
PQExpBuffer labelq;
PQExpBuffer details;
- char *aggsig;
+ char *aggsig; /* identity signature */
+ char *aggfullsig; /* full signature */
char *aggsig_tag;
PGresult *res;
int i_aggtransfn;
selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
/* Get aggregate-specific details */
- if (fout->remoteVersion >= 80100)
+ if (fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT aggtransfn, "
"aggfinalfn, aggtranstype::pg_catalog.regtype, "
"aggsortop::pg_catalog.regoperator, "
"agginitval, "
- "'t'::boolean AS convertok "
+ "'t'::boolean AS convertok, "
+ "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
+ "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
"FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"WHERE a.aggfnoid = p.oid "
"AND p.oid = '%u'::pg_catalog.oid",
agginfo->aggfn.dobj.catId.oid);
}
+ else if (fout->remoteVersion >= 80100)
+ {
+ appendPQExpBuffer(query, "SELECT aggtransfn, "
+ "aggfinalfn, aggtranstype::pg_catalog.regtype, "
+ "aggsortop::pg_catalog.regoperator, "
+ "agginitval, "
+ "'t'::boolean AS convertok "
+ "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+ "WHERE a.aggfnoid = p.oid "
+ "AND p.oid = '%u'::pg_catalog.oid",
+ agginfo->aggfn.dobj.catId.oid);
+ }
else if (fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT aggtransfn, "
agginitval = PQgetvalue(res, 0, i_agginitval);
convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
- aggsig = format_aggregate_signature(agginfo, fout, true);
+ if (fout->remoteVersion >= 80400)
+ {
+ /* 8.4 or later; we rely on server-side code for most of the work */
+ char *funcargs;
+ char *funciargs;
+
+ funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
+ funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
+ aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
+ aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
+ }
+ else
+ {
+ /* pre-8.4, do it ourselves */
+ aggsig = format_aggregate_signature(agginfo, fout, true);
+ aggfullsig = aggsig;
+ }
+
aggsig_tag = format_aggregate_signature(agginfo, fout, false);
if (!convertok)
aggsig);
appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
- aggsig, details->data);
+ aggfullsig, details->data);
appendPQExpBuffer(labelq, "AGGREGATE %s", aggsig);
gettext_noop("Name"),
gettext_noop("Result data type"));
- if (pset.sversion >= 80200)
+ if (pset.sversion >= 80400)
+ appendPQExpBuffer(&buf,
+ " CASE WHEN p.pronargs = 0\n"
+ " THEN CAST('*' AS pg_catalog.text)\n"
+ " ELSE pg_catalog.pg_get_function_arguments(p.oid)\n"
+ " END AS \"%s\",\n",
+ gettext_noop("Argument data types"));
+ else if (pset.sversion >= 80200)
appendPQExpBuffer(&buf,
" CASE WHEN p.pronargs = 0\n"
" THEN CAST('*' AS pg_catalog.text)\n"
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201307221
+#define CATALOG_VERSION_NO 201309031
#endif
*/
extern Oid AggregateCreate(const char *aggName,
Oid aggNamespace,
- Oid *aggArgTypes,
int numArgs,
+ oidvector *parameterTypes,
+ Datum allParameterTypes,
+ Datum parameterModes,
+ Datum parameterNames,
+ List *parameterDefaults,
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
#define DEFREM_H
#include "nodes/parsenodes.h"
+#include "utils/array.h"
/* commands/dropcmds.c */
extern void RemoveObjects(DropStmt *stmt);
oidvector *proargtypes, Oid nspOid);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
+extern void interpret_function_parameter_list(List *parameters,
+ Oid languageOid,
+ bool is_aggregate,
+ const char *queryString,
+ oidvector **parameterTypes,
+ ArrayType **allParameterTypes,
+ ArrayType **parameterModes,
+ ArrayType **parameterNames,
+ List **parameterDefaults,
+ Oid *requiredResultType);
/* commands/operatorcmds.c */
extern Oid DefineOperator(List *names, List *parameters);
/* commands/aggregatecmds.c */
extern Oid DefineAggregate(List *name, List *args, bool oldstyle,
- List *parameters);
+ List *parameters, const char *queryString);
/* commands/opclasscmds.c */
extern Oid DefineOpClass(CreateOpClassStmt *stmt);
List *aggdistinct; /* DISTINCT (list of SortGroupClause) */
Expr *aggfilter; /* FILTER expression */
bool aggstar; /* TRUE if argument list was really '*' */
+ bool aggvariadic; /* TRUE if VARIADIC was used in call */
Index agglevelsup; /* > 0 if agg belongs to outer query */
int location; /* token location, or -1 if unknown */
} Aggref;
extern void build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
+ bool agg_variadic,
Oid agg_state_type,
Oid agg_result_type,
Oid agg_input_collation,
{"(2,2,bar)","(3,1,baz)"}
(1 row)
+-- variadic aggregates
+select least_agg(q1,q2) from int8_tbl;
+ least_agg
+-------------------
+ -4567890123456789
+(1 row)
+
+select least_agg(variadic array[q1,q2]) from int8_tbl;
+ least_agg
+-------------------
+ -4567890123456789
+(1 row)
+
sfunc = aggfns_trans, stype = aggtype[],
initcond = '{}'
);
+-- variadic aggregate
+create function least_accum(anyelement, variadic anyarray)
+returns anyelement language sql as
+ 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
+create aggregate least_agg(variadic items anyarray) (
+ stype = anyelement, sfunc = least_accum
+);
-- to avoid this because it opens the door for confusion in connection with
-- ORDER BY: novices frequently put the ORDER BY in the wrong place.
-- See the fate of the single-argument form of string_agg() for history.
+-- (Note: we don't forbid users from creating such aggregates; the policy is
+-- just to think twice before creating built-in aggregates like this.)
-- The only aggregates that should show up here are count(x) and count(*).
SELECT p1.oid::regprocedure, p2.oid::regprocedure
FROM pg_proc AS p1, pg_proc AS p2
count("any") | count()
(1 row)
--- For the same reason, aggregates with default arguments are no good.
+-- For the same reason, we avoid creating built-in variadic aggregates.
+SELECT oid, proname
+FROM pg_proc AS p
+WHERE proisagg AND provariadic != 0;
+ oid | proname
+-----+---------
+(0 rows)
+
+-- For the same reason, built-in aggregates with default arguments are no good.
SELECT oid, proname
FROM pg_proc AS p
WHERE proisagg AND proargdefaults IS NOT NULL;
select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c),
generate_series(1,2) i;
+
+-- variadic aggregates
+select least_agg(q1,q2) from int8_tbl;
+select least_agg(variadic array[q1,q2]) from int8_tbl;
sfunc = aggfns_trans, stype = aggtype[],
initcond = '{}'
);
+
+-- variadic aggregate
+create function least_accum(anyelement, variadic anyarray)
+returns anyelement language sql as
+ 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
+
+create aggregate least_agg(variadic items anyarray) (
+ stype = anyelement, sfunc = least_accum
+);
-- to avoid this because it opens the door for confusion in connection with
-- ORDER BY: novices frequently put the ORDER BY in the wrong place.
-- See the fate of the single-argument form of string_agg() for history.
+-- (Note: we don't forbid users from creating such aggregates; the policy is
+-- just to think twice before creating built-in aggregates like this.)
-- The only aggregates that should show up here are count(x) and count(*).
SELECT p1.oid::regprocedure, p2.oid::regprocedure
array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
ORDER BY 1;
--- For the same reason, aggregates with default arguments are no good.
+-- For the same reason, we avoid creating built-in variadic aggregates.
+
+SELECT oid, proname
+FROM pg_proc AS p
+WHERE proisagg AND provariadic != 0;
+
+-- For the same reason, built-in aggregates with default arguments are no good.
SELECT oid, proname
FROM pg_proc AS p