Commit 108fe473 authored by Tom Lane's avatar Tom Lane

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.
parent c2d11383
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_aggregate.sgml,v 1.7 2005/10/13 22:44:51 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/alter_aggregate.sgml,v 1.8 2006/07/27 19:52:04 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -20,9 +20,9 @@ PostgreSQL documentation ...@@ -20,9 +20,9 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable> ) RENAME TO <replaceable>new_name</replaceable> ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable> [ , ... ] ) RENAME TO <replaceable>new_name</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable> ) OWNER TO <replaceable>new_owner</replaceable> ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable> [ , ... ] ) OWNER TO <replaceable>new_owner</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable> ) SET SCHEMA <replaceable>new_schema</replaceable> ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable> [ , ... ] ) SET SCHEMA <replaceable>new_schema</replaceable>
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -64,8 +64,9 @@ ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable ...@@ -64,8 +64,9 @@ ALTER AGGREGATE <replaceable>name</replaceable> ( <replaceable>type</replaceable
<term><replaceable class="parameter">type</replaceable></term> <term><replaceable class="parameter">type</replaceable></term>
<listitem> <listitem>
<para> <para>
The argument data type of the aggregate function, or An input data type on which the aggregate function operates.
<literal>*</literal> if the function accepts any data type. To reference a zero-argument aggregate function, write <literal>*</>
in place of the list of input data types.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.30 2006/02/12 03:22:17 momjian Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.31 2006/07/27 19:52:04 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -24,7 +24,7 @@ COMMENT ON ...@@ -24,7 +24,7 @@ COMMENT ON
{ {
TABLE <replaceable class="PARAMETER">object_name</replaceable> | TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_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">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>) | CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>) |
CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> | CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> | CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
...@@ -101,8 +101,9 @@ COMMENT ON ...@@ -101,8 +101,9 @@ COMMENT ON
<term><replaceable class="parameter">agg_type</replaceable></term> <term><replaceable class="parameter">agg_type</replaceable></term>
<listitem> <listitem>
<para> <para>
The argument data type of the aggregate function, or An input data type on which the aggregate function operates.
<literal>*</literal> if the function accepts any data type. To reference a zero-argument aggregate function, write <literal>*</>
in place of the list of input data types.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_aggregate.sgml,v 1.34 2006/04/15 17:45:18 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_aggregate.sgml,v 1.35 2006/07/27 19:52:04 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -20,7 +20,7 @@ PostgreSQL documentation ...@@ -20,7 +20,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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">input_data_type</replaceable> [ , ... ] ) (
SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>, SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
STYPE = <replaceable class="PARAMETER">state_data_type</replaceable> STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
[ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ] [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
...@@ -60,16 +60,16 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( ...@@ -60,16 +60,16 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
</para> </para>
<para> <para>
An aggregate function is identified by its name and input data type. An aggregate function is identified by its name and input data type(s).
Two aggregates in the same schema can have the same name if they operate on Two aggregates in the same schema can have the same name if they operate on
different input types. The different input types. The
name and input data type of an aggregate must also be distinct from name and input data type(s) of an aggregate must also be distinct from
the name and input data type(s) of every ordinary function in the same the name and input data type(s) of every ordinary function in the same
schema. schema.
</para> </para>
<para> <para>
An aggregate function is made from one or two ordinary An aggregate function is made from one or two ordinary
functions: functions:
a state transition function a state transition function
<replaceable class="PARAMETER">sfunc</replaceable>, <replaceable class="PARAMETER">sfunc</replaceable>,
...@@ -77,7 +77,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( ...@@ -77,7 +77,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
<replaceable class="PARAMETER">ffunc</replaceable>. <replaceable class="PARAMETER">ffunc</replaceable>.
These are used as follows: These are used as follows:
<programlisting> <programlisting>
<replaceable class="PARAMETER">sfunc</replaceable>( internal-state, next-data-item ) ---> next-internal-state <replaceable class="PARAMETER">sfunc</replaceable>( internal-state, next-data-values ) ---> next-internal-state
<replaceable class="PARAMETER">ffunc</replaceable>( internal-state ) ---> aggregate-value <replaceable class="PARAMETER">ffunc</replaceable>( internal-state ) ---> aggregate-value
</programlisting> </programlisting>
</para> </para>
...@@ -85,10 +85,11 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( ...@@ -85,10 +85,11 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
<para> <para>
<productname>PostgreSQL</productname> creates a temporary variable <productname>PostgreSQL</productname> creates a temporary variable
of data type <replaceable class="PARAMETER">stype</replaceable> of data type <replaceable class="PARAMETER">stype</replaceable>
to hold the current internal state of the aggregate. At each input to hold the current internal state of the aggregate. At each input row,
data item, the aggregate argument value(s) are calculated and
the state transition function is invoked to calculate a new the state transition function is invoked with the current state value
internal state value. After all the data has been processed, and the new argument value(s) to calculate a new
internal state value. After all the rows have been processed,
the final function is invoked once to calculate the aggregate's return the final function is invoked once to calculate the aggregate's return
value. If there is no final function then the ending state value value. If there is no final function then the ending state value
is returned as-is. is returned as-is.
...@@ -106,15 +107,16 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( ...@@ -106,15 +107,16 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
<para> <para>
If the state transition function is declared <quote>strict</quote>, If the state transition function is declared <quote>strict</quote>,
then it cannot be called with null inputs. With such a transition then it cannot be called with null inputs. With such a transition
function, aggregate execution behaves as follows. Null input values function, aggregate execution behaves as follows. Rows with any null input
are ignored (the function is not called and the previous state value values are ignored (the function is not called and the previous state value
is retained). If the initial state value is null, then the first is retained). If the initial state value is null, then at the first row
nonnull input value replaces the state value, and the transition with all-nonnull input values, the first argument value replaces the state
function is invoked beginning with the second nonnull input value. value, and the transition function is invoked at subsequent rows with
all-nonnull input values.
This is handy for implementing aggregates like <function>max</function>. This is handy for implementing aggregates like <function>max</function>.
Note that this behavior is only available when Note that this behavior is only available when
<replaceable class="PARAMETER">state_data_type</replaceable> <replaceable class="PARAMETER">state_data_type</replaceable>
is the same as is the same as the first
<replaceable class="PARAMETER">input_data_type</replaceable>. <replaceable class="PARAMETER">input_data_type</replaceable>.
When these types are different, you must supply a nonnull initial When these types are different, you must supply a nonnull initial
condition or use a nonstrict transition function. condition or use a nonstrict transition function.
...@@ -122,7 +124,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( ...@@ -122,7 +124,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
<para> <para>
If the state transition function is not strict, then it will be called If the state transition function is not strict, then it will be called
unconditionally at each input value, and must deal with null inputs unconditionally at each input row, and must deal with null inputs
and null transition values for itself. This allows the aggregate and null transition values for itself. This allows the aggregate
author to have full control over the aggregate's handling of null values. author to have full control over the aggregate's handling of null values.
</para> </para>
...@@ -180,10 +182,10 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; ...@@ -180,10 +182,10 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
<term><replaceable class="PARAMETER">input_data_type</replaceable></term> <term><replaceable class="PARAMETER">input_data_type</replaceable></term>
<listitem> <listitem>
<para> <para>
The input data type on which this aggregate function operates. An input data type on which this aggregate function operates.
This can be specified as <literal>*</> for an aggregate that To create a zero-argument aggregate function, write <literal>*</>
does not examine its input values (an example is in place of the list of input data types. (An example of such an
<function>count(*)</function>). aggregate is <function>count(*)</function>.)
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -195,8 +197,8 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; ...@@ -195,8 +197,8 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
In the old syntax for <command>CREATE AGGREGATE</>, the input data type In the old syntax for <command>CREATE AGGREGATE</>, the input data type
is specified by a <literal>basetype</> parameter rather than being is specified by a <literal>basetype</> parameter rather than being
written next to the aggregate name. Note that this syntax allows written next to the aggregate name. Note that this syntax allows
only one input parameter. To define an aggregate that does not examine only one input parameter. To define a zero-argument aggregate function,
its input values, specify the <literal>basetype</> as specify the <literal>basetype</> as
<literal>"ANY"</> (not <literal>*</>). <literal>"ANY"</> (not <literal>*</>).
</para> </para>
</listitem> </listitem>
...@@ -207,17 +209,15 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; ...@@ -207,17 +209,15 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
<listitem> <listitem>
<para> <para>
The name of the state transition function to be called for each The name of the state transition function to be called for each
input data value. This is normally a function of two arguments, input row. For an <replaceable class="PARAMETER">N</>-argument
aggregate function, the <replaceable class="PARAMETER">sfunc</>
must take <replaceable class="PARAMETER">N</>+1 arguments,
the first being of type <replaceable the first being of type <replaceable
class="PARAMETER">state_data_type</replaceable> and the second class="PARAMETER">state_data_type</replaceable> and the rest
of type <replaceable matching the declared input data type(s) of the aggregate.
class="PARAMETER">input_data_type</replaceable>. Alternatively, The function must return a value of type <replaceable
for an aggregate that does not examine its input values, the
function takes just one argument of type <replaceable
class="PARAMETER">state_data_type</replaceable>. In either case
the function must return a value of type <replaceable
class="PARAMETER">state_data_type</replaceable>. This function class="PARAMETER">state_data_type</replaceable>. This function
takes the current state value and the current input data item, takes the current state value and the current input data value(s),
and returns the next state value. and returns the next state value.
</para> </para>
</listitem> </listitem>
...@@ -237,7 +237,7 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; ...@@ -237,7 +237,7 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
<listitem> <listitem>
<para> <para>
The name of the final function called to compute the aggregate's The name of the final function called to compute the aggregate's
result after all input data has been traversed. The function result after all input rows have been traversed. The function
must take a single argument of type <replaceable must take a single argument of type <replaceable
class="PARAMETER">state_data_type</replaceable>. The return class="PARAMETER">state_data_type</replaceable>. The return
data type of the aggregate is defined as the return type of this data type of the aggregate is defined as the return type of this
...@@ -269,7 +269,7 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; ...@@ -269,7 +269,7 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
<function>MAX</>-like aggregate. <function>MAX</>-like aggregate.
This is just an operator name (possibly schema-qualified). This is just an operator name (possibly schema-qualified).
The operator is assumed to have the same input data types as The operator is assumed to have the same input data types as
the aggregate. the aggregate (which must be a single-argument aggregate).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.28 2006/06/16 22:27:55 adunstan Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.29 2006/07/27 19:52:04 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -20,7 +20,7 @@ PostgreSQL documentation ...@@ -20,7 +20,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">type</replaceable> ) [ CASCADE | RESTRICT ] DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">type</replaceable> [ , ... ] ) [ CASCADE | RESTRICT ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -43,7 +43,7 @@ DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> ( ...@@ -43,7 +43,7 @@ DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> (
<term><literal>IF EXISTS</literal></term> <term><literal>IF EXISTS</literal></term>
<listitem> <listitem>
<para> <para>
Do not throw an error if the aggregate does not exist. A notice is issued Do not throw an error if the aggregate does not exist. A notice is issued
in this case. in this case.
</para> </para>
</listitem> </listitem>
...@@ -62,8 +62,9 @@ DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> ( ...@@ -62,8 +62,9 @@ DROP AGGREGATE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> (
<term><replaceable class="parameter">type</replaceable></term> <term><replaceable class="parameter">type</replaceable></term>
<listitem> <listitem>
<para> <para>
The argument data type of the aggregate function, or An input data type on which the aggregate function operates.
<literal>*</literal> if the function accepts any data type. To reference a zero-argument aggregate function, write <literal>*</>
in place of the list of input data types.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.165 2006/05/31 22:34:35 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.166 2006/07/27 19:52:04 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -854,7 +854,7 @@ testdb=&gt; ...@@ -854,7 +854,7 @@ testdb=&gt;
<listitem> <listitem>
<para> <para>
Lists all available aggregate functions, together with the data Lists all available aggregate functions, together with the data
type they operate on. If <replaceable types they operate on. If <replaceable
class="parameter">pattern</replaceable> class="parameter">pattern</replaceable>
is specified, only aggregates whose names match the pattern are shown. is specified, only aggregates whose names match the pattern are shown.
</para> </para>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.40 2006/04/30 18:30:38 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.41 2006/07/27 19:52:04 tgl Exp $ -->
<chapter id="sql-intro"> <chapter id="sql-intro">
<title>SQL</title> <title>SQL</title>
...@@ -1247,13 +1247,13 @@ select sname, pname from supplier ...@@ -1247,13 +1247,13 @@ select sname, pname from supplier
</sect3> </sect3>
<sect3> <sect3>
<title id="aggregates-tutorial">Aggregate Operators</title> <title id="aggregates-tutorial">Aggregate Functions</title>
<para> <para>
<acronym>SQL</acronym> provides aggregate operators (e.g. AVG, <acronym>SQL</acronym> provides aggregate functions such as AVG,
COUNT, SUM, MIN, MAX) that take an expression as argument. The COUNT, SUM, MIN, and MAX. The argument(s) of an aggregate function
expression is evaluated at each row that satisfies the WHERE are evaluated at each row that satisfies the WHERE
clause, and the aggregate operator is calculated over this set clause, and the aggregate function is calculated over this set
of input values. Normally, an aggregate delivers a single of input values. Normally, an aggregate delivers a single
result for a whole <command>SELECT</command> statement. But if result for a whole <command>SELECT</command> statement. But if
grouping is specified in the query, then a separate calculation grouping is specified in the query, then a separate calculation
...@@ -1311,10 +1311,10 @@ SELECT COUNT(PNO) ...@@ -1311,10 +1311,10 @@ SELECT COUNT(PNO)
<para> <para>
<acronym>SQL</acronym> allows one to partition the tuples of a table <acronym>SQL</acronym> allows one to partition the tuples of a table
into groups. Then the into groups. Then the
aggregate operators described above can be applied to the groups &mdash; aggregate functions described above can be applied to the groups &mdash;
i.e. the value of the aggregate operator is no longer calculated over i.e. the value of the aggregate function is no longer calculated over
all the values of the specified column but over all values of a all the values of the specified column but over all values of a
group. Thus the aggregate operator is evaluated separately for every group. Thus the aggregate function is evaluated separately for every
group. group.
</para> </para>
...@@ -1396,7 +1396,7 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO) ...@@ -1396,7 +1396,7 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO)
<para> <para>
In our example we got four groups and now we can apply the aggregate In our example we got four groups and now we can apply the aggregate
operator COUNT to every group leading to the final result of the query function COUNT to every group leading to the final result of the query
given above. given above.
</para> </para>
</example> </example>
...@@ -1404,9 +1404,9 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO) ...@@ -1404,9 +1404,9 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO)
<para> <para>
Note that for a query using GROUP BY and aggregate Note that for a query using GROUP BY and aggregate
operators to make sense the target list can only refer directly to functions to make sense, the target list can only refer directly to
the attributes being grouped by. Other attributes may only be used the attributes being grouped by. Other attributes may only be used
inside the argument of an aggregate function. Otherwise there would inside the arguments of aggregate functions. Otherwise there would
not be a unique value to associate with the other attributes. not be a unique value to associate with the other attributes.
</para> </para>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.107 2006/06/26 17:24:40 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.108 2006/07/27 19:52:04 tgl Exp $ -->
<chapter id="sql-syntax"> <chapter id="sql-syntax">
<title>SQL Syntax</title> <title>SQL Syntax</title>
...@@ -673,8 +673,9 @@ CAST ( '<replaceable>string</replaceable>' AS <replaceable>type</replaceable> ) ...@@ -673,8 +673,9 @@ CAST ( '<replaceable>string</replaceable>' AS <replaceable>type</replaceable> )
<para> <para>
The asterisk (<literal>*</literal>) is used in some contexts to denote The asterisk (<literal>*</literal>) is used in some contexts to denote
all the fields of a table row or composite value. It also all the fields of a table row or composite value. It also
has a special meaning when used as the argument of the has a special meaning when used as the argument of an
<function>COUNT</function> aggregate function. aggregate function, namely that the aggregate does not require
any explicit parameter.
</para> </para>
</listitem> </listitem>
...@@ -1269,9 +1270,9 @@ sqrt(2) ...@@ -1269,9 +1270,9 @@ sqrt(2)
syntax of an aggregate expression is one of the following: syntax of an aggregate expression is one of the following:
<synopsis> <synopsis>
<replaceable>aggregate_name</replaceable> (<replaceable>expression</replaceable>) <replaceable>aggregate_name</replaceable> (<replaceable>expression</replaceable> [ , ... ] )
<replaceable>aggregate_name</replaceable> (ALL <replaceable>expression</replaceable>) <replaceable>aggregate_name</replaceable> (ALL <replaceable>expression</replaceable> [ , ... ] )
<replaceable>aggregate_name</replaceable> (DISTINCT <replaceable>expression</replaceable>) <replaceable>aggregate_name</replaceable> (DISTINCT <replaceable>expression</replaceable> [ , ... ] )
<replaceable>aggregate_name</replaceable> ( * ) <replaceable>aggregate_name</replaceable> ( * )
</synopsis> </synopsis>
...@@ -1284,16 +1285,16 @@ sqrt(2) ...@@ -1284,16 +1285,16 @@ sqrt(2)
<para> <para>
The first form of aggregate expression invokes the aggregate The first form of aggregate expression invokes the aggregate
across all input rows for which the given expression yields a across all input rows for which the given expression(s) yield
non-null value. (Actually, it is up to the aggregate function non-null values. (Actually, it is up to the aggregate function
whether to ignore null values or not &mdash; but all the standard ones do.) whether to ignore null values or not &mdash; but all the standard ones do.)
The second form is the same as the first, since The second form is the same as the first, since
<literal>ALL</literal> is the default. The third form invokes the <literal>ALL</literal> is the default. The third form invokes the
aggregate for all distinct non-null values of the expression found aggregate for all distinct non-null values of the expressions found
in the input rows. The last form invokes the aggregate once for in the input rows. The last form invokes the aggregate once for
each input row regardless of null or non-null values; since no each input row regardless of null or non-null values; since no
particular input value is specified, it is generally only useful particular input value is specified, it is generally only useful
for the <function>count()</function> aggregate function. for the <function>count(*)</function> aggregate function.
</para> </para>
<para> <para>
...@@ -1323,7 +1324,7 @@ sqrt(2) ...@@ -1323,7 +1324,7 @@ sqrt(2)
<xref linkend="sql-syntax-scalar-subqueries"> and <xref linkend="sql-syntax-scalar-subqueries"> and
<xref linkend="functions-subquery">), the aggregate is normally <xref linkend="functions-subquery">), the aggregate is normally
evaluated over the rows of the subquery. But an exception occurs evaluated over the rows of the subquery. But an exception occurs
if the aggregate's argument contains only outer-level variables: if the aggregate's arguments contain only outer-level variables:
the aggregate then belongs to the nearest such outer level, and is the aggregate then belongs to the nearest such outer level, and is
evaluated over the rows of that query. The aggregate expression evaluated over the rows of that query. The aggregate expression
as a whole is then an outer reference for the subquery it appears in, as a whole is then an outer reference for the subquery it appears in,
...@@ -1332,6 +1333,13 @@ sqrt(2) ...@@ -1332,6 +1333,13 @@ sqrt(2)
appearing only in the result list or <literal>HAVING</> clause appearing only in the result list or <literal>HAVING</> clause
applies with respect to the query level that the aggregate belongs to. applies with respect to the query level that the aggregate belongs to.
</para> </para>
<note>
<para>
<productname>PostgreSQL</productname> currently does not support
<literal>DISTINCT</> with more than one input expression.
</para>
</note>
</sect2> </sect2>
<sect2 id="sql-syntax-type-casts"> <sect2 id="sql-syntax-type-casts">
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/xaggr.sgml,v 1.31 2006/04/15 17:45:33 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/xaggr.sgml,v 1.32 2006/07/27 19:52:04 tgl Exp $ -->
<sect1 id="xaggr"> <sect1 id="xaggr">
<title>User-Defined Aggregates</title> <title>User-Defined Aggregates</title>
...@@ -10,11 +10,11 @@ ...@@ -10,11 +10,11 @@
<para> <para>
Aggregate functions in <productname>PostgreSQL</productname> Aggregate functions in <productname>PostgreSQL</productname>
are expressed as <firstterm>state values</firstterm> are expressed in terms of <firstterm>state values</firstterm>
and <firstterm>state transition functions</firstterm>. and <firstterm>state transition functions</firstterm>.
That is, an aggregate can be That is, an aggregate operates using a state value that is updated
defined in terms of state that is modified whenever an as each successive input row is processed.
input item is processed. To define a new aggregate To define a new aggregate
function, one selects a data type for the state value, function, one selects a data type for the state value,
an initial value for the state, and a state transition an initial value for the state, and a state transition
function. The state transition function is just an function. The state transition function is just an
...@@ -85,13 +85,14 @@ SELECT sum(a) FROM test_complex; ...@@ -85,13 +85,14 @@ SELECT sum(a) FROM test_complex;
Another bit of default behavior for a <quote>strict</> transition function Another bit of default behavior for a <quote>strict</> transition function
is that the previous state value is retained unchanged whenever a is that the previous state value is retained unchanged whenever a
null input value is encountered. Thus, null values are ignored. If you null input value is encountered. Thus, null values are ignored. If you
need some other behavior for null inputs, just do not define your transition need some other behavior for null inputs, do not declare your
function as strict, and code it to test for null inputs and do transition function as strict; instead code it to test for null inputs and
whatever is needed. do whatever is needed.
</para> </para>
<para> <para>
<function>avg</> (average) is a more complex example of an aggregate. It requires <function>avg</> (average) is a more complex example of an aggregate.
It requires
two pieces of running state: the sum of the inputs and the count two pieces of running state: the sum of the inputs and the count
of the number of inputs. The final result is obtained by dividing of the number of inputs. The final result is obtained by dividing
these quantities. Average is typically implemented by using a these quantities. Average is typically implemented by using a
...@@ -117,7 +118,7 @@ CREATE AGGREGATE avg (float8) ...@@ -117,7 +118,7 @@ CREATE AGGREGATE avg (float8)
See <xref linkend="extend-types-polymorphic"> See <xref linkend="extend-types-polymorphic">
for an explanation of polymorphic functions. for an explanation of polymorphic functions.
Going a step further, the aggregate function itself may be specified Going a step further, the aggregate function itself may be specified
with a polymorphic input type and state type, allowing a single with polymorphic input type(s) and state type, allowing a single
aggregate definition to serve for multiple input data types. aggregate definition to serve for multiple input data types.
Here is an example of a polymorphic aggregate: Here is an example of a polymorphic aggregate:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -42,7 +42,8 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
void void
AggregateCreate(const char *aggName, AggregateCreate(const char *aggName,
Oid aggNamespace, Oid aggNamespace,
Oid aggBaseType, Oid *aggArgTypes,
int numArgs,
List *aggtransfnName, List *aggtransfnName,
List *aggfinalfnName, List *aggfinalfnName,
List *aggsortopName, List *aggsortopName,
...@@ -57,9 +58,10 @@ AggregateCreate(const char *aggName, ...@@ -57,9 +58,10 @@ AggregateCreate(const char *aggName,
Oid transfn; Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */ Oid finalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */
bool hasPolyArg;
Oid rettype; Oid rettype;
Oid finaltype; Oid finaltype;
Oid fnArgs[2]; /* we only deal with 1- and 2-arg fns */ Oid *fnArgs;
int nargs_transfn; int nargs_transfn;
Oid procOid; Oid procOid;
TupleDesc tupDesc; TupleDesc tupDesc;
...@@ -74,27 +76,34 @@ AggregateCreate(const char *aggName, ...@@ -74,27 +76,34 @@ AggregateCreate(const char *aggName,
if (!aggtransfnName) if (!aggtransfnName)
elog(ERROR, "aggregate must have a transition function"); 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 * If transtype is polymorphic, must have polymorphic argument also;
* will have no way to deduce the actual transtype. * else we will have no way to deduce the actual transtype.
*/ */
if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) && if (!hasPolyArg &&
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID)) (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine transition data type"), errmsg("cannot determine transition data type"),
errdetail("An aggregate using \"anyarray\" or \"anyelement\" as " errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type.")));
"transition type must have one of them as its base type.")));
/* handle transfn */ /* find the transfn */
nargs_transfn = numArgs + 1;
fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
fnArgs[0] = aggTransType; fnArgs[0] = aggTransType;
if (aggBaseType == ANYOID) memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
nargs_transfn = 1;
else
{
fnArgs[1] = aggBaseType;
nargs_transfn = 2;
}
transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
&rettype); &rettype);
...@@ -123,13 +132,14 @@ AggregateCreate(const char *aggName, ...@@ -123,13 +132,14 @@ AggregateCreate(const char *aggName,
proc = (Form_pg_proc) GETSTRUCT(tup); proc = (Form_pg_proc) GETSTRUCT(tup);
/* /*
* If the transfn is strict and the initval is NULL, make sure input type * If the transfn is strict and the initval is NULL, make sure first input
* and transtype are the same (or at least binary-compatible), so that * 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. * it's OK to use the first input value as the initial transValue.
*/ */
if (proc->proisstrict && agginitval == NULL) if (proc->proisstrict && agginitval == NULL)
{ {
if (!IsBinaryCoercible(aggBaseType, aggTransType)) if (numArgs < 1 ||
!IsBinaryCoercible(aggArgTypes[0], aggTransType))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (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"))); 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, ...@@ -153,32 +163,37 @@ AggregateCreate(const char *aggName,
Assert(OidIsValid(finaltype)); 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. * 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 * happen, unless someone has snuck a finalfn definition into the catalogs
* that itself violates the rule against polymorphic result with no * that itself violates the rule against polymorphic result with no
* polymorphic input.) * polymorphic input.)
*/ */
if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) && if (!hasPolyArg &&
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID)) (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine result data type"), errmsg("cannot determine result data type"),
errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " 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 */ /* handle sortop, if supplied */
if (aggsortopName) 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, sortop = LookupOperName(NULL, aggsortopName,
aggBaseType, aggBaseType, aggArgTypes[0], aggArgTypes[0],
false, -1); false, -1);
}
/* /*
* Everything looks okay. Try to create the pg_proc entry for the * Everything looks okay. Try to create the pg_proc entry for the
* aggregate. (This could fail if there's already a conflicting entry.) * aggregate. (This could fail if there's already a conflicting entry.)
*/ */
fnArgs[0] = aggBaseType;
procOid = ProcedureCreate(aggName, procOid = ProcedureCreate(aggName,
aggNamespace, aggNamespace,
...@@ -195,7 +210,8 @@ AggregateCreate(const char *aggName, ...@@ -195,7 +210,8 @@ AggregateCreate(const char *aggName,
false, /* isStrict (not needed for agg) */ false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */ * needed for agg) */
buildoidvector(fnArgs, 1), /* paramTypes */ buildoidvector(aggArgTypes,
numArgs), /* paramTypes */
PointerGetDatum(NULL), /* allParamTypes */ PointerGetDatum(NULL), /* allParamTypes */
PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL)); /* parameterNames */ PointerGetDatum(NULL)); /* parameterNames */
...@@ -279,6 +295,8 @@ lookup_agg_function(List *fnName, ...@@ -279,6 +295,8 @@ lookup_agg_function(List *fnName,
Oid *true_oid_array; Oid *true_oid_array;
FuncDetailCode fdresult; FuncDetailCode fdresult;
AclResult aclresult; AclResult aclresult;
int i;
bool allPolyArgs = true;
/* /*
* func_get_detail looks up the function in the catalogs, does * func_get_detail looks up the function in the catalogs, does
...@@ -307,13 +325,17 @@ lookup_agg_function(List *fnName, ...@@ -307,13 +325,17 @@ lookup_agg_function(List *fnName,
* If the given type(s) are all polymorphic, there's nothing we can check. * If the given type(s) are all polymorphic, there's nothing we can check.
* Otherwise, enforce consistency, and possibly refine the result type. * Otherwise, enforce consistency, and possibly refine the result type.
*/ */
if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) && for (i = 0; i < nargs; i++)
(nargs == 1 ||
(input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
{ {
/* 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, *rettype = enforce_generic_type_consistency(input_types,
true_oid_array, true_oid_array,
...@@ -325,22 +347,16 @@ lookup_agg_function(List *fnName, ...@@ -325,22 +347,16 @@ lookup_agg_function(List *fnName,
* func_get_detail will find functions requiring run-time argument type * func_get_detail will find functions requiring run-time argument type
* coercion, but nodeAgg.c isn't prepared to deal with that * coercion, but nodeAgg.c isn't prepared to deal with that
*/ */
if (true_oid_array[0] != ANYARRAYOID && for (i = 0; i < nargs; i++)
true_oid_array[0] != ANYELEMENTOID && {
!IsBinaryCoercible(input_types[0], true_oid_array[0])) if (true_oid_array[i] != ANYARRAYOID &&
ereport(ERROR, true_oid_array[i] != ANYELEMENTOID &&
(errcode(ERRCODE_DATATYPE_MISMATCH), !IsBinaryCoercible(input_types[i], true_oid_array[i]))
errmsg("function %s requires run-time type coercion", ereport(ERROR,
func_signature_string(fnName, nargs, true_oid_array)))); (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion",
if (nargs == 2 && func_signature_string(fnName, nargs, true_oid_array))));
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))));
/* Check aggregate creator has permission to call the function */ /* Check aggregate creator has permission to call the function */
aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE); aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -57,7 +57,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) ...@@ -57,7 +57,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
TypeName *baseType = NULL; TypeName *baseType = NULL;
TypeName *transType = NULL; TypeName *transType = NULL;
char *initval = NULL; char *initval = NULL;
Oid baseTypeId; Oid *aggArgTypes;
int numArgs;
Oid transTypeId; Oid transTypeId;
ListCell *pl; ListCell *pl;
...@@ -116,12 +117,13 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) ...@@ -116,12 +117,13 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
errmsg("aggregate sfunc must be specified"))); errmsg("aggregate sfunc must be specified")));
/* /*
* look up the aggregate's input datatype. * look up the aggregate's input datatype(s).
*/ */
if (oldstyle) 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' * Historically we allowed the command to look like basetype = 'ANY'
* so we must do a case-insensitive comparison for the name ANY. Ugh. * 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) ...@@ -132,37 +134,37 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
errmsg("aggregate input type must be specified"))); errmsg("aggregate input type must be specified")));
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0) if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
baseTypeId = ANYOID; {
numArgs = 0;
aggArgTypes = NULL;
}
else else
baseTypeId = typenameTypeId(NULL, baseType); {
numArgs = 1;
aggArgTypes = (Oid *) palloc(sizeof(Oid));
aggArgTypes[0] = typenameTypeId(NULL, baseType);
}
} }
else else
{ {
/* /*
* New style: args is a list of TypeNames. For the moment, though, * New style: args is a list of TypeNames (possibly zero of 'em).
* we allow at most one.
*/ */
ListCell *lc;
int i = 0;
if (baseType != NULL) if (baseType != NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("basetype is redundant with aggregate input type specification"))); errmsg("basetype is redundant with aggregate input type specification")));
if (args == NIL) numArgs = list_length(args);
{ aggArgTypes = (Oid *) palloc(sizeof(Oid) * numArgs);
/* special case for agg(*) */ foreach(lc, args)
baseTypeId = ANYOID;
}
else if (list_length(args) != 1)
{ {
/* temporarily reject > 1 arg */ TypeName *curTypeName = (TypeName *) lfirst(lc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
errmsg("aggregates can have only one input")));
baseTypeId = InvalidOid; /* keep compiler quiet */
}
else
{
baseTypeId = typenameTypeId(NULL, (TypeName *) linitial(args));
} }
} }
...@@ -187,7 +189,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) ...@@ -187,7 +189,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
*/ */
AggregateCreate(aggName, /* aggregate name */ AggregateCreate(aggName, /* aggregate name */
aggNamespace, /* namespace */ aggNamespace, /* namespace */
baseTypeId, /* type of data being aggregated */ aggArgTypes, /* input data type(s) */
numArgs,
transfuncName, /* step function name */ transfuncName, /* step function name */
finalfuncName, /* final function name */ finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */ sortoperatorName, /* sort operator name */
...@@ -211,7 +214,7 @@ RemoveAggregate(RemoveFuncStmt *stmt) ...@@ -211,7 +214,7 @@ RemoveAggregate(RemoveFuncStmt *stmt)
/* Look up function and make sure it's an aggregate */ /* Look up function and make sure it's an aggregate */
procOid = LookupAggNameTypeNames(aggName, aggArgs, stmt->missing_ok); procOid = LookupAggNameTypeNames(aggName, aggArgs, stmt->missing_ok);
if (!OidIsValid(procOid)) if (!OidIsValid(procOid))
{ {
/* we only get here if stmt->missing_ok is true */ /* we only get here if stmt->missing_ok is true */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -3174,10 +3174,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
aggstate->aggs = lcons(astate, aggstate->aggs); aggstate->aggs = lcons(astate, aggstate->aggs);
naggs = ++aggstate->numaggs; 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 * aggregates; nested agg functions are semantically
* nonsensical. (This should have been caught earlier, * nonsensical. (This should have been caught earlier,
* but we defend against it here anyway.) * but we defend against it here anyway.)
......
This diff is collapsed.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -743,7 +743,7 @@ _copyAggref(Aggref *from)
COPY_SCALAR_FIELD(aggfnoid); COPY_SCALAR_FIELD(aggfnoid);
COPY_SCALAR_FIELD(aggtype); COPY_SCALAR_FIELD(aggtype);
COPY_NODE_FIELD(target); COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(agglevelsup); COPY_SCALAR_FIELD(agglevelsup);
COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggstar);
COPY_SCALAR_FIELD(aggdistinct); COPY_SCALAR_FIELD(aggdistinct);
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -156,7 +156,7 @@ _equalAggref(Aggref *a, Aggref *b)
{ {
COMPARE_SCALAR_FIELD(aggfnoid); COMPARE_SCALAR_FIELD(aggfnoid);
COMPARE_SCALAR_FIELD(aggtype); COMPARE_SCALAR_FIELD(aggtype);
COMPARE_NODE_FIELD(target); COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(aggstar);
COMPARE_SCALAR_FIELD(aggdistinct); COMPARE_SCALAR_FIELD(aggdistinct);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -635,7 +635,7 @@ _outAggref(StringInfo str, Aggref *node) ...@@ -635,7 +635,7 @@ _outAggref(StringInfo str, Aggref *node)
WRITE_OID_FIELD(aggfnoid); WRITE_OID_FIELD(aggfnoid);
WRITE_OID_FIELD(aggtype); WRITE_OID_FIELD(aggtype);
WRITE_NODE_FIELD(target); WRITE_NODE_FIELD(args);
WRITE_UINT_FIELD(agglevelsup); WRITE_UINT_FIELD(agglevelsup);
WRITE_BOOL_FIELD(aggstar); WRITE_BOOL_FIELD(aggstar);
WRITE_BOOL_FIELD(aggdistinct); WRITE_BOOL_FIELD(aggdistinct);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -348,7 +348,7 @@ _readAggref(void) ...@@ -348,7 +348,7 @@ _readAggref(void)
READ_OID_FIELD(aggfnoid); READ_OID_FIELD(aggfnoid);
READ_OID_FIELD(aggtype); READ_OID_FIELD(aggtype);
READ_NODE_FIELD(target); READ_NODE_FIELD(args);
READ_UINT_FIELD(agglevelsup); READ_UINT_FIELD(agglevelsup);
READ_BOOL_FIELD(aggstar); READ_BOOL_FIELD(aggstar);
READ_BOOL_FIELD(aggdistinct); READ_BOOL_FIELD(aggdistinct);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -217,12 +217,13 @@ find_minmax_aggs_walker(Node *node, List **context)
{ {
Aggref *aggref = (Aggref *) node; Aggref *aggref = (Aggref *) node;
Oid aggsortop; Oid aggsortop;
Expr *curTarget;
MinMaxAggInfo *info; MinMaxAggInfo *info;
ListCell *l; ListCell *l;
Assert(aggref->agglevelsup == 0); Assert(aggref->agglevelsup == 0);
if (aggref->aggstar) if (list_length(aggref->args) != 1)
return true; /* foo(*) is surely not optimizable */ return true; /* it couldn't be MIN/MAX */
/* note: we do not care if DISTINCT is mentioned ... */ /* note: we do not care if DISTINCT is mentioned ... */
aggsortop = fetch_agg_sort_op(aggref->aggfnoid); aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
...@@ -232,18 +233,19 @@ find_minmax_aggs_walker(Node *node, List **context) ...@@ -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. * Check whether it's already in the list, and add it if not.
*/ */
curTarget = linitial(aggref->args);
foreach(l, *context) foreach(l, *context)
{ {
info = (MinMaxAggInfo *) lfirst(l); info = (MinMaxAggInfo *) lfirst(l);
if (info->aggfnoid == aggref->aggfnoid && if (info->aggfnoid == aggref->aggfnoid &&
equal(info->target, aggref->target)) equal(info->target, curTarget))
return false; return false;
} }
info = (MinMaxAggInfo *) palloc0(sizeof(MinMaxAggInfo)); info = (MinMaxAggInfo *) palloc0(sizeof(MinMaxAggInfo));
info->aggfnoid = aggref->aggfnoid; info->aggfnoid = aggref->aggfnoid;
info->aggsortop = aggsortop; info->aggsortop = aggsortop;
info->target = aggref->target; info->target = curTarget;
*context = lappend(*context, info); *context = lappend(*context, info);
...@@ -520,13 +522,14 @@ replace_aggs_with_params_mutator(Node *node, List **context) ...@@ -520,13 +522,14 @@ replace_aggs_with_params_mutator(Node *node, List **context)
{ {
Aggref *aggref = (Aggref *) node; Aggref *aggref = (Aggref *) node;
ListCell *l; ListCell *l;
Expr *curTarget = linitial(aggref->args);
foreach(l, *context) foreach(l, *context)
{ {
MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l); MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l);
if (info->aggfnoid == aggref->aggfnoid && if (info->aggfnoid == aggref->aggfnoid &&
equal(info->target, aggref->target)) equal(info->target, curTarget))
return (Node *) info->param; return (Node *) info->param;
} }
elog(ERROR, "failed to re-find aggregate info record"); elog(ERROR, "failed to re-find aggregate info record");
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -397,17 +397,27 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts) ...@@ -397,17 +397,27 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
if (IsA(node, Aggref)) if (IsA(node, Aggref))
{ {
Aggref *aggref = (Aggref *) node; Aggref *aggref = (Aggref *) node;
Oid inputType; Oid *inputTypes;
int numArguments;
HeapTuple aggTuple; HeapTuple aggTuple;
Form_pg_aggregate aggform; Form_pg_aggregate aggform;
Oid aggtranstype; Oid aggtranstype;
int i;
ListCell *l;
Assert(aggref->agglevelsup == 0); Assert(aggref->agglevelsup == 0);
counts->numAggs++; counts->numAggs++;
if (aggref->aggdistinct) if (aggref->aggdistinct)
counts->numDistinctAggs++; 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 */ /* fetch aggregate transition datatype from pg_aggregate */
aggTuple = SearchSysCache(AGGFNOID, aggTuple = SearchSysCache(AGGFNOID,
...@@ -423,17 +433,18 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts) ...@@ -423,17 +433,18 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
/* resolve actual type of transition state, if polymorphic */ /* resolve actual type of transition state, if polymorphic */
if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID) if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
{ {
/* have to fetch the agg's declared input type... */ /* have to fetch the agg's declared input types... */
Oid *agg_arg_types; Oid *declaredArgTypes;
int agg_nargs; int agg_nargs;
(void) get_func_signature(aggref->aggfnoid, (void) get_func_signature(aggref->aggfnoid,
&agg_arg_types, &agg_nargs); &declaredArgTypes, &agg_nargs);
Assert(agg_nargs == 1); Assert(agg_nargs == numArguments);
aggtranstype = resolve_generic_type(aggtranstype, aggtranstype = enforce_generic_type_consistency(inputTypes,
inputType, declaredArgTypes,
agg_arg_types[0]); agg_nargs,
pfree(agg_arg_types); aggtranstype);
pfree(declaredArgTypes);
} }
/* /*
...@@ -448,12 +459,12 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts) ...@@ -448,12 +459,12 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
int32 avgwidth; int32 avgwidth;
/* /*
* If transition state is of same type as input, assume it's the * If transition state is of same type as first input, assume it's
* same typmod (same width) as well. This works for cases like * the same typmod (same width) as well. This works for cases
* MAX/MIN and is probably somewhat reasonable otherwise. * like MAX/MIN and is probably somewhat reasonable otherwise.
*/ */
if (aggtranstype == inputType) if (numArguments > 0 && aggtranstype == inputTypes[0])
aggtranstypmod = exprTypmod((Node *) aggref->target); aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
else else
aggtranstypmod = -1; aggtranstypmod = -1;
...@@ -464,10 +475,10 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts) ...@@ -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. * nested agg functions are semantically nonsensical.
*/ */
if (contain_agg_clause((Node *) aggref->target)) if (contain_agg_clause((Node *) aggref->args))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("aggregate function calls may not be nested"))); errmsg("aggregate function calls may not be nested")));
...@@ -3026,7 +3037,14 @@ expression_tree_walker(Node *node, ...@@ -3026,7 +3037,14 @@ expression_tree_walker(Node *node,
/* primitive node types with no expression subnodes */ /* primitive node types with no expression subnodes */
break; break;
case T_Aggref: 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: case T_ArrayRef:
{ {
ArrayRef *aref = (ArrayRef *) node; ArrayRef *aref = (ArrayRef *) node;
...@@ -3448,7 +3466,7 @@ expression_tree_mutator(Node *node, ...@@ -3448,7 +3466,7 @@ expression_tree_mutator(Node *node,
Aggref *newnode; Aggref *newnode;
FLATCOPY(newnode, aggref, Aggref); FLATCOPY(newnode, aggref, Aggref);
MUTATE(newnode->target, aggref->target, Expr *); MUTATE(newnode->args, aggref->args, List *);
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -7346,10 +7346,8 @@ func_expr: func_name '(' ')' ...@@ -7346,10 +7346,8 @@ func_expr: func_name '(' ')'
| func_name '(' '*' ')' | func_name '(' '*' ')'
{ {
/* /*
* For now, we transform AGGREGATE(*) into AGGREGATE(1). * We consider AGGREGATE(*) to invoke a parameterless
* * aggregate. This does the right thing for COUNT(*),
* This does the right thing for COUNT(*) (in fact,
* any certainly-non-null expression would do for COUNT),
* and there are no other aggregates in SQL92 that accept * and there are no other aggregates in SQL92 that accept
* '*' as parameter. * '*' as parameter.
* *
...@@ -7358,12 +7356,8 @@ func_expr: func_name '(' ')' ...@@ -7358,12 +7356,8 @@ func_expr: func_name '(' ')'
* really was. * really was.
*/ */
FuncCall *n = makeNode(FuncCall); FuncCall *n = makeNode(FuncCall);
A_Const *star = makeNode(A_Const);
star->val.type = T_Integer;
star->val.val.ival = 1;
n->funcname = $1; n->funcname = $1;
n->args = list_make1(star); n->args = NIL;
n->agg_star = TRUE; n->agg_star = TRUE;
n->agg_distinct = FALSE; n->agg_distinct = FALSE;
n->location = @1; n->location = @1;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -55,10 +55,10 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
/* /*
* The aggregate's level is the same as the level of the lowest-level * 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. * 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 * An aggregate can't directly contain another aggregate call of the same
...@@ -67,7 +67,7 @@ transformAggregateCall(ParseState *pstate, Aggref *agg) ...@@ -67,7 +67,7 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
*/ */
if (min_varlevel == 0) if (min_varlevel == 0)
{ {
if (checkExprHasAggs((Node *) agg->target)) if (checkExprHasAggs((Node *) agg->args))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("aggregate function calls may not be nested"))); errmsg("aggregate function calls may not be nested")));
...@@ -360,7 +360,7 @@ check_ungrouped_columns_walker(Node *node, ...@@ -360,7 +360,7 @@ check_ungrouped_columns_walker(Node *node,
* (The trees will never actually be executed, however, so we can skimp * (The trees will never actually be executed, however, so we can skimp
* a bit on correctness.) * 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 * transition, and result types of the aggregate. These should all be
* resolved to actual types (ie, none should ever be ANYARRAY or ANYELEMENT). * resolved to actual types (ie, none should ever be ANYARRAY or ANYELEMENT).
* *
...@@ -371,7 +371,8 @@ check_ungrouped_columns_walker(Node *node, ...@@ -371,7 +371,8 @@ check_ungrouped_columns_walker(Node *node,
* *finalfnexpr. The latter is set to NULL if there's no finalfn. * *finalfnexpr. The latter is set to NULL if there's no finalfn.
*/ */
void 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_state_type,
Oid agg_result_type, Oid agg_result_type,
Oid transfn_oid, Oid transfn_oid,
...@@ -379,13 +380,9 @@ build_aggregate_fnexprs(Oid agg_input_type, ...@@ -379,13 +380,9 @@ build_aggregate_fnexprs(Oid agg_input_type,
Expr **transfnexpr, Expr **transfnexpr,
Expr **finalfnexpr) Expr **finalfnexpr)
{ {
int transfn_nargs; Param *argp;
Param *arg0;
Param *arg1;
List *args; List *args;
int i;
/* get the transition function arg count */
transfn_nargs = get_func_nargs(transfn_oid);
/* /*
* Build arg list to use in the transfn FuncExpr node. We really only care * 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, ...@@ -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 * get_fn_expr_argtype(), so it's okay to use Param nodes that don't
* correspond to any real Param. * correspond to any real Param.
*/ */
arg0 = makeNode(Param); argp = makeNode(Param);
arg0->paramkind = PARAM_EXEC; argp->paramkind = PARAM_EXEC;
arg0->paramid = -1; argp->paramid = -1;
arg0->paramtype = agg_state_type; argp->paramtype = agg_state_type;
if (transfn_nargs == 2) args = list_make1(argp);
{
arg1 = makeNode(Param);
arg1->paramkind = PARAM_EXEC;
arg1->paramid = -1;
arg1->paramtype = agg_input_type;
args = list_make2(arg0, arg1); for (i = 0; i < agg_num_inputs; i++)
{
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, *transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
agg_state_type, agg_state_type,
...@@ -425,11 +421,11 @@ build_aggregate_fnexprs(Oid agg_input_type, ...@@ -425,11 +421,11 @@ build_aggregate_fnexprs(Oid agg_input_type,
/* /*
* Build expr tree for final function * Build expr tree for final function
*/ */
arg0 = makeNode(Param); argp = makeNode(Param);
arg0->paramkind = PARAM_EXEC; argp->paramkind = PARAM_EXEC;
arg0->paramid = -1; argp->paramid = -1;
arg0->paramtype = agg_state_type; argp->paramtype = agg_state_type;
args = list_make1(arg0); args = list_make1(argp);
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid, *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
agg_result_type, agg_result_type,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -259,10 +259,21 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggfnoid = funcid; aggref->aggfnoid = funcid;
aggref->aggtype = rettype; aggref->aggtype = rettype;
aggref->target = linitial(fargs); aggref->args = fargs;
aggref->aggstar = agg_star; aggref->aggstar = agg_star;
aggref->aggdistinct = agg_distinct; 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 */ /* parse_agg.c does additional aggregate-specific processing */
transformAggregateCall(pstate, aggref); transformAggregateCall(pstate, aggref);
...@@ -1194,9 +1205,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) ...@@ -1194,9 +1205,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError)
* *
* This is almost like LookupFuncNameTypeNames, but the error messages refer * This is almost like LookupFuncNameTypeNames, but the error messages refer
* to aggregates rather than plain functions, and we verify that the found * to aggregates rather than plain functions, and we verify that the found
* function really is an aggregate, and we recognize the convention used by * function really is an aggregate.
* the grammar that agg(*) translates to a NIL list, which we have to treat
* as one ANY argument. (XXX this ought to be changed)
*/ */
Oid Oid
LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
...@@ -1204,7 +1213,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) ...@@ -1204,7 +1213,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
Oid argoids[FUNC_MAX_ARGS]; Oid argoids[FUNC_MAX_ARGS];
int argcount; int argcount;
int i; int i;
ListCell *args_item; ListCell *lc;
Oid oid; Oid oid;
HeapTuple ftup; HeapTuple ftup;
Form_pg_proc pform; Form_pg_proc pform;
...@@ -1216,29 +1225,18 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) ...@@ -1216,29 +1225,18 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
errmsg("functions cannot have more than %d arguments", errmsg("functions cannot have more than %d arguments",
FUNC_MAX_ARGS))); 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); TypeName *t = (TypeName *) lfirst(lc);
for (i = 0; i < argcount; i++)
{
TypeName *t = (TypeName *) lfirst(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))));
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); oid = LookupFuncName(aggname, argcount, argoids, true);
...@@ -1247,7 +1245,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) ...@@ -1247,7 +1245,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
{ {
if (noError) if (noError)
return InvalidOid; return InvalidOid;
if (argcount == 1 && argoids[0] == ANYOID) if (argcount == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("aggregate %s(*) does not exist", errmsg("aggregate %s(*) does not exist",
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees * ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text * 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" #include "postgres.h"
...@@ -3880,15 +3880,29 @@ static void ...@@ -3880,15 +3880,29 @@ static void
get_agg_expr(Aggref *aggref, deparse_context *context) get_agg_expr(Aggref *aggref, deparse_context *context)
{ {
StringInfo buf = context->buf; 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", appendStringInfo(buf, "%s(%s",
generate_function_name(aggref->aggfnoid, 1, &argtype), generate_function_name(aggref->aggfnoid, nargs, argtypes),
aggref->aggdistinct ? "DISTINCT " : ""); aggref->aggdistinct ? "DISTINCT " : "");
/* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar) if (aggref->aggstar)
appendStringInfo(buf, "*"); appendStringInfoChar(buf, '*');
else else
get_rule_expr((Node *) aggref->target, context, true); get_rule_expr((Node *) aggref->args, context, true);
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
} }
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* by PostgreSQL * by PostgreSQL
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.441 2006/07/14 14:52:26 momjian Exp $ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.442 2006/07/27 19:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2325,7 +2325,8 @@ getAggregates(int *numAggs) ...@@ -2325,7 +2325,8 @@ getAggregates(int *numAggs)
int i_oid; int i_oid;
int i_aggname; int i_aggname;
int i_aggnamespace; int i_aggnamespace;
int i_aggbasetype; int i_pronargs;
int i_proargtypes;
int i_rolname; int i_rolname;
int i_aggacl; int i_aggacl;
...@@ -2334,11 +2335,25 @@ getAggregates(int *numAggs) ...@@ -2334,11 +2335,25 @@ getAggregates(int *numAggs)
/* find all user-defined aggregates */ /* find all user-defined aggregates */
if (g_fout->remoteVersion >= 70300) if (g_fout->remoteVersion >= 80200)
{ {
appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, " appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
"pronamespace as aggnamespace, " "pronamespace as aggnamespace, "
"proargtypes[0] as aggbasetype, " "pronargs, proargtypes, "
"(%s proowner) as rolname, "
"proacl as aggacl "
"FROM pg_proc "
"WHERE proisagg "
"AND pronamespace != "
"(select oid from pg_namespace where nspname = 'pg_catalog')",
username_subquery);
}
else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
"pronamespace as aggnamespace, "
"CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END as pronargs, "
"proargtypes, "
"(%s proowner) as rolname, " "(%s proowner) as rolname, "
"proacl as aggacl " "proacl as aggacl "
"FROM pg_proc " "FROM pg_proc "
...@@ -2351,7 +2366,8 @@ getAggregates(int *numAggs) ...@@ -2351,7 +2366,8 @@ getAggregates(int *numAggs)
{ {
appendPQExpBuffer(query, "SELECT tableoid, oid, aggname, " appendPQExpBuffer(query, "SELECT tableoid, oid, aggname, "
"0::oid as aggnamespace, " "0::oid as aggnamespace, "
"aggbasetype, " "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END as pronargs, "
"aggbasetype as proargtypes, "
"(%s aggowner) as rolname, " "(%s aggowner) as rolname, "
"'{=X}' as aggacl " "'{=X}' as aggacl "
"FROM pg_aggregate " "FROM pg_aggregate "
...@@ -2365,7 +2381,8 @@ getAggregates(int *numAggs) ...@@ -2365,7 +2381,8 @@ getAggregates(int *numAggs)
"(SELECT oid FROM pg_class WHERE relname = 'pg_aggregate') AS tableoid, " "(SELECT oid FROM pg_class WHERE relname = 'pg_aggregate') AS tableoid, "
"oid, aggname, " "oid, aggname, "
"0::oid as aggnamespace, " "0::oid as aggnamespace, "
"aggbasetype, " "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END as pronargs, "
"aggbasetype as proargtypes, "
"(%s aggowner) as rolname, " "(%s aggowner) as rolname, "
"'{=X}' as aggacl " "'{=X}' as aggacl "
"FROM pg_aggregate " "FROM pg_aggregate "
...@@ -2386,7 +2403,8 @@ getAggregates(int *numAggs) ...@@ -2386,7 +2403,8 @@ getAggregates(int *numAggs)
i_oid = PQfnumber(res, "oid"); i_oid = PQfnumber(res, "oid");
i_aggname = PQfnumber(res, "aggname"); i_aggname = PQfnumber(res, "aggname");
i_aggnamespace = PQfnumber(res, "aggnamespace"); i_aggnamespace = PQfnumber(res, "aggnamespace");
i_aggbasetype = PQfnumber(res, "aggbasetype"); i_pronargs = PQfnumber(res, "pronargs");
i_proargtypes = PQfnumber(res, "proargtypes");
i_rolname = PQfnumber(res, "rolname"); i_rolname = PQfnumber(res, "rolname");
i_aggacl = PQfnumber(res, "aggacl"); i_aggacl = PQfnumber(res, "aggacl");
...@@ -2404,13 +2422,21 @@ getAggregates(int *numAggs) ...@@ -2404,13 +2422,21 @@ getAggregates(int *numAggs)
write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n", write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
agginfo[i].aggfn.dobj.name); agginfo[i].aggfn.dobj.name);
agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */ agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
agginfo[i].aggfn.nargs = 1;
agginfo[i].aggfn.argtypes = (Oid *) malloc(sizeof(Oid));
agginfo[i].aggfn.argtypes[0] = atooid(PQgetvalue(res, i, i_aggbasetype));
agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */ agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl)); agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl));
agginfo[i].anybasetype = false; /* computed when it's dumped */ agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
agginfo[i].fmtbasetype = NULL; /* computed when it's dumped */ if (agginfo[i].aggfn.nargs == 0)
agginfo[i].aggfn.argtypes = NULL;
else
{
agginfo[i].aggfn.argtypes = (Oid *) malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
if (g_fout->remoteVersion >= 70300)
parseOidArray(PQgetvalue(res, i, i_proargtypes),
agginfo[i].aggfn.argtypes,
agginfo[i].aggfn.nargs);
else /* it's just aggbasetype */
agginfo[i].aggfn.argtypes[0] = atooid(PQgetvalue(res, i, i_proargtypes));
}
/* Decide whether we want to dump it */ /* Decide whether we want to dump it */
selectDumpableObject(&(agginfo[i].aggfn.dobj)); selectDumpableObject(&(agginfo[i].aggfn.dobj));
...@@ -6759,6 +6785,7 @@ static char * ...@@ -6759,6 +6785,7 @@ static char *
format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes) format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
{ {
PQExpBufferData buf; PQExpBufferData buf;
int j;
initPQExpBuffer(&buf); initPQExpBuffer(&buf);
if (honor_quotes) if (honor_quotes)
...@@ -6767,23 +6794,24 @@ format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes) ...@@ -6767,23 +6794,24 @@ format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
else else
appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name); appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name);
/* If using regtype or format_type, fmtbasetype is already quoted */ if (agginfo->aggfn.nargs == 0)
if (fout->remoteVersion >= 70100) appendPQExpBuffer(&buf, "(*)");
{
if (agginfo->anybasetype)
appendPQExpBuffer(&buf, "(*)");
else
appendPQExpBuffer(&buf, "(%s)", agginfo->fmtbasetype);
}
else else
{ {
if (agginfo->anybasetype) appendPQExpBuffer(&buf, "(");
appendPQExpBuffer(&buf, "(*)"); for (j = 0; j < agginfo->aggfn.nargs; j++)
else {
appendPQExpBuffer(&buf, "(%s)", char *typname;
fmtId(agginfo->fmtbasetype));
} typname = getFormattedTypeName(agginfo->aggfn.argtypes[j], zeroAsOpaque);
appendPQExpBuffer(&buf, "%s%s",
(j > 0) ? ", " : "",
typname);
free(typname);
}
appendPQExpBuffer(&buf, ")");
}
return buf.data; return buf.data;
} }
...@@ -6807,8 +6835,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6807,8 +6835,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
int i_aggsortop; int i_aggsortop;
int i_aggtranstype; int i_aggtranstype;
int i_agginitval; int i_agginitval;
int i_anybasetype;
int i_fmtbasetype;
int i_convertok; int i_convertok;
const char *aggtransfn; const char *aggtransfn;
const char *aggfinalfn; const char *aggfinalfn;
...@@ -6836,8 +6862,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6836,8 +6862,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"aggfinalfn, aggtranstype::pg_catalog.regtype, " "aggfinalfn, aggtranstype::pg_catalog.regtype, "
"aggsortop::pg_catalog.regoperator, " "aggsortop::pg_catalog.regoperator, "
"agginitval, " "agginitval, "
"proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
"'t'::boolean as convertok " "'t'::boolean as convertok "
"from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"where a.aggfnoid = p.oid " "where a.aggfnoid = p.oid "
...@@ -6850,8 +6874,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6850,8 +6874,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"aggfinalfn, aggtranstype::pg_catalog.regtype, " "aggfinalfn, aggtranstype::pg_catalog.regtype, "
"0 as aggsortop, " "0 as aggsortop, "
"agginitval, " "agginitval, "
"proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
"'t'::boolean as convertok " "'t'::boolean as convertok "
"from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"where a.aggfnoid = p.oid " "where a.aggfnoid = p.oid "
...@@ -6864,9 +6886,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6864,9 +6886,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"format_type(aggtranstype, NULL) as aggtranstype, " "format_type(aggtranstype, NULL) as aggtranstype, "
"0 as aggsortop, " "0 as aggsortop, "
"agginitval, " "agginitval, "
"aggbasetype = 0 as anybasetype, "
"CASE WHEN aggbasetype = 0 THEN '-' "
"ELSE format_type(aggbasetype, NULL) END as fmtbasetype, "
"'t'::boolean as convertok " "'t'::boolean as convertok "
"from pg_aggregate " "from pg_aggregate "
"where oid = '%u'::oid", "where oid = '%u'::oid",
...@@ -6879,8 +6898,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6879,8 +6898,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"(select typname from pg_type where oid = aggtranstype1) as aggtranstype, " "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
"0 as aggsortop, " "0 as aggsortop, "
"agginitval1 as agginitval, " "agginitval1 as agginitval, "
"aggbasetype = 0 as anybasetype, "
"(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
"(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok " "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok "
"from pg_aggregate " "from pg_aggregate "
"where oid = '%u'::oid", "where oid = '%u'::oid",
...@@ -6904,8 +6921,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6904,8 +6921,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
i_aggsortop = PQfnumber(res, "aggsortop"); i_aggsortop = PQfnumber(res, "aggsortop");
i_aggtranstype = PQfnumber(res, "aggtranstype"); i_aggtranstype = PQfnumber(res, "aggtranstype");
i_agginitval = PQfnumber(res, "agginitval"); i_agginitval = PQfnumber(res, "agginitval");
i_anybasetype = PQfnumber(res, "anybasetype");
i_fmtbasetype = PQfnumber(res, "fmtbasetype");
i_convertok = PQfnumber(res, "convertok"); i_convertok = PQfnumber(res, "convertok");
aggtransfn = PQgetvalue(res, 0, i_aggtransfn); aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
...@@ -6913,10 +6928,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6913,10 +6928,6 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
aggsortop = PQgetvalue(res, 0, i_aggsortop); aggsortop = PQgetvalue(res, 0, i_aggsortop);
aggtranstype = PQgetvalue(res, 0, i_aggtranstype); aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
agginitval = PQgetvalue(res, 0, i_agginitval); agginitval = PQgetvalue(res, 0, i_agginitval);
/* we save anybasetype for format_aggregate_signature */
agginfo->anybasetype = (PQgetvalue(res, 0, i_anybasetype)[0] == 't');
/* we save fmtbasetype for format_aggregate_signature */
agginfo->fmtbasetype = strdup(PQgetvalue(res, 0, i_fmtbasetype));
convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
aggsig = format_aggregate_signature(agginfo, fout, true); aggsig = format_aggregate_signature(agginfo, fout, true);
...@@ -6932,27 +6943,20 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6932,27 +6943,20 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
if (g_fout->remoteVersion >= 70300) if (g_fout->remoteVersion >= 70300)
{ {
/* If using 7.3's regproc or regtype, data is already quoted */ /* If using 7.3's regproc or regtype, data is already quoted */
appendPQExpBuffer(details, " BASETYPE = %s,\n SFUNC = %s,\n STYPE = %s", appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
agginfo->anybasetype ? "'any'" :
agginfo->fmtbasetype,
aggtransfn, aggtransfn,
aggtranstype); aggtranstype);
} }
else if (g_fout->remoteVersion >= 70100) else if (g_fout->remoteVersion >= 70100)
{ {
/* format_type quotes, regproc does not */ /* format_type quotes, regproc does not */
appendPQExpBuffer(details, " BASETYPE = %s,\n SFUNC = %s,\n STYPE = %s", appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
agginfo->anybasetype ? "'any'" :
agginfo->fmtbasetype,
fmtId(aggtransfn), fmtId(aggtransfn),
aggtranstype); aggtranstype);
} }
else else
{ {
/* need quotes all around */ /* need quotes all around */
appendPQExpBuffer(details, " BASETYPE = %s,\n",
agginfo->anybasetype ? "'any'" :
fmtId(agginfo->fmtbasetype));
appendPQExpBuffer(details, " SFUNC = %s,\n", appendPQExpBuffer(details, " SFUNC = %s,\n",
fmtId(aggtransfn)); fmtId(aggtransfn));
appendPQExpBuffer(details, " STYPE = %s", appendPQExpBuffer(details, " STYPE = %s",
...@@ -6986,8 +6990,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -6986,8 +6990,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
aggsig); aggsig);
appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n", appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
fmtId(agginfo->aggfn.dobj.name), aggsig, details->data);
details->data);
ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId, ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
aggsig_tag, aggsig_tag,
...@@ -7008,7 +7011,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) ...@@ -7008,7 +7011,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
/* /*
* Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
* command look like a function's GRANT; in particular this affects the * command look like a function's GRANT; in particular this affects the
* syntax for aggregates on ANY. * syntax for zero-argument aggregates.
*/ */
free(aggsig); free(aggsig);
free(aggsig_tag); free(aggsig_tag);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.126 2006/07/02 02:23:21 momjian Exp $ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.127 2006/07/27 19:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -147,8 +147,7 @@ typedef struct _funcInfo ...@@ -147,8 +147,7 @@ typedef struct _funcInfo
typedef struct _aggInfo typedef struct _aggInfo
{ {
FuncInfo aggfn; FuncInfo aggfn;
bool anybasetype; /* is the basetype "any"? */ /* we don't require any other fields at the moment */
char *fmtbasetype; /* formatted type name */
} AggInfo; } AggInfo;
typedef struct _oprInfo typedef struct _oprInfo
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2006, PostgreSQL Global Development Group * Copyright (c) 2000-2006, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.141 2006/07/17 00:21:23 neilc Exp $ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.142 2006/07/27 19:52:06 tgl Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
#include "describe.h" #include "describe.h"
...@@ -67,17 +67,22 @@ describeAggregates(const char *pattern, bool verbose) ...@@ -67,17 +67,22 @@ describeAggregates(const char *pattern, bool verbose)
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n" "SELECT n.nspname as \"%s\",\n"
" p.proname AS \"%s\",\n" " p.proname AS \"%s\",\n"
" CASE p.proargtypes[0]\n" " CASE WHEN p.pronargs = 0\n"
" WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype\n" " THEN CAST('*' AS pg_catalog.text)\n"
" THEN CAST('%s' AS pg_catalog.text)\n" " ELSE\n"
" ELSE pg_catalog.format_type(p.proargtypes[0], NULL)\n" " pg_catalog.array_to_string(ARRAY(\n"
" SELECT\n"
" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
" FROM\n"
" pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
" ), ', ')\n"
" END AS \"%s\",\n" " END AS \"%s\",\n"
" pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n" " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
"FROM pg_catalog.pg_proc p\n" "FROM pg_catalog.pg_proc p\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
"WHERE p.proisagg\n", "WHERE p.proisagg\n",
_("Schema"), _("Name"), _("(all types)"), _("Schema"), _("Name"),
_("Data type"), _("Description")); _("Argument data types"), _("Description"));
processNamePattern(&buf, pattern, true, false, processNamePattern(&buf, pattern, true, false,
"n.nspname", "p.proname", NULL, "n.nspname", "p.proname", NULL,
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.341 2006/07/26 19:31:51 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.342 2006/07/27 19:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200607261 #define CATALOG_VERSION_NO 200607271
#endif #endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.55 2006/07/21 20:51:33 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.56 2006/07/27 19:52:06 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -140,11 +140,9 @@ DATA(insert ( 2051 array_smaller - 1072 2277 _null_ )); ...@@ -140,11 +140,9 @@ DATA(insert ( 2051 array_smaller - 1072 2277 _null_ ));
DATA(insert ( 2245 bpchar_smaller - 1058 1042 _null_ )); DATA(insert ( 2245 bpchar_smaller - 1058 1042 _null_ ));
DATA(insert ( 2798 tidsmaller - 2799 27 _null_ )); DATA(insert ( 2798 tidsmaller - 2799 27 _null_ ));
/* /* count */
* Using int8inc for count() is cheating a little, since it really only DATA(insert ( 2147 int8inc_any - 0 20 "0" ));
* takes 1 parameter not 2, but nodeAgg.c won't complain ... DATA(insert ( 2803 int8inc - 0 20 "0" ));
*/
DATA(insert ( 2147 int8inc - 0 20 0 ));
/* var_pop */ /* var_pop */
DATA(insert ( 2718 int8_accum numeric_var_pop 0 1231 "{0,0,0}" )); DATA(insert ( 2718 int8_accum numeric_var_pop 0 1231 "{0,0,0}" ));
...@@ -214,7 +212,8 @@ DATA(insert ( 2243 bitor - 0 1560 _null_ )); ...@@ -214,7 +212,8 @@ DATA(insert ( 2243 bitor - 0 1560 _null_ ));
*/ */
extern void AggregateCreate(const char *aggName, extern void AggregateCreate(const char *aggName,
Oid aggNamespace, Oid aggNamespace,
Oid aggBaseType, Oid *aggArgTypes,
int numArgs,
List *aggtransfnName, List *aggtransfnName,
List *aggfinalfnName, List *aggfinalfnName,
List *aggsortopName, List *aggsortopName,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.417 2006/07/25 03:51:21 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.418 2006/07/27 19:52:06 tgl Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -1534,6 +1534,8 @@ DESCR("truncate interval to specified units"); ...@@ -1534,6 +1534,8 @@ DESCR("truncate interval to specified units");
DATA(insert OID = 1219 ( int8inc PGNSP PGUID 12 f f t f i 1 20 "20" _null_ _null_ _null_ int8inc - _null_ )); DATA(insert OID = 1219 ( int8inc PGNSP PGUID 12 f f t f i 1 20 "20" _null_ _null_ _null_ int8inc - _null_ ));
DESCR("increment"); DESCR("increment");
DATA(insert OID = 2804 ( int8inc_any PGNSP PGUID 12 f f t f i 2 20 "20 2276" _null_ _null_ _null_ int8inc - _null_ ));
DESCR("increment, ignores second argument");
DATA(insert OID = 1230 ( int8abs PGNSP PGUID 12 f f t f i 1 20 "20" _null_ _null_ _null_ int8abs - _null_ )); DATA(insert OID = 1230 ( int8abs PGNSP PGUID 12 f f t f i 1 20 "20" _null_ _null_ _null_ int8abs - _null_ ));
DESCR("absolute value"); DESCR("absolute value");
...@@ -3148,7 +3150,9 @@ DATA(insert OID = 2051 ( min PGNSP PGUID 12 t f f f i 1 2277 "2277" _null_ _ ...@@ -3148,7 +3150,9 @@ DATA(insert OID = 2051 ( min PGNSP PGUID 12 t f f f i 1 2277 "2277" _null_ _
DATA(insert OID = 2245 ( min PGNSP PGUID 12 t f f f i 1 1042 "1042" _null_ _null_ _null_ aggregate_dummy - _null_ )); DATA(insert OID = 2245 ( min PGNSP PGUID 12 t f f f i 1 1042 "1042" _null_ _null_ _null_ aggregate_dummy - _null_ ));
DATA(insert OID = 2798 ( min PGNSP PGUID 12 t f f f i 1 27 "27" _null_ _null_ _null_ aggregate_dummy - _null_ )); DATA(insert OID = 2798 ( min PGNSP PGUID 12 t f f f i 1 27 "27" _null_ _null_ _null_ aggregate_dummy - _null_ ));
/* count has two forms: count(any) and count(*) */
DATA(insert OID = 2147 ( count PGNSP PGUID 12 t f f f i 1 20 "2276" _null_ _null_ _null_ aggregate_dummy - _null_ )); DATA(insert OID = 2147 ( count PGNSP PGUID 12 t f f f i 1 20 "2276" _null_ _null_ _null_ aggregate_dummy - _null_ ));
DATA(insert OID = 2803 ( count PGNSP PGUID 12 t f f f i 0 20 "" _null_ _null_ _null_ aggregate_dummy - _null_ ));
DATA(insert OID = 2718 ( var_pop PGNSP PGUID 12 t f f f i 1 1700 "20" _null_ _null_ _null_ aggregate_dummy - _null_ )); DATA(insert OID = 2718 ( var_pop PGNSP PGUID 12 t f f f i 1 1700 "20" _null_ _null_ _null_ aggregate_dummy - _null_ ));
DATA(insert OID = 2719 ( var_pop PGNSP PGUID 12 t f f f i 1 1700 "23" _null_ _null_ _null_ aggregate_dummy - _null_ )); DATA(insert OID = 2719 ( var_pop PGNSP PGUID 12 t f f f i 1 1700 "23" _null_ _null_ _null_ aggregate_dummy - _null_ ));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.154 2006/07/26 00:34:48 momjian Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.155 2006/07/27 19:52:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -449,7 +449,7 @@ typedef struct GenericExprState ...@@ -449,7 +449,7 @@ typedef struct GenericExprState
typedef struct AggrefExprState typedef struct AggrefExprState
{ {
ExprState xprstate; ExprState xprstate;
ExprState *target; /* state of my child node */ List *args; /* states of argument expressions */
int aggno; /* ID number for agg within its plan node */ int aggno; /* ID number for agg within its plan node */
} AggrefExprState; } AggrefExprState;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.114 2006/07/13 16:49:19 momjian Exp $ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.115 2006/07/27 19:52:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -184,9 +184,9 @@ typedef struct Aggref ...@@ -184,9 +184,9 @@ typedef struct Aggref
Expr xpr; Expr xpr;
Oid aggfnoid; /* pg_proc Oid of the aggregate */ Oid aggfnoid; /* pg_proc Oid of the aggregate */
Oid aggtype; /* type Oid of result of the aggregate */ Oid aggtype; /* type Oid of result of the aggregate */
Expr *target; /* expression we are aggregating on */ List *args; /* arguments to the aggregate */
Index agglevelsup; /* > 0 if agg belongs to outer query */ Index agglevelsup; /* > 0 if agg belongs to outer query */
bool aggstar; /* TRUE if argument was really '*' */ bool aggstar; /* TRUE if argument list was really '*' */
bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */ bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */
} Aggref; } Aggref;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parse_agg.h,v 1.33 2006/03/05 15:58:57 momjian Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_agg.h,v 1.34 2006/07/27 19:52:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,7 +19,8 @@ extern void transformAggregateCall(ParseState *pstate, Aggref *agg); ...@@ -19,7 +19,8 @@ extern void transformAggregateCall(ParseState *pstate, Aggref *agg);
extern void parseCheckAggregates(ParseState *pstate, Query *qry); extern void parseCheckAggregates(ParseState *pstate, Query *qry);
extern void build_aggregate_fnexprs(Oid agg_input_type, extern void build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
Oid agg_state_type, Oid agg_state_type,
Oid agg_result_type, Oid agg_result_type,
Oid transfn_oid, Oid transfn_oid,
......
...@@ -181,6 +181,7 @@ group by ten order by ten; ...@@ -181,6 +181,7 @@ group by ten order by ten;
9 | 100 | 4 9 | 100 | 4
(10 rows) (10 rows)
-- user-defined aggregates
SELECT newavg(four) AS avg_1 FROM onek; SELECT newavg(four) AS avg_1 FROM onek;
avg_1 avg_1
-------------------- --------------------
...@@ -199,6 +200,24 @@ SELECT newcnt(four) AS cnt_1000 FROM onek; ...@@ -199,6 +200,24 @@ SELECT newcnt(four) AS cnt_1000 FROM onek;
1000 1000
(1 row) (1 row)
SELECT newcnt(*) AS cnt_1000 FROM onek;
cnt_1000
----------
1000
(1 row)
SELECT oldcnt(*) AS cnt_1000 FROM onek;
cnt_1000
----------
1000
(1 row)
SELECT sum2(q1,q2) FROM int8_tbl;
sum2
-------------------
18271560493827981
(1 row)
-- test for outer-level aggregates -- test for outer-level aggregates
-- this should work -- this should work
select ten, sum(distinct four) from onek a select ten, sum(distinct four) from onek a
......
...@@ -17,12 +17,29 @@ CREATE AGGREGATE newsum ( ...@@ -17,12 +17,29 @@ CREATE AGGREGATE newsum (
sfunc1 = int4pl, basetype = int4, stype1 = int4, sfunc1 = int4pl, basetype = int4, stype1 = int4,
initcond1 = '0' initcond1 = '0'
); );
-- value-independent transition function -- zero-argument aggregate
CREATE AGGREGATE newcnt ( CREATE AGGREGATE newcnt (*) (
sfunc = int4inc, basetype = 'any', stype = int4, sfunc = int8inc, stype = int8,
initcond = '0'
);
-- old-style spelling of same
CREATE AGGREGATE oldcnt (
sfunc = int8inc, basetype = 'ANY', stype = int8,
initcond = '0'
);
-- aggregate that only cares about null/nonnull input
CREATE AGGREGATE newcnt ("any") (
sfunc = int8inc_any, stype = int8,
initcond = '0'
);
-- multi-argument aggregate
create function sum3(int8,int8,int8) returns int8 as
'select $1 + $2 + $3' language sql strict immutable;
create aggregate sum2(int8,int8) (
sfunc = sum3, stype = int8,
initcond = '0' initcond = '0'
); );
COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail'; COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail';
ERROR: aggregate nosuchagg(*) does not exist ERROR: aggregate nosuchagg(*) does not exist
COMMENT ON AGGREGATE newcnt (*) IS 'an any agg comment'; COMMENT ON AGGREGATE newcnt (*) IS 'an agg(*) comment';
COMMENT ON AGGREGATE newcnt (*) IS NULL; COMMENT ON AGGREGATE newcnt ("any") IS 'an agg(any) comment';
...@@ -51,7 +51,7 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR ...@@ -51,7 +51,7 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR
-- Look for conflicting proc definitions (same names and input datatypes). -- Look for conflicting proc definitions (same names and input datatypes).
-- (This test should be dead code now that we have the unique index -- (This test should be dead code now that we have the unique index
-- pg_proc_proname_narg_type_index, but I'll leave it in anyway.) -- pg_proc_proname_args_nsp_index, but I'll leave it in anyway.)
SELECT p1.oid, p1.proname, p2.oid, p2.proname SELECT p1.oid, p1.proname, p2.oid, p2.proname
FROM pg_proc AS p1, pg_proc AS p2 FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND WHERE p1.oid != p2.oid AND
...@@ -67,11 +67,14 @@ WHERE p1.oid != p2.oid AND ...@@ -67,11 +67,14 @@ WHERE p1.oid != p2.oid AND
-- have several entries with different pronames for the same internal function, -- have several entries with different pronames for the same internal function,
-- but conflicts in the number of arguments and other critical items should -- but conflicts in the number of arguments and other critical items should
-- be complained of. -- be complained of.
-- Ignore aggregates, since they all use "aggregate_dummy".
-- As of 8.2, this finds int8inc and int8inc_any, which are OK.
SELECT p1.oid, p1.proname, p2.oid, p2.proname SELECT p1.oid, p1.proname, p2.oid, p2.proname
FROM pg_proc AS p1, pg_proc AS p2 FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND WHERE p1.oid < p2.oid AND
p1.prosrc = p2.prosrc AND p1.prosrc = p2.prosrc AND
p1.prolang = 12 AND p2.prolang = 12 AND p1.prolang = 12 AND p2.prolang = 12 AND
p1.proisagg = false AND p2.proisagg = false AND
(p1.prolang != p2.prolang OR (p1.prolang != p2.prolang OR
p1.proisagg != p2.proisagg OR p1.proisagg != p2.proisagg OR
p1.prosecdef != p2.prosecdef OR p1.prosecdef != p2.prosecdef OR
...@@ -79,9 +82,10 @@ WHERE p1.oid != p2.oid AND ...@@ -79,9 +82,10 @@ WHERE p1.oid != p2.oid AND
p1.proretset != p2.proretset OR p1.proretset != p2.proretset OR
p1.provolatile != p2.provolatile OR p1.provolatile != p2.provolatile OR
p1.pronargs != p2.pronargs); p1.pronargs != p2.pronargs);
oid | proname | oid | proname oid | proname | oid | proname
-----+---------+-----+--------- ------+---------+------+-------------
(0 rows) 1219 | int8inc | 2804 | int8inc_any
(1 row)
-- Look for uses of different type OIDs in the argument/result type fields -- Look for uses of different type OIDs in the argument/result type fields
-- for different aliases of the same built-in function. -- for different aliases of the same built-in function.
...@@ -617,7 +621,7 @@ WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0; ...@@ -617,7 +621,7 @@ WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0;
SELECT a.aggfnoid::oid, p.proname SELECT a.aggfnoid::oid, p.proname
FROM pg_aggregate as a, pg_proc as p FROM pg_aggregate as a, pg_proc as p
WHERE a.aggfnoid = p.oid AND WHERE a.aggfnoid = p.oid AND
(NOT p.proisagg OR p.pronargs != 1 OR p.proretset); (NOT p.proisagg OR p.proretset);
aggfnoid | proname aggfnoid | proname
----------+--------- ----------+---------
(0 rows) (0 rows)
...@@ -648,13 +652,17 @@ FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr ...@@ -648,13 +652,17 @@ FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
WHERE a.aggfnoid = p.oid AND WHERE a.aggfnoid = p.oid AND
a.aggtransfn = ptr.oid AND a.aggtransfn = ptr.oid AND
(ptr.proretset (ptr.proretset
OR NOT (ptr.pronargs = p.pronargs + 1)
OR NOT physically_coercible(ptr.prorettype, a.aggtranstype) OR NOT physically_coercible(ptr.prorettype, a.aggtranstype)
OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0]) OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0])
OR NOT ((ptr.pronargs = 2 AND OR (p.pronargs > 0 AND
physically_coercible(p.proargtypes[0], ptr.proargtypes[1])) NOT physically_coercible(p.proargtypes[0], ptr.proargtypes[1]))
OR OR (p.pronargs > 1 AND
(ptr.pronargs = 1 AND NOT physically_coercible(p.proargtypes[1], ptr.proargtypes[2]))
p.proargtypes[0] = '"any"'::regtype))); OR (p.pronargs > 2 AND
NOT physically_coercible(p.proargtypes[2], ptr.proargtypes[3]))
-- we could carry the check further, but that's enough for now
);
aggfnoid | proname | oid | proname aggfnoid | proname | oid | proname
----------+---------+-----+--------- ----------+---------+-----+---------
(0 rows) (0 rows)
......
This diff is collapsed.
...@@ -48,11 +48,13 @@ group by ten order by ten; ...@@ -48,11 +48,13 @@ group by ten order by ten;
select ten, count(four), sum(DISTINCT four) from onek select ten, count(four), sum(DISTINCT four) from onek
group by ten order by ten; group by ten order by ten;
-- user-defined aggregates
SELECT newavg(four) AS avg_1 FROM onek; SELECT newavg(four) AS avg_1 FROM onek;
SELECT newsum(four) AS sum_1500 FROM onek; SELECT newsum(four) AS sum_1500 FROM onek;
SELECT newcnt(four) AS cnt_1000 FROM onek; SELECT newcnt(four) AS cnt_1000 FROM onek;
SELECT newcnt(*) AS cnt_1000 FROM onek;
SELECT oldcnt(*) AS cnt_1000 FROM onek;
SELECT sum2(q1,q2) FROM int8_tbl;
-- test for outer-level aggregates -- test for outer-level aggregates
......
...@@ -20,12 +20,33 @@ CREATE AGGREGATE newsum ( ...@@ -20,12 +20,33 @@ CREATE AGGREGATE newsum (
initcond1 = '0' initcond1 = '0'
); );
-- value-independent transition function -- zero-argument aggregate
CREATE AGGREGATE newcnt ( CREATE AGGREGATE newcnt (*) (
sfunc = int4inc, basetype = 'any', stype = int4, sfunc = int8inc, stype = int8,
initcond = '0'
);
-- old-style spelling of same
CREATE AGGREGATE oldcnt (
sfunc = int8inc, basetype = 'ANY', stype = int8,
initcond = '0'
);
-- aggregate that only cares about null/nonnull input
CREATE AGGREGATE newcnt ("any") (
sfunc = int8inc_any, stype = int8,
initcond = '0'
);
-- multi-argument aggregate
create function sum3(int8,int8,int8) returns int8 as
'select $1 + $2 + $3' language sql strict immutable;
create aggregate sum2(int8,int8) (
sfunc = sum3, stype = int8,
initcond = '0' initcond = '0'
); );
COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail'; COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail';
COMMENT ON AGGREGATE newcnt (*) IS 'an any agg comment'; COMMENT ON AGGREGATE newcnt (*) IS 'an agg(*) comment';
COMMENT ON AGGREGATE newcnt (*) IS NULL; COMMENT ON AGGREGATE newcnt ("any") IS 'an agg(any) comment';
...@@ -55,7 +55,7 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR ...@@ -55,7 +55,7 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR
-- Look for conflicting proc definitions (same names and input datatypes). -- Look for conflicting proc definitions (same names and input datatypes).
-- (This test should be dead code now that we have the unique index -- (This test should be dead code now that we have the unique index
-- pg_proc_proname_narg_type_index, but I'll leave it in anyway.) -- pg_proc_proname_args_nsp_index, but I'll leave it in anyway.)
SELECT p1.oid, p1.proname, p2.oid, p2.proname SELECT p1.oid, p1.proname, p2.oid, p2.proname
FROM pg_proc AS p1, pg_proc AS p2 FROM pg_proc AS p1, pg_proc AS p2
...@@ -69,12 +69,16 @@ WHERE p1.oid != p2.oid AND ...@@ -69,12 +69,16 @@ WHERE p1.oid != p2.oid AND
-- have several entries with different pronames for the same internal function, -- have several entries with different pronames for the same internal function,
-- but conflicts in the number of arguments and other critical items should -- but conflicts in the number of arguments and other critical items should
-- be complained of. -- be complained of.
-- Ignore aggregates, since they all use "aggregate_dummy".
-- As of 8.2, this finds int8inc and int8inc_any, which are OK.
SELECT p1.oid, p1.proname, p2.oid, p2.proname SELECT p1.oid, p1.proname, p2.oid, p2.proname
FROM pg_proc AS p1, pg_proc AS p2 FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND WHERE p1.oid < p2.oid AND
p1.prosrc = p2.prosrc AND p1.prosrc = p2.prosrc AND
p1.prolang = 12 AND p2.prolang = 12 AND p1.prolang = 12 AND p2.prolang = 12 AND
p1.proisagg = false AND p2.proisagg = false AND
(p1.prolang != p2.prolang OR (p1.prolang != p2.prolang OR
p1.proisagg != p2.proisagg OR p1.proisagg != p2.proisagg OR
p1.prosecdef != p2.prosecdef OR p1.prosecdef != p2.prosecdef OR
...@@ -515,7 +519,7 @@ WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0; ...@@ -515,7 +519,7 @@ WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0;
SELECT a.aggfnoid::oid, p.proname SELECT a.aggfnoid::oid, p.proname
FROM pg_aggregate as a, pg_proc as p FROM pg_aggregate as a, pg_proc as p
WHERE a.aggfnoid = p.oid AND WHERE a.aggfnoid = p.oid AND
(NOT p.proisagg OR p.pronargs != 1 OR p.proretset); (NOT p.proisagg OR p.proretset);
-- Make sure there are no proisagg pg_proc entries without matches. -- Make sure there are no proisagg pg_proc entries without matches.
...@@ -539,13 +543,17 @@ FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr ...@@ -539,13 +543,17 @@ FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
WHERE a.aggfnoid = p.oid AND WHERE a.aggfnoid = p.oid AND
a.aggtransfn = ptr.oid AND a.aggtransfn = ptr.oid AND
(ptr.proretset (ptr.proretset
OR NOT (ptr.pronargs = p.pronargs + 1)
OR NOT physically_coercible(ptr.prorettype, a.aggtranstype) OR NOT physically_coercible(ptr.prorettype, a.aggtranstype)
OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0]) OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0])
OR NOT ((ptr.pronargs = 2 AND OR (p.pronargs > 0 AND
physically_coercible(p.proargtypes[0], ptr.proargtypes[1])) NOT physically_coercible(p.proargtypes[0], ptr.proargtypes[1]))
OR OR (p.pronargs > 1 AND
(ptr.pronargs = 1 AND NOT physically_coercible(p.proargtypes[1], ptr.proargtypes[2]))
p.proargtypes[0] = '"any"'::regtype))); OR (p.pronargs > 2 AND
NOT physically_coercible(p.proargtypes[2], ptr.proargtypes[3]))
-- we could carry the check further, but that's enough for now
);
-- Cross-check finalfn (if present) against its entry in pg_proc. -- Cross-check finalfn (if present) against its entry in pg_proc.
......
...@@ -57,6 +57,10 @@ CREATE FUNCTION tf1p(anyarray,int) RETURNS anyarray AS ...@@ -57,6 +57,10 @@ CREATE FUNCTION tf1p(anyarray,int) RETURNS anyarray AS
CREATE FUNCTION tf2p(int[],anyelement) RETURNS int[] AS CREATE FUNCTION tf2p(int[],anyelement) RETURNS int[] AS
'select $1' LANGUAGE SQL; 'select $1' LANGUAGE SQL;
-- multi-arg polymorphic
CREATE FUNCTION sum3(anyelement,anyelement,anyelement) returns anyelement AS
'select $1+$2+$3' language sql strict;
-- finalfn polymorphic -- finalfn polymorphic
CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS
'select $1' LANGUAGE SQL; 'select $1' LANGUAGE SQL;
...@@ -78,26 +82,26 @@ CREATE FUNCTION ffnp(int[]) returns int[] as ...@@ -78,26 +82,26 @@ CREATE FUNCTION ffnp(int[]) returns int[] as
-- ------- -- -------
-- N N -- N N
-- should CREATE -- should CREATE
CREATE AGGREGATE myaggp01a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[], CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
FINALFUNC = ffp, INITCOND = '{}'); FINALFUNC = ffp, INITCOND = '{}');
-- P N -- P N
-- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) -- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
CREATE AGGREGATE myaggp02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray, CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}'); FINALFUNC = ffp, INITCOND = '{}');
-- N P -- N P
-- should CREATE -- should CREATE
CREATE AGGREGATE myaggp03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[], CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
FINALFUNC = ffp, INITCOND = '{}'); FINALFUNC = ffp, INITCOND = '{}');
CREATE AGGREGATE myaggp03b(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[], CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
INITCOND = '{}'); INITCOND = '{}');
-- P P -- P P
-- should ERROR: we have no way to resolve S -- should ERROR: we have no way to resolve S
CREATE AGGREGATE myaggp04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray, CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}'); FINALFUNC = ffp, INITCOND = '{}');
CREATE AGGREGATE myaggp04b(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray, CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}'); INITCOND = '{}');
...@@ -207,26 +211,26 @@ CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp, ...@@ -207,26 +211,26 @@ CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp,
-- ------- -- -------
-- N N -- N N
-- should CREATE -- should CREATE
CREATE AGGREGATE myaggn01a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[], CREATE AGGREGATE myaggn01a(*) (SFUNC = stfnp, STYPE = int4[],
FINALFUNC = ffnp, INITCOND = '{}'); FINALFUNC = ffnp, INITCOND = '{}');
CREATE AGGREGATE myaggn01b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[], CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
INITCOND = '{}'); INITCOND = '{}');
-- P N -- P N
-- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) -- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
CREATE AGGREGATE myaggn02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray, CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}'); FINALFUNC = ffnp, INITCOND = '{}');
CREATE AGGREGATE myaggn02b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray, CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}'); INITCOND = '{}');
-- N P -- N P
-- should CREATE -- should CREATE
CREATE AGGREGATE myaggn03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[], CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
FINALFUNC = ffnp, INITCOND = '{}'); FINALFUNC = ffnp, INITCOND = '{}');
-- P P -- P P
-- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) -- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
CREATE AGGREGATE myaggn04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray, CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}'); FINALFUNC = ffnp, INITCOND = '{}');
...@@ -330,6 +334,10 @@ CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p, ...@@ -330,6 +334,10 @@ CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp, CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp,
STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
-- multi-arg polymorphic
CREATE AGGREGATE mysum2(anyelement,anyelement) (SFUNC = sum3,
STYPE = anyelement, INITCOND = '0');
-- create test data for polymorphic aggregates -- create test data for polymorphic aggregates
create temp table t(f1 int, f2 int[], f3 text); create temp table t(f1 int, f2 int[], f3 text);
insert into t values(1,array[1],'a'); insert into t values(1,array[1],'a');
...@@ -365,3 +373,4 @@ select f3, myaggn08a(f1) from t group by f3; ...@@ -365,3 +373,4 @@ select f3, myaggn08a(f1) from t group by f3;
select f3, myaggn08b(f1) from t group by f3; select f3, myaggn08b(f1) from t group by f3;
select f3, myaggn09a(f1) from t group by f3; select f3, myaggn09a(f1) from t group by f3;
select f3, myaggn10a(f1) from t group by f3; select f3, myaggn10a(f1) from t group by f3;
select mysum2(f1, f1 + 1) from t;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment