mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
Aggregate functions now support multiple input arguments. I also took
the opportunity to treat COUNT(*) as a zero-argument aggregate instead of the old hack that equated it to COUNT(1); this is materially cleaner (no more weird ANYOID cases) and ought to be at least a tiny bit faster. Original patch by Sergey Koposov; review, documentation, simple regression tests, pg_dump and psql support by moi.
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
|
|
||||||
<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
|
|||||||
<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>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
{
|
{
|
||||||
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
|
|||||||
<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>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
|
|
||||||
<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> (
|
|||||||
</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> (
|
|||||||
<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> (
|
|||||||
<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> (
|
|||||||
<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> (
|
|||||||
|
|
||||||
<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;
|
|||||||
<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;
|
|||||||
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;
|
|||||||
<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;
|
|||||||
<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;
|
|||||||
<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>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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
|
|||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
@ -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>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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=>
|
|||||||
<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>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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
|
|||||||
</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)
|
|||||||
<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 —
|
aggregate functions described above can be applied to the groups —
|
||||||
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)
|
|||||||
|
|
||||||
<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)
|
|||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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> )
|
|||||||
<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)
|
|||||||
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)
|
|||||||
|
|
||||||
<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 — but all the standard ones do.)
|
whether to ignore null values or not — 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)
|
|||||||
<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)
|
|||||||
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">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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 @@
|
|||||||
|
|
||||||
<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;
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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);
|
||||||
|
foreach(lc, args)
|
||||||
{
|
{
|
||||||
/* special case for agg(*) */
|
TypeName *curTypeName = (TypeName *) lfirst(lc);
|
||||||
baseTypeId = ANYOID;
|
|
||||||
}
|
aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
|
||||||
else if (list_length(args) != 1)
|
|
||||||
{
|
|
||||||
/* temporarily reject > 1 arg */
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("aggregates can have only one input")));
|
|
||||||
baseTypeId = InvalidOid; /* keep compiler quiet */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
baseTypeId = typenameTypeId(NULL, (TypeName *) linitial(args));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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 */
|
||||||
|
@ -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)
|
|||||||
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.)
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
* ExecAgg evaluates each aggregate in the following steps:
|
* ExecAgg evaluates each aggregate in the following steps:
|
||||||
*
|
*
|
||||||
* transvalue = initcond
|
* transvalue = initcond
|
||||||
* foreach input_value do
|
* foreach input_tuple do
|
||||||
* transvalue = transfunc(transvalue, input_value)
|
* transvalue = transfunc(transvalue, input_value(s))
|
||||||
* result = finalfunc(transvalue)
|
* result = finalfunc(transvalue)
|
||||||
*
|
*
|
||||||
* If a finalfunc is not supplied then the result is just the ending
|
* If a finalfunc is not supplied then the result is just the ending
|
||||||
@ -16,12 +16,12 @@
|
|||||||
* If transfunc is marked "strict" in pg_proc and initcond is NULL,
|
* If transfunc is marked "strict" in pg_proc and initcond is NULL,
|
||||||
* then the first non-NULL input_value is assigned directly to transvalue,
|
* then the first non-NULL input_value is assigned directly to transvalue,
|
||||||
* and transfunc isn't applied until the second non-NULL input_value.
|
* and transfunc isn't applied until the second non-NULL input_value.
|
||||||
* The agg's input type and transtype must be the same in this case!
|
* The agg's first input type and transtype must be the same in this case!
|
||||||
*
|
*
|
||||||
* If transfunc is marked "strict" then NULL input_values are skipped,
|
* If transfunc is marked "strict" then NULL input_values are skipped,
|
||||||
* keeping the previous transvalue. If transfunc is not strict then it
|
* keeping the previous transvalue. If transfunc is not strict then it
|
||||||
* is called for every input tuple and must deal with NULL initcond
|
* is called for every input tuple and must deal with NULL initcond
|
||||||
* or NULL input_value for itself.
|
* or NULL input_values for itself.
|
||||||
*
|
*
|
||||||
* If finalfunc is marked "strict" then it is not called when the
|
* If finalfunc is marked "strict" then it is not called when the
|
||||||
* ending transvalue is NULL, instead a NULL result is created
|
* ending transvalue is NULL, instead a NULL result is created
|
||||||
@ -61,7 +61,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/executor/nodeAgg.c,v 1.144 2006/07/14 14:52:19 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.145 2006/07/27 19:52:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -103,6 +103,9 @@ typedef struct AggStatePerAggData
|
|||||||
AggrefExprState *aggrefstate;
|
AggrefExprState *aggrefstate;
|
||||||
Aggref *aggref;
|
Aggref *aggref;
|
||||||
|
|
||||||
|
/* number of input arguments for aggregate */
|
||||||
|
int numArguments;
|
||||||
|
|
||||||
/* Oids of transfer functions */
|
/* Oids of transfer functions */
|
||||||
Oid transfn_oid;
|
Oid transfn_oid;
|
||||||
Oid finalfn_oid; /* may be InvalidOid */
|
Oid finalfn_oid; /* may be InvalidOid */
|
||||||
@ -214,7 +217,7 @@ static void initialize_aggregates(AggState *aggstate,
|
|||||||
static void advance_transition_function(AggState *aggstate,
|
static void advance_transition_function(AggState *aggstate,
|
||||||
AggStatePerAgg peraggstate,
|
AggStatePerAgg peraggstate,
|
||||||
AggStatePerGroup pergroupstate,
|
AggStatePerGroup pergroupstate,
|
||||||
Datum newVal, bool isNull);
|
FunctionCallInfoData *fcinfo);
|
||||||
static void advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup);
|
static void advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup);
|
||||||
static void process_sorted_aggregate(AggState *aggstate,
|
static void process_sorted_aggregate(AggState *aggstate,
|
||||||
AggStatePerAgg peraggstate,
|
AggStatePerAgg peraggstate,
|
||||||
@ -314,7 +317,11 @@ initialize_aggregates(AggState *aggstate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a new input value, advance the transition function of an aggregate.
|
* Given new input value(s), advance the transition function of an aggregate.
|
||||||
|
*
|
||||||
|
* The new values (and null flags) have been preloaded into argument positions
|
||||||
|
* 1 and up in fcinfo, so that we needn't copy them again to pass to the
|
||||||
|
* transition function. No other fields of fcinfo are assumed valid.
|
||||||
*
|
*
|
||||||
* It doesn't matter which memory context this is called in.
|
* It doesn't matter which memory context this is called in.
|
||||||
*/
|
*/
|
||||||
@ -322,19 +329,24 @@ static void
|
|||||||
advance_transition_function(AggState *aggstate,
|
advance_transition_function(AggState *aggstate,
|
||||||
AggStatePerAgg peraggstate,
|
AggStatePerAgg peraggstate,
|
||||||
AggStatePerGroup pergroupstate,
|
AggStatePerGroup pergroupstate,
|
||||||
Datum newVal, bool isNull)
|
FunctionCallInfoData *fcinfo)
|
||||||
{
|
{
|
||||||
FunctionCallInfoData fcinfo;
|
int numArguments = peraggstate->numArguments;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
Datum newVal;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (peraggstate->transfn.fn_strict)
|
if (peraggstate->transfn.fn_strict)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* For a strict transfn, nothing happens at a NULL input tuple; we
|
* For a strict transfn, nothing happens when there's a NULL input;
|
||||||
* just keep the prior transValue.
|
* we just keep the prior transValue.
|
||||||
*/
|
*/
|
||||||
if (isNull)
|
for (i = 1; i <= numArguments; i++)
|
||||||
return;
|
{
|
||||||
|
if (fcinfo->argnull[i])
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (pergroupstate->noTransValue)
|
if (pergroupstate->noTransValue)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -347,7 +359,7 @@ advance_transition_function(AggState *aggstate,
|
|||||||
* do not need to pfree the old transValue, since it's NULL.
|
* do not need to pfree the old transValue, since it's NULL.
|
||||||
*/
|
*/
|
||||||
oldContext = MemoryContextSwitchTo(aggstate->aggcontext);
|
oldContext = MemoryContextSwitchTo(aggstate->aggcontext);
|
||||||
pergroupstate->transValue = datumCopy(newVal,
|
pergroupstate->transValue = datumCopy(fcinfo->arg[1],
|
||||||
peraggstate->transtypeByVal,
|
peraggstate->transtypeByVal,
|
||||||
peraggstate->transtypeLen);
|
peraggstate->transtypeLen);
|
||||||
pergroupstate->transValueIsNull = false;
|
pergroupstate->transValueIsNull = false;
|
||||||
@ -373,14 +385,13 @@ advance_transition_function(AggState *aggstate,
|
|||||||
/*
|
/*
|
||||||
* OK to call the transition function
|
* OK to call the transition function
|
||||||
*/
|
*/
|
||||||
InitFunctionCallInfoData(fcinfo, &(peraggstate->transfn), 2,
|
InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
|
||||||
|
numArguments + 1,
|
||||||
(void *) aggstate, NULL);
|
(void *) aggstate, NULL);
|
||||||
fcinfo.arg[0] = pergroupstate->transValue;
|
fcinfo->arg[0] = pergroupstate->transValue;
|
||||||
fcinfo.argnull[0] = pergroupstate->transValueIsNull;
|
fcinfo->argnull[0] = pergroupstate->transValueIsNull;
|
||||||
fcinfo.arg[1] = newVal;
|
|
||||||
fcinfo.argnull[1] = isNull;
|
|
||||||
|
|
||||||
newVal = FunctionCallInvoke(&fcinfo);
|
newVal = FunctionCallInvoke(fcinfo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If pass-by-ref datatype, must copy the new value into aggcontext and
|
* If pass-by-ref datatype, must copy the new value into aggcontext and
|
||||||
@ -390,7 +401,7 @@ advance_transition_function(AggState *aggstate,
|
|||||||
if (!peraggstate->transtypeByVal &&
|
if (!peraggstate->transtypeByVal &&
|
||||||
DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue))
|
DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue))
|
||||||
{
|
{
|
||||||
if (!fcinfo.isnull)
|
if (!fcinfo->isnull)
|
||||||
{
|
{
|
||||||
MemoryContextSwitchTo(aggstate->aggcontext);
|
MemoryContextSwitchTo(aggstate->aggcontext);
|
||||||
newVal = datumCopy(newVal,
|
newVal = datumCopy(newVal,
|
||||||
@ -402,7 +413,7 @@ advance_transition_function(AggState *aggstate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pergroupstate->transValue = newVal;
|
pergroupstate->transValue = newVal;
|
||||||
pergroupstate->transValueIsNull = fcinfo.isnull;
|
pergroupstate->transValueIsNull = fcinfo->isnull;
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
}
|
}
|
||||||
@ -423,27 +434,46 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
|
|||||||
|
|
||||||
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
|
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
|
||||||
{
|
{
|
||||||
AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
|
AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
|
||||||
AggStatePerGroup pergroupstate = &pergroup[aggno];
|
AggStatePerGroup pergroupstate = &pergroup[aggno];
|
||||||
AggrefExprState *aggrefstate = peraggstate->aggrefstate;
|
AggrefExprState *aggrefstate = peraggstate->aggrefstate;
|
||||||
Aggref *aggref = peraggstate->aggref;
|
Aggref *aggref = peraggstate->aggref;
|
||||||
Datum newVal;
|
FunctionCallInfoData fcinfo;
|
||||||
bool isNull;
|
int i;
|
||||||
|
ListCell *arg;
|
||||||
|
MemoryContext oldContext;
|
||||||
|
|
||||||
newVal = ExecEvalExprSwitchContext(aggrefstate->target, econtext,
|
/* Switch memory context just once for all args */
|
||||||
&isNull, NULL);
|
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||||
|
|
||||||
|
/* Evaluate inputs and save in fcinfo */
|
||||||
|
/* We start from 1, since the 0th arg will be the transition value */
|
||||||
|
i = 1;
|
||||||
|
foreach(arg, aggrefstate->args)
|
||||||
|
{
|
||||||
|
ExprState *argstate = (ExprState *) lfirst(arg);
|
||||||
|
|
||||||
|
fcinfo.arg[i] = ExecEvalExpr(argstate, econtext,
|
||||||
|
fcinfo.argnull + i, NULL);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch back */
|
||||||
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
if (aggref->aggdistinct)
|
if (aggref->aggdistinct)
|
||||||
{
|
{
|
||||||
/* in DISTINCT mode, we may ignore nulls */
|
/* in DISTINCT mode, we may ignore nulls */
|
||||||
if (isNull)
|
/* XXX we assume there is only one input column */
|
||||||
|
if (fcinfo.argnull[1])
|
||||||
continue;
|
continue;
|
||||||
tuplesort_putdatum(peraggstate->sortstate, newVal, isNull);
|
tuplesort_putdatum(peraggstate->sortstate, fcinfo.arg[1],
|
||||||
|
fcinfo.argnull[1]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
advance_transition_function(aggstate, peraggstate, pergroupstate,
|
advance_transition_function(aggstate, peraggstate, pergroupstate,
|
||||||
newVal, isNull);
|
&fcinfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,11 +495,15 @@ process_sorted_aggregate(AggState *aggstate,
|
|||||||
bool haveOldVal = false;
|
bool haveOldVal = false;
|
||||||
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
|
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
Datum newVal;
|
Datum *newVal;
|
||||||
bool isNull;
|
bool *isNull;
|
||||||
|
FunctionCallInfoData fcinfo;
|
||||||
|
|
||||||
tuplesort_performsort(peraggstate->sortstate);
|
tuplesort_performsort(peraggstate->sortstate);
|
||||||
|
|
||||||
|
newVal = fcinfo.arg + 1;
|
||||||
|
isNull = fcinfo.argnull + 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: if input type is pass-by-ref, the datums returned by the sort are
|
* Note: if input type is pass-by-ref, the datums returned by the sort are
|
||||||
* freshly palloc'd in the per-query context, so we must be careful to
|
* freshly palloc'd in the per-query context, so we must be careful to
|
||||||
@ -477,13 +511,13 @@ process_sorted_aggregate(AggState *aggstate,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
while (tuplesort_getdatum(peraggstate->sortstate, true,
|
while (tuplesort_getdatum(peraggstate->sortstate, true,
|
||||||
&newVal, &isNull))
|
newVal, isNull))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* DISTINCT always suppresses nulls, per SQL spec, regardless of the
|
* DISTINCT always suppresses nulls, per SQL spec, regardless of the
|
||||||
* transition function's strictness.
|
* transition function's strictness.
|
||||||
*/
|
*/
|
||||||
if (isNull)
|
if (*isNull)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -495,21 +529,21 @@ process_sorted_aggregate(AggState *aggstate,
|
|||||||
|
|
||||||
if (haveOldVal &&
|
if (haveOldVal &&
|
||||||
DatumGetBool(FunctionCall2(&peraggstate->equalfn,
|
DatumGetBool(FunctionCall2(&peraggstate->equalfn,
|
||||||
oldVal, newVal)))
|
oldVal, *newVal)))
|
||||||
{
|
{
|
||||||
/* equal to prior, so forget this one */
|
/* equal to prior, so forget this one */
|
||||||
if (!peraggstate->inputtypeByVal)
|
if (!peraggstate->inputtypeByVal)
|
||||||
pfree(DatumGetPointer(newVal));
|
pfree(DatumGetPointer(*newVal));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
advance_transition_function(aggstate, peraggstate, pergroupstate,
|
advance_transition_function(aggstate, peraggstate, pergroupstate,
|
||||||
newVal, false);
|
&fcinfo);
|
||||||
/* forget the old value, if any */
|
/* forget the old value, if any */
|
||||||
if (haveOldVal && !peraggstate->inputtypeByVal)
|
if (haveOldVal && !peraggstate->inputtypeByVal)
|
||||||
pfree(DatumGetPointer(oldVal));
|
pfree(DatumGetPointer(oldVal));
|
||||||
/* and remember the new one for subsequent equality checks */
|
/* and remember the new one for subsequent equality checks */
|
||||||
oldVal = newVal;
|
oldVal = *newVal;
|
||||||
haveOldVal = true;
|
haveOldVal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1286,7 +1320,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|||||||
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
|
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
|
||||||
Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
|
Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
|
||||||
AggStatePerAgg peraggstate;
|
AggStatePerAgg peraggstate;
|
||||||
Oid inputType;
|
Oid inputTypes[FUNC_MAX_ARGS];
|
||||||
|
int numArguments;
|
||||||
HeapTuple aggTuple;
|
HeapTuple aggTuple;
|
||||||
Form_pg_aggregate aggform;
|
Form_pg_aggregate aggform;
|
||||||
Oid aggtranstype;
|
Oid aggtranstype;
|
||||||
@ -1297,6 +1332,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|||||||
*finalfnexpr;
|
*finalfnexpr;
|
||||||
Datum textInitVal;
|
Datum textInitVal;
|
||||||
int i;
|
int i;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
/* Planner should have assigned aggregate to correct level */
|
/* Planner should have assigned aggregate to correct level */
|
||||||
Assert(aggref->agglevelsup == 0);
|
Assert(aggref->agglevelsup == 0);
|
||||||
@ -1324,13 +1360,19 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|||||||
/* Fill in the peraggstate data */
|
/* Fill in the peraggstate data */
|
||||||
peraggstate->aggrefstate = aggrefstate;
|
peraggstate->aggrefstate = aggrefstate;
|
||||||
peraggstate->aggref = aggref;
|
peraggstate->aggref = aggref;
|
||||||
|
numArguments = list_length(aggref->args);
|
||||||
|
peraggstate->numArguments = numArguments;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get actual datatype of the input. We need this because it may be
|
* Get actual datatypes of the inputs. These could be different
|
||||||
* different from the agg's declared input type, when the agg accepts
|
* from the agg's declared input types, when the agg accepts ANY,
|
||||||
* ANY (eg, COUNT(*)) or ANYARRAY or ANYELEMENT.
|
* ANYARRAY or ANYELEMENT.
|
||||||
*/
|
*/
|
||||||
inputType = exprType((Node *) aggref->target);
|
i = 0;
|
||||||
|
foreach(lc, aggref->args)
|
||||||
|
{
|
||||||
|
inputTypes[i++] = exprType((Node *) lfirst(lc));
|
||||||
|
}
|
||||||
|
|
||||||
aggTuple = SearchSysCache(AGGFNOID,
|
aggTuple = SearchSysCache(AGGFNOID,
|
||||||
ObjectIdGetDatum(aggref->aggfnoid),
|
ObjectIdGetDatum(aggref->aggfnoid),
|
||||||
@ -1383,21 +1425,23 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|||||||
aggtranstype = aggform->aggtranstype;
|
aggtranstype = aggform->aggtranstype;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build expression trees using actual argument & result types */
|
/* build expression trees using actual argument & result types */
|
||||||
build_aggregate_fnexprs(inputType,
|
build_aggregate_fnexprs(inputTypes,
|
||||||
|
numArguments,
|
||||||
aggtranstype,
|
aggtranstype,
|
||||||
aggref->aggtype,
|
aggref->aggtype,
|
||||||
transfn_oid,
|
transfn_oid,
|
||||||
@ -1437,14 +1481,15 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the transfn is strict and the initval is NULL, make sure input
|
* If the transfn is strict and the initval is NULL, make sure input
|
||||||
* type and transtype are the same (or at least binary- compatible),
|
* type and transtype are the same (or at least binary-compatible),
|
||||||
* so that it's OK to use the first input value as the initial
|
* so that it's OK to use the first input value as the initial
|
||||||
* transValue. This should have been checked at agg definition time,
|
* transValue. This should have been checked at agg definition time,
|
||||||
* but just in case...
|
* but just in case...
|
||||||
*/
|
*/
|
||||||
if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
|
if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
|
||||||
{
|
{
|
||||||
if (!IsBinaryCoercible(inputType, aggtranstype))
|
if (numArguments < 1 ||
|
||||||
|
!IsBinaryCoercible(inputTypes[0], aggtranstype))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
errmsg("aggregate %u needs to have compatible input type and transition type",
|
errmsg("aggregate %u needs to have compatible input type and transition type",
|
||||||
@ -1458,14 +1503,25 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|||||||
/* We don't implement DISTINCT aggs in the HASHED case */
|
/* We don't implement DISTINCT aggs in the HASHED case */
|
||||||
Assert(node->aggstrategy != AGG_HASHED);
|
Assert(node->aggstrategy != AGG_HASHED);
|
||||||
|
|
||||||
peraggstate->inputType = inputType;
|
/*
|
||||||
get_typlenbyval(inputType,
|
* We don't currently implement DISTINCT aggs for aggs having
|
||||||
|
* more than one argument. This isn't required for anything
|
||||||
|
* in the SQL spec, but really it ought to be implemented for
|
||||||
|
* feature-completeness. FIXME someday.
|
||||||
|
*/
|
||||||
|
if (numArguments != 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("DISTINCT is supported only for single-argument aggregates")));
|
||||||
|
|
||||||
|
peraggstate->inputType = inputTypes[0];
|
||||||
|
get_typlenbyval(inputTypes[0],
|
||||||
&peraggstate->inputtypeLen,
|
&peraggstate->inputtypeLen,
|
||||||
&peraggstate->inputtypeByVal);
|
&peraggstate->inputtypeByVal);
|
||||||
|
|
||||||
eq_function = equality_oper_funcid(inputType);
|
eq_function = equality_oper_funcid(inputTypes[0]);
|
||||||
fmgr_info(eq_function, &(peraggstate->equalfn));
|
fmgr_info(eq_function, &(peraggstate->equalfn));
|
||||||
peraggstate->sortOperator = ordering_oper_opid(inputType);
|
peraggstate->sortOperator = ordering_oper_opid(inputTypes[0]);
|
||||||
peraggstate->sortstate = NULL;
|
peraggstate->sortstate = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
/* 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,
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 '(' ')'
|
|||||||
| 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 '(' ')'
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
* (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,
|
|||||||
* *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,
|
|||||||
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,
|
|||||||
* 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);
|
||||||
|
|
||||||
|
for (i = 0; i < agg_num_inputs; i++)
|
||||||
{
|
{
|
||||||
arg1 = makeNode(Param);
|
argp = makeNode(Param);
|
||||||
arg1->paramkind = PARAM_EXEC;
|
argp->paramkind = PARAM_EXEC;
|
||||||
arg1->paramid = -1;
|
argp->paramid = -1;
|
||||||
arg1->paramtype = agg_input_type;
|
argp->paramtype = agg_input_types[i];
|
||||||
|
args = lappend(args, argp);
|
||||||
args = list_make2(arg0, arg1);
|
|
||||||
}
|
}
|
||||||
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,
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
|
|
||||||
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)
|
|||||||
*
|
*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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(*) */
|
TypeName *t = (TypeName *) lfirst(lc);
|
||||||
argoids[0] = ANYOID;
|
|
||||||
argcount = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
args_item = list_head(argtypes);
|
|
||||||
for (i = 0; i < argcount; i++)
|
|
||||||
{
|
|
||||||
TypeName *t = (TypeName *) lfirst(args_item);
|
|
||||||
|
|
||||||
argoids[i] = LookupTypeName(NULL, t);
|
argoids[i] = LookupTypeName(NULL, t);
|
||||||
|
if (!OidIsValid(argoids[i]))
|
||||||
if (!OidIsValid(argoids[i]))
|
ereport(ERROR,
|
||||||
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
errmsg("type \"%s\" does not exist",
|
||||||
errmsg("type \"%s\" does not exist",
|
TypeNameToString(t))));
|
||||||
TypeNameToString(t))));
|
i++;
|
||||||
|
|
||||||
args_item = lnext(args_item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oid = LookupFuncName(aggname, argcount, argoids, true);
|
oid = LookupFuncName(aggname, argcount, argoids, true);
|
||||||
@ -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 @@
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
"(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)
|
|||||||
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)
|
|||||||
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 *
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
"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)
|
|||||||
"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)
|
|||||||
"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)
|
|||||||
"(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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
/*
|
/*
|
||||||
* 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 @@
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
* 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)
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200607261
|
#define CATALOG_VERSION_NO 200607271
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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_ ));
|
|||||||
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_ ));
|
|||||||
*/
|
*/
|
||||||
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 @@
|
|||||||
* 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");
|
|||||||
|
|
||||||
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_ _
|
|||||||
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 @@
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
|
|
||||||
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;
|
|||||||
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;
|
|||||||
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 (
|
|||||||
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
|
|||||||
|
|
||||||
-- 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
|
|||||||
-- 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
|
|||||||
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;
|
|||||||
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
|
|||||||
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)
|
||||||
|
@ -50,6 +50,9 @@ CREATE FUNCTION tf1p(anyarray,int) RETURNS anyarray AS
|
|||||||
-- arg2 only polymorphic transfn
|
-- arg2 only polymorphic transfn
|
||||||
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;
|
||||||
@ -70,30 +73,30 @@ 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 = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- 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 = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
CREATE AGGREGATE myaggp04b(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
|
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
|
||||||
INITCOND = '{}');
|
INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- Case2 (R = P) && ((B = P) || (B = N))
|
-- Case2 (R = P) && ((B = P) || (B = N))
|
||||||
-- -------------------------------------
|
-- -------------------------------------
|
||||||
-- S tf1 B tf2
|
-- S tf1 B tf2
|
||||||
@ -148,13 +151,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
|
|||||||
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
|
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
|
||||||
FINALFUNC = ffp, INITCOND = '{}');
|
FINALFUNC = ffp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P N N P
|
-- P N N P
|
||||||
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
|
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
|
||||||
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
|
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
|
||||||
FINALFUNC = ffp, INITCOND = '{}');
|
FINALFUNC = ffp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P N P N
|
-- P N P N
|
||||||
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
|
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
|
||||||
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
|
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
|
||||||
@ -170,21 +173,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
|
|||||||
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
|
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
|
||||||
FINALFUNC = ffp, INITCOND = '{}');
|
FINALFUNC = ffp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
|
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
|
||||||
INITCOND = '{}');
|
INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P P N P
|
-- P P N P
|
||||||
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
|
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
|
||||||
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
|
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
|
||||||
FINALFUNC = ffp, INITCOND = '{}');
|
FINALFUNC = ffp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
|
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
|
||||||
INITCOND = '{}');
|
INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P P P N
|
-- P P P N
|
||||||
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
|
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
|
||||||
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
|
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
|
||||||
@ -205,30 +208,30 @@ 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 = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
CREATE AGGREGATE myaggn02b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
|
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
|
||||||
INITCOND = '{}');
|
INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- 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 = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- Case4 (R = N) && ((B = P) || (B = N))
|
-- Case4 (R = N) && ((B = P) || (B = N))
|
||||||
-- -------------------------------------
|
-- -------------------------------------
|
||||||
-- S tf1 B tf2
|
-- S tf1 B tf2
|
||||||
@ -282,21 +285,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
|
|||||||
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
|
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
|
||||||
FINALFUNC = ffnp, INITCOND = '{}');
|
FINALFUNC = ffnp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
|
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
|
||||||
INITCOND = '{}');
|
INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P N N P
|
-- P N N P
|
||||||
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
|
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
|
||||||
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
|
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
|
||||||
FINALFUNC = ffnp, INITCOND = '{}');
|
FINALFUNC = ffnp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
|
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
|
||||||
INITCOND = '{}');
|
INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P N P N
|
-- P N P N
|
||||||
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
|
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
|
||||||
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
|
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
|
||||||
@ -318,13 +321,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
|
|||||||
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
|
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
|
||||||
FINALFUNC = ffnp, INITCOND = '{}');
|
FINALFUNC = ffnp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P P N P
|
-- P P N P
|
||||||
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
|
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
|
||||||
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
|
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
|
||||||
FINALFUNC = ffnp, INITCOND = '{}');
|
FINALFUNC = ffnp, INITCOND = '{}');
|
||||||
ERROR: cannot determine transition data type
|
ERROR: cannot determine transition data type
|
||||||
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
|
DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
|
||||||
-- P P P N
|
-- P P P N
|
||||||
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
|
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
|
||||||
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
|
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
|
||||||
@ -335,6 +338,9 @@ ERROR: function tf1p(anyarray, anyelement) does not exist
|
|||||||
CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp,
|
CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp,
|
||||||
STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
|
STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
|
||||||
ERROR: function ffnp(anyarray) does not exist
|
ERROR: function ffnp(anyarray) does not exist
|
||||||
|
-- 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');
|
||||||
@ -530,3 +536,9 @@ select f3, myaggn10a(f1) from t group by f3;
|
|||||||
a | {1,2,3}
|
a | {1,2,3}
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
select mysum2(f1, f1 + 1) from t;
|
||||||
|
mysum2
|
||||||
|
--------
|
||||||
|
38
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -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 (
|
|||||||
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
|
|||||||
|
|
||||||
-- 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
|
|||||||
-- 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;
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
-- -------
|
-- -------
|
||||||
-- 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,
|
|||||||
-- -------
|
-- -------
|
||||||
-- 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,
|
|||||||
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;
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user